Android自定义键盘之汉字键盘

实现软键盘主要用到了系统的两个类:Keyboard和KeyboardView。

Keyboard类源码的介绍是: Listener for virtual keyboard events.即用于监听虚拟键盘。

KeyboardView类源码的介绍是: A view that renders a virtual {@link Keyboard}. It handles rendering of keys and detecting key presses and touch movements.即它处理绘制键盘和检测按键和触摸动作。

它里面有很多方法,在我们自定义的软键盘很多属性,就需要我们用这个类来设置。比如:

keyboardView = (KeyboardView) act.findViewById(R.id.keyboard_view);
keyboardView.setKeyboard(k);
keyboardView.setEnabled(true);
keyboardView.setPreviewEnabled(true);
keyboardView.setVisibility(View.VISIBLE);
keyboardView.setOnKeyboardActionListener(listener);

了解一些源码,就可以是我们知道我们为什么要这样写,为什么要这样做了!

首先在res下新建xml文件夹,在xml文件夹中新建symbols.xml文件,这个布局文件主要是实现数字软键盘的布局,每一个按键都有一个codes值,在类中就是通过codes值来监听每一个按钮。

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="20%p" android:horizontalGap="0px"
    android:verticalGap="0px" android:keyHeight="@dimen/key_height">

    <Row>
        <Key android:codes="49" android:keyLabel="1" />
        <Key android:codes="50" android:keyLabel="2" />
        <Key android:codes="51" android:keyLabel="3" />
        <Key android:codes="52" android:keyLabel="4" />
        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" />
    </Row>

    <Row>
        <Key android:codes="53" android:keyLabel="5" />
        <Key android:codes="54" android:keyLabel="6" />
        <Key android:codes="55" android:keyLabel="7" />
        <Key android:codes="56" android:keyLabel="8" />
        <Key android:codes="-2" android:keyLabel="中文" />
    </Row>

    <Row>
        <Key android:codes="57" android:keyLabel="9" />
        <Key android:codes="48" android:keyLabel="0" />
        <Key android:codes="46" android:keyLabel="." />
        <Key android:codes="-3" android:keyWidth="40%p"
            android:isRepeatable="true" android:keyLabel="完成" />
    </Row>

</Keyboard>

数字键盘界面如下:

在上面的键盘定义中,通过Keyboard说明是一个软键盘定义文件,Row元素说明这是一行按键的定义,Key元素说明这是一个按键的定义。Key元素通过一些属性来定义每个按键,下面是一些常用的属性介绍:

Codes:代表按键对应的输出值,可以为unicode值或者逗号(,)分割的多个值,也可以为一个字符串。在字符串中通过“\”来转义特殊字符,例如 ‘\n’ 或则 ‘\uxxxx’ 。Codes通常用来定义该键的键码,例如上图中的数字按键1对应的为49;如果提供的是逗号分割的多个值则和普通手机输入键盘一样在多个值之间切换。

keyLabel:代表按键显示的文本内容。

keyIcon:代表按键显示的图标内容,如果指定了该值则在显示的时候显示为图片不显示文本。

keyWidth:代表按键的宽度,可以为精确值或则相对值,对于精确值支持多种单位,例如:像素,英寸 等;相对值为相对于基础取值的百分比,为以% 或%p 结尾,其中%p表示相对于父容器。

keyHeight:代表按键的高度,取值同上。

horizontalGap:代表按键前的间隙(水平方向),取值同上。

isSticky:指定按键是否为sticky的。例如Shift大小写切换按键,具有两种状态,按下状态和正常状态,取值为true或者false。

isModifier:指定按键是否为功能键( modifier key ) ,例如 Alt 或者 Shift ,取值为true或false。

keyOutputText:指定按键输出的文本内容,取值为字符串。

isRepeatable:指定按键是否是可重复的,如果长按该键可以触发重复按键事件则为true,否则为false。

keyEdgeFlags:指定按键的对齐指令,取值为left或right。

我们在设置每一个按键的code时,就是根据keyboard类中定义的一些属性,比如回退,切换,完成等都是固定的。

  public static final int KEYCODE_SHIFT = -1;
  public static final int KEYCODE_MODE_CHANGE = -2;
  public static final int KEYCODE_CANCEL = -3;
  public static final int KEYCODE_DONE = -4;
  public static final int KEYCODE_DELETE = -5;
  public static final int KEYCODE_ALT = -6;

知道了这些,我们就不会有太多的疑惑了!也是说,我们自定义的每一个按键都将会有一个codes值,比如回退我们就写成:

<Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" />

在监听处就是:

if (primaryCode == Keyboard.KEYCODE_DELETE){}

这就表示,监听回退事件了!

然后在xml文件夹中新建chinese.xml文件,这个布局文件主要是实现中文软键盘的布局,每一个按键都有一个codes值,这个codes就是一个汉字在utf16标准中的编码值。

查找汉字的编码:两个方法:

1,直接查找utf16表,可以参考:http://blog.csdn.net/lintax/article/details/51866861

如果汉字个数比较多,需要将汉字与编码值一个个准确对应的键入xml中,也是一个挺费神的事情。

2,使用程序员的办法,用代码来帮我们实现:

//get the codes for xml
        char c;
        int i;
        String[] privinces = { "一",  "二", "三", "四", "五", "六", "年", "级", "班", "."};
        for(String str : privinces){
            c=str.toCharArray()[0];
            i=c;
            //xml中格式:<Key android:codes="19968" android:keyLabel="一" />
            Log.i("key","<Key android:codes=\""+i+"\" android:keyLabel=\""+c+"\" />");
        }

这样,就按照xml中的格式,写好了汉字与编码值的关联语句。

剩下还有一个小问题:在logcat的输出中,还有其他的信息,如时间、log等级等,如下:

07-16 18:20:12.220: I/key(5200): <Key android:codes="29677" android:keyLabel="班" />

我们可以将logcat信息保存到文本文件中,使用一个文本编辑器将前面的不想要的信息(此处是“07-16 18:20:12.220: I/key(5200): ”)全部替换为8个空格即可。

为何是8个空格?是为了xml中的格式对齐。

最终,xml中文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Keyboard android:keyWidth="25.000002%p" android:keyHeight="@dimen/key_height"
    android:horizontalGap="0.0px" android:verticalGap="0.0px"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <Row>
        <Key android:codes="19968" android:keyLabel="一" />
        <Key android:codes="20108" android:keyLabel="二" />
        <Key android:codes="19977" android:keyLabel="三" />
        <Key android:codes="-5" android:isRepeatable="true"
            android:keyIcon="@drawable/sym_keyboard_delete" />
    </Row>

    <Row>
        <Key android:codes="22235" android:keyLabel="四" />
        <Key android:codes="20116" android:keyLabel="五" />
        <Key android:codes="20845" android:keyLabel="六" />
        <Key android:codes="-2" android:keyLabel="数字" />
    </Row>

    <Row>
        <Key android:codes="24180" android:keyLabel="年" />
        <Key android:codes="32423" android:keyLabel="级" />
        <Key android:codes="29677" android:keyLabel="班" />
        <Key android:keyWidth="25.000004%p" android:codes="-3"
            android:keyEdgeFlags="right" android:keyLabel="完成" />
    </Row>

</Keyboard>

中文键盘最终画面如下:

然后创建一个类,用于处理软键盘事件,文件名为KeyboardUtil.Java,内容如下:

package cn.key;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.Keyboard.Key;
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.text.Editable;
import android.view.View;
import android.widget.EditText;

public class KeyboardUtil {
    private Context ctx;
    private Activity act;
    private KeyboardView keyboardView;
    private Keyboard k1;// 中文键盘
    private Keyboard k2;// 数字键盘
    public boolean isNumber = false;// 是否数字键盘
    public boolean isUpper = false;// 是否大写

    private EditText ed;

    public KeyboardUtil(Activity act, Context ctx, EditText edit) {
        this.act = act;
        this.ctx = ctx;
        this.ed = edit;
        k1 = new Keyboard(ctx, R.xml.chinese);
        k2 = new Keyboard(ctx, R.xml.symbols);
        keyboardView = (KeyboardView) act.findViewById(R.id.keyboard_view);
        keyboardView.setKeyboard(k1);
        keyboardView.setEnabled(true);
        keyboardView.setPreviewEnabled(true);
        keyboardView.setOnKeyboardActionListener(listener);
    }

    private OnKeyboardActionListener listener = new OnKeyboardActionListener() {
        @Override
        public void swipeUp() {
//          super.swipeUp();

        }

        @Override
        public void swipeRight() {
        }

        @Override
        public void swipeLeft() {
        }

        @Override
        public void swipeDown() {
        }

        @Override
        public void onText(CharSequence text) {
//          super.onText(text);
        }

        @Override
        public void onRelease(int primaryCode) {
        }

        @Override
        public void onPress(int primaryCode) {
        }

        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            Editable editable = ed.getText();
            int start = ed.getSelectionStart();
            if (primaryCode == Keyboard.KEYCODE_CANCEL) {//完成
                hideKeyboard();
            } else if (primaryCode == Keyboard.KEYCODE_DELETE) {//回退
                if (editable != null && editable.length() > 0) {
                    if (start > 0) {
                        editable.delete(start - 1, start);
                    }
                }
            } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {//大小写切换

    changeKey();
                keyboardView.setKeyboard(k1);

            } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {//数字键盘切换
                if (isNumber) {
                    isNumber = false;
                    keyboardView.setKeyboard(k1);
                } else {
                    isNumber = true;
                    keyboardView.setKeyboard(k2);
                }
            } else if (primaryCode == 57419) { // go left
                if (start > 0) {
                    ed.setSelection(start - 1);
                }
            } else if (primaryCode == 57421) { // go right
                if (start < ed.length()) {
                    ed.setSelection(start + 1);
                }
            } else {
                editable.insert(start, Character.toString((char) primaryCode));
            }
        }
    };

    /**
     * 键盘大小写切换
     */
    private void changeKey() {
        List<Key> keylist = k1.getKeys();
        if (isUpper) {//大写切换小写
            isUpper = false;
            for(Key key:keylist){
                if (key.label!=null && isword(key.label.toString())) {
                    key.label = key.label.toString().toLowerCase();
                    key.codes[0] = key.codes[0]+32;
                }
            }
        } else {//小写切换大写
            isUpper = true;
            for(Key key:keylist){
                if (key.label!=null && isword(key.label.toString())) {
                    key.label = key.label.toString().toUpperCase();
                    key.codes[0] = key.codes[0]-32;
                }
            }
        }
    }

    public void showKeyboard() {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.GONE || visibility == View.INVISIBLE) {
            keyboardView.setVisibility(View.VISIBLE);
        }
    }

    public void hideKeyboard() {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.VISIBLE) {
            keyboardView.setVisibility(View.INVISIBLE);
        }
    }

    private boolean isword(String str){
        String wordstr = "abcdefghijklmnopqrstuvwxyz";
        if (wordstr.indexOf(str.toLowerCase())>-1) {
            return true;
        }
        return false;
    }

    public void showChinese() {
        showKeyboard();
        isNumber = false;
        keyboardView.setKeyboard(k1);
    }

    public void showNumber() {
        showKeyboard();
        isNumber = true;
        keyboardView.setKeyboard(k2);
    }

}

接下来就是实现activity的视图布局文件了,文件名为main.xml,内容如下:

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

    <LinearLayout
        android:id="@+id/ll_hint"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tv_class"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginLeft="10dp"
            android:text="班级"
            android:textSize="20dip" />

        <TextView
            android:id="@+id/tv_scrore"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginRight="20dp"
            android:text="得分"
            android:textSize="20dip" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/ll_hint"
        android:layout_marginTop="10dp"
        android:orientation="horizontal" >

        <EditText
            android:id="@+id/edit_class"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="请输入班级" />

        <EditText
            android:id="@+id/edit_score"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="请输入分数" />
    </LinearLayout>

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <android.inputmethodservice.KeyboardView
            android:id="@+id/keyboard_view"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@color/lightblack"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:keyBackground="@drawable/btn_keyboard_chinese"
            android:keyTextColor="@color/white"
            android:visibility="gone" />
    </RelativeLayout>

</RelativeLayout>

界面比较简单,主要是两个文本输入框,一个是班级(输入汉字),一个是分数(输入数字)。另外有一个隐藏的KeyboardView,在我们点击文本输入框时,会显示出来。

最后就在你要执行的activity中,添加一些代码就行了,剩下的就和其他控件使用方式一样了,类名为KeydemoActivity.java,内容如下:

package cn.key;

import java.lang.reflect.Method;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.EditText;

public class KeydemoActivity extends Activity {
    private Context ctx;
    private Activity act;
    private EditText edit_class;
    private EditText edit_score;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        //get the codes for xml
        char c;
        int i;
        String[] privinces = { "一",  "二", "三", "四", "五", "六", "年", "级", "班", "."};
        for(String str : privinces){
            c=str.toCharArray()[0];
            i=c;
            //xml中格式:<Key android:codes="19968" android:keyLabel="一" />
            Log.i("key","<Key android:codes=\""+i+"\" android:keyLabel=\""+c+"\" />");
        }

        edit_class = (EditText) this.findViewById(R.id.edit_class);
        edit_score = (EditText) this.findViewById(R.id.edit_score);

        //禁止弹出系统默认的软键盘
        if (android.os.Build.VERSION.SDK_INT <= 10) {
            edit_class.setInputType(InputType.TYPE_NULL);
        } else {
            this.getWindow().setSoftInputMode

(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
            try {
                Class<EditText> cls = EditText.class;
                Method setSoftInputShownOnFocus;
                setSoftInputShownOnFocus = cls.getMethod("setShowSoftInputOnFocus", 

boolean.class);
                setSoftInputShownOnFocus.setAccessible(true);
                setSoftInputShownOnFocus.invoke(edit_class, false);
                setSoftInputShownOnFocus.invoke(edit_score, false);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //禁止长按选择
        edit_class.setCustomSelectionActionModeCallback(new Callback() {
            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }
            @Override
            public void onDestroyActionMode(ActionMode mode) {
            }
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                //这里可以添加自己的菜单选项(前提是要返回true的)
                return false;//返回false 就是屏蔽ActionMode菜单
            }
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                return false;
            }
        });

        edit_score.setCustomSelectionActionModeCallback(new Callback() {
            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }
            @Override
            public void onDestroyActionMode(ActionMode mode) {
            }
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                //这里可以添加自己的菜单选项(前提是要返回true的)
                return false;//返回false 就是屏蔽ActionMode菜单
            }
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                return false;
            }
        });

        //设置监听动作,弹出自定义键盘
        ctx = this;
        act = this;
        edit_class.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                new KeyboardUtil(act, ctx, edit_class).showChinese();
                return false;
            }
        });

        edit_score.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                new KeyboardUtil(act, ctx, edit_score).showNumber();
                return false;
            }
        });

    }
}

最后的整体界面如下:

demo地址:

http://download.csdn.net/detail/lintax/9577994

参考:

http://blog.csdn.net/hfsu0419/article/details/7924673

http://blog.csdn.net/acrambler/article/details/13213181

时间: 2024-10-12 21:25:51

Android自定义键盘之汉字键盘的相关文章

android 自定义键盘 KeyboardView的key 文字颜色发虚模糊

开发中自定义键盘是否遇到文字发虚吗??如下图: 解决办法: 1. 在key的xml中设置key文字不用keyLabel ,而用keyIcon,即用图片来代替文本,但是这种方法比较笨 2.最简单的是在keyboardview中设置两个属性即可: android:shadowColor="@color/c_white" android:shadowRadius="0.0" shadowColor 设置跟你按键的背景色一致即可!!! 这样按键的文字就会显示的很清晰了啊!!

android 自定义键盘,代码实现自定义属性(自定义键盘背景,各个键的背景等)

由于工作需要,所以接触了自定义键盘.但是发现自己写的键盘太过丑陋了一些.废话不多说,先上图     第一张是修改后的.第二张是修改钱的.这基本上就是 OK.接下来就是重点了. android的keyboardview的属性中是有keybackground 的,但是在使用的时候,却发现没有生效.仔细看了下源码才发现.下面的这句话,把属性集合给设置成了空.所以就键盘的属性就一直无法生效. KeyboardView mKeyboardView = new KeyboardView(this, null

android自定义键盘(解决弹出提示的字体颜色问题)

最近准备要做一个项目,需要用到自定义小键盘来确保安全,而且还需要精确获得用户点击键盘时的落点位置.力度.指尖接触屏幕的面积等参数. 在写自定义键盘的时候,用到了国内网上的一些代码,出处是 http://blog.csdn.net/hfsu0419/article/details/7924673 向先人致敬! 然后发现down下来的代码用到我的项目时,出现了各种问题: 1.首先,是一打开应用,就会出现弹出的是系统的输入法键盘,而是不自定义键盘,这个问题是由于EditText会在应用打开的使用获得焦

Android 平板中 自定义键盘(popuwindow) 居于屏幕左下方 仿微信的密码输入界面

之前博客中,介绍过使用谷歌提供的键盘的一些api,可以很好地自定义键盘,参考我之前的博客链接:android 自定义键盘 ,这个有一个局限性,只能占满屏幕,无法做到只能占一部分的需求键盘,如下图我平板中的键盘需求:(由于公司保密需要,所以比较恐怖一点,嘿嘿) 类似于上方的左下角的一小块键盘,这就不能使用系统自带的一些键盘api,需要自己写布局,并且对输入的金额进行位数的限制,以及一些栏位输入的整数和小数位的控制,整体的实现步骤如下; 1.点击某个edittext,弹出下方的键盘(也就是使用pop

Android 自定义的数字键盘 支持随意拖动 和稳定的字符输入的控件

经过 研究 实现了自定义 键盘 ,支持随意拖动 和数字及其他字符输入 下面是主要的代码 和使用方法 import android.content.Context; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.OnGestureListener; import android.view.Gravity; import android.view.Mo

Android自定义软键盘

前不久由于项目的需要,要做一个自定义的软键盘,我也上网看了很多,都觉得很繁琐,所以想自己动手实现个.以备不时之需把.我选择了参考百度钱包的软键盘,看起来还不错: 下面一起来实现它: 1.写一个键盘控件,这个实现起来比较简单,就不多说了 public class SoftInputBoard extends RelativeLayout implements View.OnClickListener{ private Scroller mScroller; private int mScreenH

(转)[原] Android 自定义View 密码框 例子

遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 样子 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="border

[原] Android 自定义View 密码框 例子

遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 样子 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="border

Android 自定义View合集

自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/Mr-XiaoLiang 自定义控件三部曲 http://blog.csdn.net/harvic880925?viewmode=contents Android 从0开始自定义控件之View基础知识与概念 http://blog.csdn.net/airsaid/article/details/5