You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android自定义键盘:InputMethodService中PopupWindow按键预览不显示求助

解决自定义键盘PopupWindow按键预览不显示的问题

核心问题分析

  • 窗口层级不匹配:输入法属于系统级窗口,普通PopupWindow无法在其上显示,需指定适配的窗口类型。
  • 预览窗口被立即关闭:原代码在点击事件中调用writeText时直接关闭PopupWindow,导致窗口刚显示就消失。
  • 坐标计算错误:getX()/getY()获取的是按键相对父布局的坐标,而showAtLocation需要屏幕绝对坐标,导致窗口位置异常。
  • PopupWindow初始化不完整:未设置背景、窗口类型等必要参数,部分Android版本下无法渲染窗口。

修复后的完整代码

1. 调整PopupWindow初始化与触摸事件逻辑

import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputConnection;
import android.widget.Button;
import android.widget.PopupWindow;
import android.widget.TextView;

public class ButtonsKeyboard extends InputMethodService {
    final int KEY_CODE_DELETE = -1;
    View layout_KeyPad, layout_KeyPreview;
    PopupWindow keyPopup;
    TextView tvKeyPreview;

    @Override
    public View onCreateInputView() {
        layout_KeyPad = getLayoutInflater().inflate(R.layout.number_key_pad, null);
        layout_KeyPreview = getLayoutInflater().inflate(R.layout.key_preview, null);
        tvKeyPreview = layout_KeyPreview.findViewById(R.id.tvKeyPreview);
        
        Button button1 = layout_KeyPad.findViewById(R.id.Button1);
        Button button2 = layout_KeyPad.findViewById(R.id.Button2);
        Button button3 = layout_KeyPad.findViewById(R.id.Button3);
        Button buttonDelete = layout_KeyPad.findViewById(R.id.ButtonDelete);

        // 初始化PopupWindow,提前配置核心参数
        keyPopup = new PopupWindow(layout_KeyPreview, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
        keyPopup.setBackgroundDrawable(getResources().getDrawable(android.R.color.transparent));
        // 设置适配输入法的窗口类型
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            keyPopup.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
        } else {
            keyPopup.setWindowLayoutType(WindowManager.LayoutParams.TYPE_INPUT_METHOD);
        }
        keyPopup.setOutsideTouchable(false);
        keyPopup.setFocusable(false);

        // 改用触摸事件实现:按下显示预览,抬起/取消时输入文本并关闭预览
        button1.setOnTouchListener(getKeyTouchListener(button1));
        button2.setOnTouchListener(getKeyTouchListener(button2));
        button3.setOnTouchListener(getKeyTouchListener(button3));

        buttonDelete.setOnClickListener(v -> {
            InputConnection ic = getCurrentInputConnection();
            if (ic != null) ic.deleteSurroundingText(1, 0);
        });
        return layout_KeyPad;
    }

    // 封装通用触摸事件逻辑,避免重复代码
    private View.OnTouchListener getKeyTouchListener(Button button) {
        return (v, event) -> {
            int keyCode = Integer.parseInt(v.getTag().toString());
            if (keyCode > 0) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        tvKeyPreview.setText(button.getText());
                        // 获取按键的屏幕绝对坐标
                        int[] location = new int[2];
                        v.getLocationOnScreen(location);
                        // 设置预览窗口大小与位置(显示在按键上方)
                        keyPopup.setWidth(v.getWidth() + 4);
                        keyPopup.setHeight(v.getHeight() + 4);
                        keyPopup.showAtLocation(layout_KeyPad, Gravity.NO_GRAVITY, location[0] - 2, location[1] - v.getHeight() - 6);
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        writeText(keyCode);
                        keyPopup.dismiss();
                        break;
                }
            }
            return true;
        };
    }

    private void writeText(int keyCode) {
        InputConnection ic = getCurrentInputConnection();
        if (ic == null) return;
        char code = (char) keyCode;
        ic.commitText(String.valueOf(code), 1);
    }
}

2. 布局文件无需修改(保留原key_preview.xml与number_key_pad.xml)

额外优化建议

  • 可以根据屏幕分辨率动态调整预览窗口的偏移量,保证在不同设备上显示位置合理。
  • 给预览窗口添加阴影、圆角等样式,提升视觉体验。
  • 对删除键也添加预览效果,保持交互一致性。

内容的提问来源于stack exchange,提问作者SHIMON HANSDA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 03:52:00