如何在Android中创建自定义键盘布局

在 Android 中,键盘布局(Keyboard Layout)定义了虚拟键盘的按键排列、功能和外观。自定义键盘布局通常用于创建特定场景的输入方式(如数字键盘、自定义符号键盘等)。以下是关于 Android 键盘布局的核心知识和实现方式:

1. 系统键盘布局文件

Android 系统的默认键盘布局定义在 res/xml 目录下,以 .xml 文件形式存在。基本结构如下:

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="33%p"    <!-- 按键宽度(百分比或固定值) -->
    android:keyHeight="60dp"   <!-- 按键高度 -->
    android:horizontalGap="2dp" <!-- 按键水平间距 -->
    android:verticalGap="2dp">  <!-- 按键垂直间距 -->

    <!-- 第一行按键 -->
    <Row>
        <Key
            android:keyLabel="Q"       <!-- 按键显示文本 -->
            android:keyCode="KEYCODE_Q" <!-- 对应系统按键码 -->
            android:keyEdgeFlags="left" /> <!-- 边缘标识(左/右) -->
        <!-- 其他按键... -->
    </Row>

    <!-- 第二行按键 -->
    <Row>
        <!-- 按键定义... -->
    </Row>

</Keyboard>

2. 核心属性说明

<Keyboard> 根元素属性(全局配置)

用于定义整个键盘的整体布局参数:

属性 作用 示例
android:keyWidth 按键宽度(支持固定值 dp 或父容器百分比 %p 25%p(每行 4 个键)
android:keyHeight 按键高度(固定值或百分比) 60dp
android:horizontalGap 按键之间的水平间距 2dp
android:verticalGap 行之间的垂直间距 2dp
android:keyBackground 所有按键的默认背景(可引用选择器) @drawable/key_bg
android:keyTextColor 按键文字默认颜色 #333333
android:keyTextSize 按键文字默认大小 18sp
android:labelTextSize 辅助标签文字大小(如 Shift 键的 "↑") 12sp

注:verticalGap 属性不包含第一行按键的上方间距 。它仅用于控制相邻两行按键之间 的垂直间隔。一般第 1 行上方没有间隔,除非通过 KeyboardViewpaddingTop 额外设置。

horizontalGap同理

<Key> 子元素属性(单个按键配置)

用于定义单个按键的功能、样式和行为,可覆盖 <Keyboard> 的全局设置:

1. 基础功能属性
属性 作用 示例
android:keyCode 按键对应的系统键码(定义在 KeyEvent 中) KEYCODE_AKEYCODE_ENTER
android:keyLabel 按键显示的文本 "A""1""删除"
android:codes 多个键码(用逗号分隔,适合多功能键) KEYCODE_SHIFT_LEFT,KEYCODE_SHIFT_RIGHT
android:keyOutputText 按键直接输出的文本(覆盖 keyCode 行为) "@"(直接输入 @符号)
2. 样式与布局属性
属性 作用 示例
android:keyWidth 单个按键宽度(覆盖全局设置) 30%p
android:keyHeight 单个按键高度(覆盖全局设置) 70dp
android:keyEdgeFlags 标记按键在边缘(左 / 右对齐) left(左边缘)、right(右边缘)
android:horizontalGap 单个按键左侧的额外间距 5dp
android:iconPreview 长按按键时的预览图标 @drawable/preview_del

注:Row 标签的 android:verticalGap 优先级高于根标签的 verticalGap

horizontalGap同理

3. 交互行为属性
属性 作用 示例
android:isModifier 是否为功能修饰键(如 Shift、Ctrl) true(Shift 键)
android:isSticky 是否为粘性键(按下后保持状态,如 Shift 切换大小写) true
android:isRepeatable 长按是否重复触发(如删除键连续删除) true
android:popupCharacters 长按弹出的备选字符(如长按 "1" 弹出 "!") "!@#"
android:popupKeyboard 长按弹出的子键盘(引用另一个键盘布局) @xml/symbol_keyboard
4.示例:综合使用属性
XML 复制代码
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="25%p"
    android:keyHeight="60dp"
    android:horizontalGap="1dp"
    android:verticalGap="1dp"
    android:keyTextColor="#333">

    <Row>
        <!-- 带弹出备选字符的按键 -->
        <Key
            android:keyLabel="1"
            android:keyCode="KEYCODE_1"
            android:popupCharacters="!¹" />  <!-- 长按弹出!和¹ -->

        <!-- 粘性修饰键(Shift) -->
        <Key
            android:keyLabel="↑"
            android:keyCode="KEYCODE_SHIFT_LEFT"
            android:isModifier="true"
            android:isSticky="true" />

        <!-- 带图标和重复功能的删除键 -->
        <Key
            android:keyCode="KEYCODE_DEL"
            android:keyIcon="@drawable/ic_del"
            android:isRepeatable="true" />

        <!-- 弹出子键盘的按键 -->
        <Key
            android:keyLabel="≡"
            android:popupKeyboard="@xml/symbol_keyboard" />  <!-- 长按弹出符号键盘 -->
    </Row>

</Keyboard>
5.部分详解
android:keyIcon

用于为按键设置图标(替代或补充 keyLabel 文本),适用于需要图标化展示的按键(如删除键、回车、切换键盘等)。

使用方式:
XML 复制代码
<Key
    android:keyCode="KEYCODE_DEL"
    android:keyIcon="@drawable/ic_delete"  <!-- 引用drawable资源 -->
    android:keyWidth="25%p" />
注意事项:
  • 图标资源建议放在 res/drawable 目录,支持 PNG、SVG 或 VectorDrawable。
  • 若同时设置 keyLabelkeyIcon,部分设备可能只显示图标,需测试适配。
  • 可通过 android:iconPreview 设置长按按键时的预览图标(如系统键盘的 Shift 键预览)。
android:isRepeatable

设置按键是否支持「长按重复触发」(如删除键长按连续删除,音量键长按连续增减)。默认值为 false

使用方式:
XML 复制代码
<Key
    android:keyCode="KEYCODE_DEL"
    android:keyIcon="@drawable/ic_delete"
    android:isRepeatable="true"  <!-- 支持长按重复 -->
    android:keyWidth="25%p" />
作用:
  • 当设置为 true 时,长按该按键会持续触发 onKey() 回调(间隔约 50ms),适合需要连续操作的场景(删除、滚动等)。
  • 配合 onPress()onRelease() 可实现更精细的长按逻辑。
示例:带图标和重复功能的删除键
XML 复制代码
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="25%p"
    android:keyHeight="60dp">

    <Row>
        <!-- 其他按键 -->
        <Key
            android:keyCode="KEYCODE_DEL"
            android:keyIcon="@drawable/ic_delete_24"  <!-- 图标 -->
            android:isRepeatable="true"               <!-- 长按连续删除 -->
            android:keyWidth="25%p" />
    </Row>

</Keyboard>

3. 自定义键盘实现步骤

3.1、创建键盘布局 XML 文件

首先在 res/xml 目录下创建键盘布局文件(如 custom_keyboard.xml),定义按键的排列、样式和功能:

自定义键盘布局XML文件

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="25%p"       <!-- 按键宽度(占父容器的25%) -->
    android:keyHeight="60dp"      <!-- 按键高度 -->
    android:horizontalGap="1dp"   <!-- 按键水平间距 -->
    android:verticalGap="1dp">    <!-- 按键垂直间距 -->

    <!-- 第一行按键 -->
    <Row>
        <Key
            android:keyLabel="1"
            android:keyCode="KEYCODE_1" />  <!-- 对应系统按键码 -->
        <Key
            android:keyLabel="2"
            android:keyCode="KEYCODE_2" />
        <Key
            android:keyLabel="3"
            android:keyCode="KEYCODE_3" />
        <Key
            android:keyLabel="⌫"
            android:keyCode="KEYCODE_DEL" /> <!-- 删除键 -->
    </Row>

    <!-- 第二行按键 -->
    <Row>
        <Key
            android:keyLabel="4"
            android:keyCode="KEYCODE_4" />
        <Key
            android:keyLabel="5"
            android:keyCode="KEYCODE_5" />
        <Key
            android:keyLabel="6"
            android:keyCode="KEYCODE_6" />
        <Key
            android:keyLabel="+"
            android:keyCode="KEYCODE_PLUS" /> <!-- 加号 -->
    </Row>

    <!-- 第三行按键 -->
    <Row>
        <Key
            android:keyLabel="7"
            android:keyCode="KEYCODE_7" />
        <Key
            android:keyLabel="8"
            android:keyCode="KEYCODE_8" />
        <Key
            android:keyLabel="9"
            android:keyCode="KEYCODE_9" />
        <Key
            android:keyLabel="-"
            android:keyCode="KEYCODE_MINUS" /> <!-- 减号 -->
    </Row>

    <!-- 第四行按键 -->
    <Row>
        <Key
            android:keyLabel="."
            android:keyCode="KEYCODE_PERIOD" /> <!-- 小数点 -->
        <Key
            android:keyLabel="0"
            android:keyCode="KEYCODE_0" />
        <Key
            android:keyLabel="*"
            android:keyCode="KEYCODE_STAR" /> <!-- 乘号 -->
        <Key
            android:keyLabel="÷"
            android:keyCode="KEYCODE_SLASH" /> <!-- 除号 -->
    </Row>

    <!-- 第五行确认键 -->
    <Row>
        <Key
            android:keyLabel="确认"
            android:keyCode="KEYCODE_ENTER"
            android:keyWidth="100%p" /> <!-- 占满整行宽度 -->
    </Row>

</Keyboard>
3.2、在布局中添加KeyboardView

在 Activity 的布局文件(如 activity_main.xml)中添加 KeyboardView 用于显示自定义键盘,并搭配一个输入框:

包含自定义键盘的Activity布局

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <!-- 输入框 -->
    <EditText
        android:id="@+id/inputEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="点击输入..."
        android:padding="12dp"
        android:background="@drawable/edittext_border" />

    <!-- 自定义键盘视图(默认隐藏,点击输入框后显示) -->
    <android.inputmethodservice.KeyboardView
        android:id="@+id/keyboardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:background="#F5F5F5"
        android:keyBackground="@drawable/key_background"  <!-- 按键背景 -->
        android:keyTextColor="#333333"                    <!-- 按键文字颜色 -->
        android:keyTextSize="18sp"                        <!-- 按键文字大小 -->
        android:visibility="gone" />  <!-- 默认隐藏 -->

</LinearLayout>
3.3、创建按键样式(可选)

为了美化按键,可创建按键背景选择器(res/drawable/key_background.xml),实现按压效果:

按键背景选择器

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 按压状态 -->
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#CCCCCC" />
            <corners android:radius="4dp" />
        </shape>
    </item>
    <!-- 正常状态 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#FFFFFF" />
            <stroke android:color="#EEEEEE" android:width="1dp" />
            <corners android:radius="4dp" />
        </shape>
    </item>
</selector>
3.4、在代码中关联键盘逻辑

在 Activity 中加载键盘布局,处理按键事件,并控制键盘显示 / 隐藏:

处理自定义键盘逻辑的Activity

java 复制代码
package com.example.customkeyboard;

import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.text.Editable;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private EditText inputEditText;
    private KeyboardView keyboardView;
    private Keyboard customKeyboard;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化控件
        inputEditText = findViewById(R.id.inputEditText);
        keyboardView = findViewById(R.id.keyboardView);
        
        // 加载自定义键盘布局
        customKeyboard = new Keyboard(this, R.xml.custom_keyboard);
        keyboardView.setKeyboard(customKeyboard);
        
        // 禁用系统键盘(避免冲突)
        inputEditText.setShowSoftInputOnFocus(false);
        inputEditText.setOnClickListener(v -> showCustomKeyboard());

        // 设置键盘按键监听器
        keyboardView.setOnKeyboardActionListener(new KeyboardView.OnKeyboardActionListener() {
            @Override
            public void onPress(int primaryCode) {}

            @Override
            public void onRelease(int primaryCode) {}

            @Override
            public void onKey(int primaryCode, int[] keyCodes) {
                Editable editable = inputEditText.getText();
                int cursorPosition = inputEditText.getSelectionStart();

                switch (primaryCode) {
                    case Keyboard.KEYCODE_DEL:  // 删除键
                        if (editable != null && cursorPosition > 0) {
                            editable.delete(cursorPosition - 1, cursorPosition);
                        }
                        break;
                    case Keyboard.KEYCODE_ENTER:  // 确认键
                        hideCustomKeyboard();  // 隐藏键盘
                        break;
                    default:  // 普通字符键
                        char keyChar = (char) primaryCode;
                        editable.insert(cursorPosition, String.valueOf(keyChar));
                }
            }

            @Override
            public void onText(CharSequence text) {}

            @Override
            public void swipeLeft() {}

            @Override
            public void swipeRight() {}

            @Override
            public void swipeDown() {}

            @Override
            public void swipeUp() {}
        });
    }

    // 显示自定义键盘
    private void showCustomKeyboard() {
        keyboardView.setVisibility(View.VISIBLE);
        keyboardView.setEnabled(true);
        // 隐藏系统键盘(如果显示)
        InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(inputEditText.getWindowToken(), 0);
    }

    // 隐藏自定义键盘
    private void hideCustomKeyboard() {
        keyboardView.setVisibility(View.GONE);
        keyboardView.setEnabled(false);
    }
}

4. 高级用法

  • 动态切换布局 :通过 keyboardView.setKeyboard(new Keyboard(...)) 切换不同布局(如数字 / 字母键盘)。
  • 自定义按键样式 :通过 android:keyBackground 为按键设置背景(支持选择器 selector 实现按压效果)。
  • 支持手势 :在 OnKeyboardActionListener 中处理滑动事件(swipeLeft 等)。
  • 集成到输入法 :若需开发独立输入法,需继承 InputMethodService 并在 onCreateInputView() 中返回 KeyboardView

5、核心知识点说明

  1. 键盘布局属性

    • keyWidth/keyHeight:推荐使用 %p(父容器百分比)适配不同屏幕。
    • keyCode:对应 KeyEvent 中的系统按键码(如 KEYCODE_0KEYCODE_ENTER),也可自定义。
    • keyLabel:按键上显示的文本,支持特殊符号(如 表示删除)。
  2. 事件处理

    • 通过 OnKeyboardActionListener 监听按键事件,处理输入、删除、确认等逻辑。
    • 需手动管理光标位置(getSelectionStart())和文本编辑(Editable)。
  3. 进阶功能

    • 动态切换布局:调用 keyboardView.setKeyboard(new Keyboard(...)) 切换不同 XML 布局(如数字 / 字母键盘)。
    • 支持手势:利用 swipeLeft/swipeRight 等方法实现滑动切换功能。
    • 集成输入法:若需开发独立输入法应用,需继承 InputMethodService 并在 onCreateInputView() 中返回 KeyboardView

6、java中无法直接获取的属性

android:keyTextSize--设置按键文字大小

在 Android 中,要在 Java 代码中获取 XML 中为 KeyboardView 设置的 android:keyTextSize 属性值。
由于 KeyboardView 没有直接提供获取 keyTextSize 的公开方法,可通过自定义 KeyboardView,在初始化时解析 XML 属性并保存。

java 复制代码
package com.example.keyboard;

import android.content.Context;
import android.content.res.TypedArray;
import android.inputmethodservice.KeyboardView;
import android.util.AttributeSet;

public class MyKeyboardView extends KeyboardView {
    // 存储从XML中获取的keyTextSize(单位:px)
    private float mKeyTextSize;

    public MyKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 解析XML属性
        init(attrs);
    }

    public MyKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        if (attrs != null) {
            // 1. 获取系统定义的KeyboardView属性
            TypedArray ta = getContext().obtainStyledAttributes(
                attrs, 
                new int[]{android.R.attr.keyTextSize} // 目标属性:keyTextSize
            );

            // 2. 读取属性值(第二个参数为默认值,单位px)
            mKeyTextSize = ta.getDimension(0, 48); // 0表示数组中第一个属性

            // 3. 回收资源
            ta.recycle();
        }
    }

    // 提供公开方法获取keyTextSize
    public float getKeyTextSize() {
        return mKeyTextSize;
    }
}
android:keyBackground--设置按键文字背景色

在 Android 中,KeyboardView 确实没有 setKeyBackground() 这个公开方法,这是一个常见的误解。虽然在 XML 中可以通过 android:keyBackground 属性设置按键背景,但在 Java 代码中需要通过其他方式实现动态修改。

通过继承 KeyboardView 并重写按键绘制方法,完全控制按键背景:

java 复制代码
package com.example.mykeyboard;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.util.AttributeSet;
import android.view.inputmethod.InputMethodManager;

import java.util.List;

public class CustomKeyboardView extends KeyboardView {
    private Drawable mKeyBackground; // 按键背景

    public CustomKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 初始化默认背景(可选)
        mKeyBackground = getContext().getDrawable(R.drawable.key_bg_normal);
    }

    // 提供公开方法设置按键背景
    public void setKeyBackground(Drawable drawable) {
        mKeyBackground = drawable;
        invalidate(); // 刷新绘制
    }

    @Override
    public void onDraw(Canvas canvas) {
        if (mKeyBackground == null) {
            super.onDraw(canvas); // 无自定义背景时使用默认绘制
            return;
        }

        // 获取所有按键
        List<Keyboard.Key> keys = getKeyboard().getKeys();
        for (Keyboard.Key key : keys) {
            // 绘制按键背景
            drawKeyBackground(canvas, key);
        }

        // 绘制按键文字和图标(复用系统逻辑)
        super.onDraw(canvas);
    }

    private void drawKeyBackground(Canvas canvas, Keyboard.Key key) {
        // 设置背景边界为按键区域
        mKeyBackground.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
        
        // 根据按键状态设置背景(如按压状态)
        if (key.pressed) {
            mKeyBackground.setState(new int[]{android.R.attr.state_pressed});
        } else {
            mKeyBackground.setState(new int[]{});
        }
        
        // 绘制背景
        mKeyBackground.draw(canvas);
    }
}

使用自定义 KeyboardView

1、在布局文件中替换为自定义 View:

XML 复制代码
<com.example.mykeyboard.CustomKeyboardView
    android:id="@+id/keyboardView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:keyTextColor="@color/key_text"
    android:keyTextSize="18sp"/>

2、在代码中动态设置背景:

java 复制代码
// 初始化自定义键盘视图
CustomKeyboardView keyboardView = findViewById(R.id.keyboardView);
keyboardView.setKeyboard(new Keyboard(this, R.xml.keyboard_layout));

// 动态设置按键背景
Drawable normalBg = getDrawable(R.drawable.key_bg_normal);
keyboardView.setKeyBackground(normalBg);

// 切换为按压状态的背景(示例)
button.setOnClickListener(v -> {
    Drawable pressedBg = getDrawable(R.drawable.key_bg_pressed);
    keyboardView.setKeyBackground(pressedBg);
});

通过重写 onDraw() 方法,完全控制按键绘制过程,确保背景颜色和文字大小生效:

java 复制代码
package com.example.customkeyboard;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.util.AttributeSet;

import java.util.List;

public class MyKeyboardView extends KeyboardView {
    // 按键背景相关
    private Drawable mKeyBackground;
    private int mNormalBgColor = Color.GRAY;      // 默认背景色
    private int mPressedBgColor = Color.DKGRAY;   // 按压背景色

    // 文字相关
    private Paint mTextPaint;
    private int mKeyTextColor = Color.WHITE;      // 文字颜色
    private float mKeyTextSize = 30;              // 文字大小(px)

    public MyKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public MyKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        // 禁用系统默认绘制(关键)
        setWillNotDraw(false);

        // 初始化画笔
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextAlign(Paint.Align.CENTER);

        // 解析XML属性(包括系统属性和自定义属性)
        if (attrs != null) {
            TypedArray ta = getContext().obtainStyledAttributes(attrs, 
                new int[]{android.R.attr.keyTextSize, android.R.attr.keyTextColor});

            // 获取系统属性:keyTextSize(单位px)
            mKeyTextSize = ta.getDimension(0, mKeyTextSize);
            // 获取系统属性:keyTextColor
            mKeyTextColor = ta.getColor(1, mKeyTextColor);

            ta.recycle();
        }

        // 应用文字属性
        mTextPaint.setTextSize(mKeyTextSize);
        mTextPaint.setColor(mKeyTextColor);
    }

    // 公开方法:设置按键背景颜色
    public void setKeyBgColors(int normalColor, int pressedColor) {
        mNormalBgColor = normalColor;
        mPressedBgColor = pressedColor;
        invalidate();
    }

    // 公开方法:设置文字大小(px)
    public void setKeyTextSize(float textSize) {
        mKeyTextSize = textSize;
        mTextPaint.setTextSize(textSize);
        invalidate();
    }

    // 公开方法:设置文字颜色
    public void setKeyTextColor(int color) {
        mKeyTextColor = color;
        mTextPaint.setColor(color);
        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas); // 先绘制键盘基础背景

        if (getKeyboard() == null) return;

        List<Keyboard.Key> keys = getKeyboard().getKeys();
        for (Keyboard.Key key : keys) {
            // 1. 绘制按键背景
            drawKeyBackground(canvas, key);
            // 2. 绘制按键文字
            drawKeyText(canvas, key);
        }
    }

    // 绘制按键背景
    private void drawKeyBackground(Canvas canvas, Keyboard.Key key) {
        // 定义按键区域
        Rect keyRect = new Rect(key.x, key.y, key.x + key.width, key.y + key.height);

        // 根据状态设置背景色
        if (key.pressed) {
            canvas.drawRect(keyRect, getPaint(mPressedBgColor));
        } else {
            canvas.drawRect(keyRect, getPaint(mNormalBgColor));
        }

        // 绘制边框(可选)
        canvas.drawRect(keyRect, getPaint(Color.WHITE, 2, Paint.Style.STROKE));
    }

    // 绘制按键文字
    private void drawKeyText(Canvas canvas, Keyboard.Key key) {
        if (key.label == null) return;

        // 计算文字位置(居中)
        int x = key.x + key.width / 2;
        int y = key.y + (key.height - (mTextPaint.descent() + mTextPaint.ascent())) / 2;

        // 按压状态文字颜色变化(可选)
        if (key.pressed) {
            mTextPaint.setColor(Color.LTGRAY);
        } else {
            mTextPaint.setColor(mKeyTextColor);
        }

        canvas.drawText(key.label.toString(), x, y, mTextPaint);
    }

    // 辅助方法:获取指定颜色和样式的画笔
    private Paint getPaint(int color) {
        return getPaint(color, 0, Paint.Style.FILL);
    }

    private Paint getPaint(int color, int strokeWidth, Paint.Style style) {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(color);
        paint.setStrokeWidth(strokeWidth);
        paint.setStyle(style);
        return paint;
    }
}

使用方法

  1. 布局文件中引用
XML 复制代码
<com.example.customkeyboard.MyKeyboardView
    android:id="@+id/keyboardView"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:keyTextSize="18sp"  <!-- XML中设置文字大小 -->
    android:keyTextColor="#FFFFFF"/> <!-- XML中设置文字颜色 -->
  1. Java 代码中动态设置
java 复制代码
MyKeyboardView keyboardView = findViewById(R.id.keyboardView);
keyboardView.setKeyboard(new Keyboard(this, R.xml.keyboard_layout));

// 动态设置背景颜色
keyboardView.setKeyBgColors(
    ContextCompat.getColor(this, R.color.key_normal),
    ContextCompat.getColor(this, R.color.key_pressed)
);

// 动态设置文字大小(单位px,18sp≈27px)
float textSizePx = getResources().getDimension(R.dimen.key_text_size);
keyboardView.setKeyTextSize(textSizePx);

// 动态设置文字颜色
keyboardView.setKeyTextColor(ContextCompat.getColor(this, R.color.key_text));

关键修复点

  1. 禁用系统默认绘制 :通过 setWillNotDraw(false) 允许自定义绘制。
  2. 完全重写绘制逻辑 :在 onDraw() 中手动绘制背景和文字,避免系统逻辑覆盖。
  3. 状态区分处理 :通过 key.pressed 判断按键状态,分别应用不同样式。
  4. 属性正确解析 :从 TypedArray 中获取系统属性(keyTextSizekeyTextColor)并应用到画笔。
  5. 提供公开方法 :通过 setKeyBgColors()setKeyTextSize() 等方法支持动态修改。

注意事项

  • 自定义键盘需处理各种输入逻辑(如光标位置、删除、换行等),系统默认键盘的复杂功能(如联想输入)需自行实现。
  • 适配不同屏幕尺寸时,建议使用百分比(%p)定义按键大小,避免固定尺寸导致的适配问题。
相关推荐
baidu_2474386115 小时前
Android ViewModel定时任务
android·开发语言·javascript
有位神秘人15 小时前
Android中Notification的使用详解
android·java·javascript
·云扬·16 小时前
MySQL Binlog落盘机制深度解析:性能与安全性的平衡艺术
android·mysql·adb
独自破碎E17 小时前
【BISHI9】田忌赛马
android·java·开发语言
代码s贝多芬的音符18 小时前
android 两个人脸对比 mlkit
android
darkb1rd20 小时前
五、PHP类型转换与类型安全
android·安全·php
gjxDaniel20 小时前
Kotlin编程语言入门与常见问题
android·开发语言·kotlin
csj5020 小时前
安卓基础之《(22)—高级控件(4)碎片Fragment》
android
峥嵘life21 小时前
Android16 【CTS】CtsMediaCodecTestCases等一些列Media测试存在Failed项
android·linux·学习
stevenzqzq1 天前
Compose 中的状态可变性体系
android·compose