一初识自定义键盘

自定义键盘涉及到系统的两个类Keyboard和KeyboardView。Keyboard设置键盘的布局文件(键盘长什么样子),KeyboardView处理绘制,检测按键,触摸动作等。

首先,去写一个键盘布局文件:在res下新建xml文件夹,在xml文件夹中新建.xml文件,用来实现软键盘的布局,这里先提供一个写好键盘布局,关于布局标签后边再做介绍。

接下来,在我们Activity对应的布局文件中写上KeyboardView控件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.inputmethodservice.KeyboardView
        android:id="@+id/keyboardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom" />

</FrameLayout>复制代码

最后,对应的Activity找到这个控件。给KeyboardView设置keyboard对象,然后给keyboardView设置key的点击事件。

代码如下:

//找到键盘控件
KeyboardView keyboardView = findViewById(R.id.keyboardView);
//创建keyboard,设置我们写的布局文件
Keyboard keyboard = new Keyboard(this, R.xml.keyboard_number_abc);
//把创建的键盘布局设置给控件
keyboardView.setKeyboard(keyboard);
//给键盘设置监听
keyboardView.setOnKeyboardActionListener(this);...复制代码

运行代码显示ui如下:

          

一个很丑的自定义键盘出来了。

二自定义键盘布局

通过上边的介绍,我们对自定义键盘有一个整体的了解。当然如果想随心所遇自定义,第一步就需要我们去学习自定义键盘布局文件的书写,了解Keyboard类。

keyboard类继承Object,作用是解析XML描述键盘和存储的属性键。一个键盘(Keyboard)由成排(Row)的钥匙(Key)组成。而Row 和Key属于Keyboard的内部类。

2.1Keyboard类

keyboard.Row :行,键在键盘的容器。

keyboard.Key: 按钮,类用于描述一个键的位置和特征的键盘.

keyboard的属性有



  • android:horizontalGap 每个Key之间默认的水平间隙
  • android:keyHeight Key的默认高度.
  • android:keyWidth Key的默认宽度.
  • android:verticalGap 每行(Row)之间的默认行间隙



它们的取值单位可以是px,dp,sp,% ,%p。前三个单位都好理解,最后两个单位官方文档说的很模糊。

 The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to some parent container.

后缀为%是相对于基准的高度或者宽度的百分比,而%p则是相对于某些父容器宽度或高度百分比,我并不知道键盘的基准高度和某些父容器指的是什么。但我发现他们并没有差别,但是保险起见,我们最好不要混着用,而是以一种参考系去设置大小。

2.2keyboard.Key类

keyboard.Key类继承Object,类用于描述一个键的位置和特征的键盘(按钮)。

属性有:



  • android:codes (点击key输出的值)
  • android:horizontalGap key之间的水平间隙
  • android:iconPreview 弹出预览中显示的图片
  • android:isModifier 是否这是一个功能key,如键盘中的Alt or Shift
  • android:isRepeatable 按住不放,是否一直输出内容
  • android:isSticky 是否是一个切换键
  • android:keyEdgeFlags 边界标记
  • android:keyHeight key的高度
  • android:keyIcon  key显示的图片
  • android:keyLabel key显示的内容的内容.
  • android:keyOutputText 按这个键时,输出字符的字符串
  • android:keyWidth key宽度
  • android:popupCharacters 字符显示在预览键盘
  • android:popupKeyboard 自定义预览布局



2.3Keyboard.Row类

键(Key)在键盘的容器。他的属性会覆盖keyboard中的属性

属性有:



  • android:horizontalGap 每个Key之间的默认间距.
  • android:keyHeight Key的默认高度
  • android:keyWidth Key的默认宽度
  • android:keyboardMode keyboard的模式(Mode)
  • android:rowEdgeFlags 边境标记
  • android:verticalGap 垂直Key之间的差距.



注意

1.观察这三个类的属性,Keyboard.Row类属性和Keyboard类属性有四个属性是一样的:


  • android:horizontalGap 每个Key之间默认的水平间隙
  • android:keyHeight Key的默认高度.
  • android:keyWidth Key的默认宽度.
  • android:verticalGap 每行(Row)之间的默认行间隙

当根布局Keyboard和列布局Keyboard.Row都写有这样的属性的时候,根布局的属性将会被取代。同理,Keyboard.Row 和keyboard.Key有的共同属性,如果key重写,也将按照key设置的为准,有点类似java中重写方法的作用。

2.上边的属性都是设置key之前的间隙,却没有设置最外边key和内边距。那么这个应该怎么设置呢?

写一个空的行或者空的key去解决;

<Row android:keyHeight="0%p" />复制代码
<Key
    android:codes="-9999"
    android:keyWidth="0%p" />复制代码

最后贴上一个带有键盘布局的注释代码:

<?xml version="1.0" encoding="utf-8"?><!--根布局设置键盘整体的每个key之间的间隙和键的宽度和高度-->
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="3%p"
    android:keyHeight="7.5%p"
    android:keyWidth="21.25%p"
    android:verticalGap="1.5%p">

    <!--类似与内边距属性-->
    <Row android:keyHeight="0%p" />

    <Row>
        <Key
            android:codes="49"
            android:keyLabel="1" />
        <Key
            android:codes="50"
            android:keyLabel="2" />
        <Key
            android:codes="51"
            android:keyLabel="3" />
        <!--由于这个key的高度和其他key的高度不一样,所以这里需要重写这个属性-->
        <Key
            android:codes="-5"
            android:isRepeatable="true"
            android:keyHeight="16.5%p"
            android:keyIcon="@drawable/ic_outline_backspace_24dp" />
        <!--类似与内边距属性-->
        <Key
            android:codes="-9999"
            android:keyWidth="0%p" />
    </Row>
    <Row>
        <Key
            android:codes="52"
            android:keyLabel="4" />
        <Key
            android:codes="53"
            android:keyLabel="5" />
        <Key
            android:codes="54"
            android:keyLabel="6" />
    </Row>
    <Row>
        <Key
            android:codes="55"
            android:keyLabel="7" />
        <Key
            android:codes="56"
            android:keyLabel="8" />
        <Key
            android:codes="57"
            android:keyLabel="9" />
        <!--由于这个key的高度和其他key的高度不一样,所以这里需要重写这个属性-->
        <Key
            android:codes="-4"
            android:keyHeight="16.5%p"
            android:keyLabel="确定" />
    </Row>

    <Row>
        <!--这里从新定于键的宽度-->
        <Key
            android:codes="789789"
            android:keyLabel="ABC"
            android:keyWidth="9.875%p" />
        <!--这里从新定于键的宽度-->
        <Key
            android:codes="45"
            android:horizontalGap="1.5%p"
            android:keyLabel="-"
            android:keyWidth="9.875%p" />
        <Key
            android:codes="48"
            android:keyLabel="0" />
        <Key
            android:codes="46"
            android:keyLabel="." />
    </Row>
    <!--下边的内边距-->
    <Row android:keyHeight="0%p" />
</Keyboard>复制代码

到此我想读者应该可以随心所欲的写出来一个满足产品需求的键盘布局。

三设置自定义键盘样式

上边我们写出来一个黑不溜秋的键盘,满足不了任何产品的需求,google设置键盘的样式通过KeyboardView类属性提供给开发者。

3.1KeyboardView类

它继承View,实现了View.OnClickListener接口,官网对这个类的描述:一个呈现虚拟键盘的视图。它处理key的渲染和检测按键和触摸动作。

它有一个内部类KeyboardView.OnKeyboardActionListener:虚拟键盘事件的监听器。

属性有:


  • android:background :设置整个键盘的背景,View共有的属性
  • android:keyBackground key的背景 ,建议用drawable类型选择器,显示选中与未选中效果,这个属性设置的是所有key的背景,关于不同背景设置后边介绍。
  • android:keyPreviewLayout 按钮按下时提示布局视图,不要时候可以设置成@null
  • android:keyTextColor 按钮文字的颜色
  • android:keyTextSize 按钮文字的大小
  • android:labelTextSize 没有图片的按钮中文本大小
  •  android:shadowColor 设置key中字体阴影的颜色要不字体显示模糊。

当我们把我们需要设置的属性设置进去,呈现出来的键盘如下图:

        

比上边黑不溜秋的好看多了。通过上边需求我们基本可以满足产品需求。

3.2设置单个key的样式

但是如果需求要求是“确定key”的背景颜色和其他的key颜色不一样,比如就这一个key设置成蓝色,对于这个需求google并未给我们提供设置单个key的背景颜色的属性,但是我们要去实现这个效果,我们该怎么办呢?

这个时候我们就要自定义KeyboardView类,重写onDraw方法,加上绘制特殊key的背景,覆盖掉原来的key。

绘制代码如下:

public class CustomKeyboardView extends KeyboardView {

    private Paint mtextPaint;

    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() {
        mtextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mtextPaint.setTextAlign(Paint.Align.CENTER);
        mtextPaint.setTextSize(46);
        mtextPaint.setColor(Color.WHITE);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        List<Keyboard.Key> keys = getKeyboard().getKeys();

        for (Keyboard.Key key : keys) {
            if (key.codes[0] == -4) {
                //获取drawable资源
                Drawable drawable = getContext().getResources().getDrawable(R.drawable.srue_bg_key);
                //把真正的状态设置给上层绘制的key键
                drawable.setState(key.getCurrentDrawableState());
                //rawable将在被绘制在canvas的哪个矩形区域内(这个矩形区域的坐标是以canvas左上角为坐标原点的)
                drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
                //开始绘制
                drawable.draw(canvas);
                //绘制文字
                if (key.label != null) {
                    Paint.FontMetrics fontMetrics = mtextPaint.getFontMetrics();
                    //计算基线
                    Float baseLine = key.y + key.height / 2 + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
                    //绘制文字
                    canvas.drawText(key.label.toString(), key.x + (key.width / 2), baseLine, mtextPaint);
                }

            }
        }


    }
}复制代码

效果图如下:

       

由于篇幅限制,另起一篇讲解更多关于自定义键盘的更多技巧。