在 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 或父容器百分比 %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 行上方没有间隔,除非通过 KeyboardView
的 paddingTop
额外设置。
horizontalGap同理
<Key>
子元素属性(单个按键配置)
用于定义单个按键的功能、样式和行为,可覆盖 <Keyboard>
的全局设置:
1. 基础功能属性
属性 | 作用 | 示例 |
---|---|---|
android:keyCode | 按键对应的系统键码(定义在 KeyEvent 中) | KEYCODE_A 、KEYCODE_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、核心知识点说明
-
键盘布局属性:
keyWidth
/keyHeight
:推荐使用%p
(父容器百分比)适配不同屏幕。keyCode
:对应KeyEvent
中的系统按键码(如KEYCODE_0
、KEYCODE_ENTER
),也可自定义。keyLabel
:按键上显示的文本,支持特殊符号(如⌫
表示删除)。
-
事件处理:
- 通过
OnKeyboardActionListener
监听按键事件,处理输入、删除、确认等逻辑。 - 需手动管理光标位置(
getSelectionStart()
)和文本编辑(Editable
)。
- 通过
-
进阶功能:
- 动态切换布局:调用
keyboardView.setKeyboard(new Keyboard(...))
切换不同 XML 布局(如数字 / 字母键盘)。 - 支持手势:利用
swipeLeft
/swipeRight
等方法实现滑动切换功能。 - 集成输入法:若需开发独立输入法应用,需继承
InputMethodService
并在onCreateInputView()
中返回KeyboardView
。
- 动态切换布局:调用
6、java中无法直接获取的属性
-
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; } }
-
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;
}
}
使用方法
-
布局文件中引用
<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中设置文字颜色 -->
-
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));
关键修复点
- 禁用系统默认绘制:通过
setWillNotDraw(false)
允许自定义绘制。 - 完全重写绘制逻辑:在
onDraw()
中手动绘制背景和文字,避免系统逻辑覆盖。 - 状态区分处理:通过
key.pressed
判断按键状态,分别应用不同样式。 - 属性正确解析:从
TypedArray
中获取系统属性(keyTextSize
、keyTextColor
)并应用到画笔。 - 提供公开方法:通过
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" />
关键属性说明
-
keyPreviewLayout
:- 必须指定一个布局资源(通常是
TextView
) - 预览框会自动显示按键的
keyLabel
或keyOutputText
内容
- 必须指定一个布局资源(通常是
-
预览相关辅助属性:
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"
)