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

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

1. 系统键盘布局文件

Android 系统的默认键盘布局定义在 res/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 或父容器百分比 %p25%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 行上方没有间隔,除非通过 KeyboardView 的 paddingTop 额外设置。

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.示例:综合使用属性
<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 文本),适用于需要图标化展示的按键(如删除键、回车、切换键盘等)。

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

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

    使用方式:
    <Key
        android:keyCode="KEYCODE_DEL"
        android:keyIcon="@drawable/ic_delete"
        android:isRepeatable="true"  <!-- 支持长按重复 -->
        android:keyWidth="25%p" />
    

    作用:
    • 当设置为 true 时,长按该按键会持续触发 onKey() 回调(间隔约 50ms),适合需要连续操作的场景(删除、滚动等)。
    • 配合 onPress() 和 onRelease() 可实现更精细的长按逻辑。

                示例:带图标和重复功能的删除键

<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 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 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 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

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中无法直接获取的属性

  1. android:keyTextSize--设置按键文字大小
            在 Android 中,要在 Java 代码中获取 XML 中为 KeyboardView 设置的 android:keyTextSize 属性值。
            由于 KeyboardView 没有直接提供获取 keyTextSize 的公开方法,可通过自定义 KeyboardView,在初始化时解析 XML 属性并保存。
    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;
        }
    }
    
  2. android:keyBackground--设置按键文字背景色

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

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

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:

<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、在代码中动态设置背景:

// 初始化自定义键盘视图
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() 方法,完全控制按键绘制过程,确保背景颜色和文字大小生效:

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. 布局文件中引用
<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 代码中动态设置
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() 等方法支持动态修改。

按键预览效果

在 Android 自定义键盘开发中,keyPreviewLayout 是一个非常实用的属性,用于定义按键被按下时显示的预览效果(即按键上方弹出的放大提示框)。

基本用法

keyPreviewLayout 通常在 KeyboardView 中设置,指定一个自定义的布局文件作为预览样式:

<android.inputmethodservice.KeyboardView
    android:id="@+id/keyboard_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:keyPreviewLayout="@layout/key_preview"  <!-- 指定预览布局 -->
    android:keyPreviewOffset="10dp"  <!-- 预览框与按键的垂直偏移量 -->
    android:keyPreviewHeight="80dp"  <!-- 预览框高度 -->
    android:keyPreviewAnimationDuration="200" />  <!-- 预览动画时长(毫秒) -->

自定义预览布局(key_preview.xml)

需要创建一个布局文件来定义预览效果的样式,通常包含一个显示按键文本的 TextView

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#CC000000"  <!-- 预览框背景 -->
    android:textColor="#FFFFFF"     <!-- 预览文本颜色 -->
    android:textSize="32sp"         <!-- 预览文本大小 -->
    android:padding="8dp"           <!-- 内边距 -->
    android:radius="4dp"            <!-- 圆角(API 21+) -->
    android:shadowColor="#000000"   <!-- 文本阴影 -->
    android:shadowRadius="2" />

关键属性说明

  1. keyPreviewLayout

    • 必须指定一个布局资源(通常是 TextView
    • 预览框会自动显示按键的 keyLabel 或 keyOutputText 内容
  2. 预览相关辅助属性

    • keyPreviewOffset:预览框相对于按键的垂直偏移(正值向上,负值向下)
    • keyPreviewHeight:预览框的固定高度(也可在自定义布局中通过 layout_height 设置)
    • keyPreviewAnimationDuration:预览框显示 / 消失的动画时长
    • isPreviewEnabled:是否启用预览(false 可禁用所有预览)

代码中控制预览

也可以在代码中动态设置或修改预览相关属性:

val keyboardView = findViewById<KeyboardView>(R.id.keyboard_view)

// 禁用预览功能
keyboardView.isPreviewEnabled = false

// 动态设置预览布局
keyboardView.keyPreviewLayout = R.layout.custom_key_preview

// 修改预览偏移量
keyboardView.keyPreviewOffset = 20

注意事项

  • 预览布局的根视图建议使用 TextView,系统会自动将按键的文本设置到该视图
  • 复杂布局可能导致预览显示异常,尽量保持简洁
  • 预览功能会轻微影响性能,对低配置设备可考虑禁用(isPreviewEnabled="false"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值