InputMethodService
InputMethodService是整个输入法框架的核心,我们要写一个输入法,最核心的就是继承InputMethodService。
之后我们要重写几个重要的方法: onInitializeInterface()
看名字就知道,他是在初始化界面的时候被调用,而一般是一位配置文件的更改导致该函数的执行 onBinndInput()
它在另外的客户端和该输入法连接时调用 onStartInput()
这个非常重要的一个回调,它在编辑框中用户已经开始输入的时候调用。比如你点击一个输入框,你需要根据这个输入框的信息,设置输入法的一些特性,这个在 Sample中很有体会 onCreateInputView()
是返回一个层次性的输入视图,而且只是在第一次这个视图显示的时候被调用。 onCreateCandidatesView()
跟上一个一样,只不过创建的是候选框的视图。 onCreateExtractTextView()
他是在全屏模式下的一个视图。 onStartInputView()
它是在输入视图被显示并且在一个新的输入框中输入已经开始的时候调用。
最后我们需要在AndroidManifest.xml中进行注册,代码声明了一个输入法的service.它请求了 BIND_INPUT_METHOD 权限,来允许服务把IME和系统关联起来,创建一个 intent filter来匹配 android.view.InputMethod,并且定义了输入法的元数据:
<service
android:name="xxxxIMS"
android:permission="android.permission.BIND_INPUT_METHOD" >
<intent-filter>
<action android:name="android.view.InputMethod" />
</intent-filter>
<meta-data
android:name="android.view.im"
android:resource="@xml/method" />
</service>
我们在res文件夹下新建xml文件夹,之后新建method.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- The attributes in this XML file provide configuration information -->
<!-- for the Search Manager. -->
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="xx.xx.MainActivity" />
这里指定了 MainActivity为输入法的设置activity。
两个重要的View
①软键盘输入视图。这个是和用户交互的主要发生地:按键,画图或者其他的方式。通常的实现就是简单的用一个视图来处理所有的工作,并且在调用onCreateInputView的时候返回一个新的实例。那么你怎么知道该不该来显示你的输入视图呢,这个可以调用系统的 onEvaluateInputViewShow来测试是否需要,这个是系统根据当前的上下文环境来实现的。所以你的输入法状态改变的时候,就需要调用 updateInputViewShown来重新估计一下。
软键盘视图需要我们在res/xml/下自定义,类似这样:
<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
android:horizontalGap="0.0px"
android:keyHeight="32%p"
android:keyWidth="100%p"
android:verticalGap="0.0px">
<Row android:keyHeight="8%p">
<Key
android:codes="49"
android:keyEdgeFlags="left"
android:keyLabel="Q" />
<Key
android:codes="50"
android:keyLabel="W" />
<Key
android:codes="51"
android:keyLabel="E" />
<Key
android:codes="52"
android:keyLabel="R" />
<Key
android:codes="53"
android:keyLabel="T" />
<Key
android:codes="54"
android:keyLabel="Y" />
<Key
android:codes="55"
android:keyLabel="U" />
<Key
android:codes="56"
android:keyLabel="I" />
<Key
android:codes="57"
android:keyLabel="O" />
<Key
android:codes="58"
android:keyEdgeFlags="right"
android:keyLabel="P" />
</Row>
<Row android:keyHeight="8%p">
<Key
android:codes="59"
android:keyEdgeFlags="left"
android:keyLabel="A" />
<Key
android:codes="60"
android:keyLabel="S" />
<Key
android:codes="61"
android:keyLabel="D" />
<Key
android:codes="62"
android:keyLabel="F" />
<Key
android:codes="63"
android:keyLabel="G" />
<Key
android:codes="64"
android:keyLabel="H" />
<Key
android:codes="65"
android:keyLabel="J" />
<Key
android:codes="66"
android:keyLabel="K" />
<Key
android:codes="67"
android:keyEdgeFlags="right"
android:keyLabel="L" />
</Row>
<Row android:keyHeight="8%p">
<Key
android:codes="-1999"
android:keyEdgeFlags="left"
android:isSticky="true"
android:keyIcon="@mipmap/sym_keyboard_shift" />
<Key
android:codes="68"
android:keyLabel="Z" />
<Key
android:codes="69"
android:keyLabel="X" />
<Key
android:codes="70"
android:keyLabel="C" />
<Key
android:codes="71"
android:keyLabel="V" />
<Key
android:codes="72"
android:keyLabel="B" />
<Key
android:codes="73"
android:keyLabel="N" />
<Key
android:codes="74"
android:keyLabel="M" />
<Key
android:codes="-2000"
android:keyEdgeFlags="right"
android:isRepeatable="true"
android:keyIcon="@mipmap/sym_keyboard_delete" />
</Row>
<Row android:keyHeight="8%p">
<Key
android:codes="-2001"
android:keyEdgeFlags="left"
android:keyLabel="~*" />
<Key
android:codes="-2002"
android:keyLabel="123" />
<Key
android:codes="-2003"
android:keyLabel="," />
<Key
android:codes="-2004"
android:isRepeatable="true"
android:keyIcon="@mipmap/sym_keyboard_space" />
<Key
android:codes="-2005"
android:keyLabel="。" />
<Key
android:codes="-2006"
android:keyLabel="CH" />
<Key
android:codes="-2007"
android:keyEdgeFlags="right"
android:isRepeatable="true"
android:keyIcon="@mipmap/sym_keyboard_return" />
</Row>
</Keyboard>
Codes:代表按键对应的输出值,可以为unicode值或则逗号(,)分割的多个值,也可以为一个字 符串。在字符串中通过“\”来转义特殊字符,例如 ‘\n’ 或则 ‘\uxxxx’ 。Codes通常用来定义该键的键码,如果提供的是逗号分割的多个值则和普通手机输入键盘一样在多个值之间切换。
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。
还有在Android源码中一些功能性的按键是有固定的codes的:
public static final int CODE_SHIFT = -1;
public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
public static final int CODE_OUTPUT_TEXT = -3;
public static final int CODE_DELETE = -4;
public static final int CODE_SETTINGS = -5;
public static final int CODE_SHORTCUT = -6;
public static final int CODE_ACTION_ENTER = -7;
public static final int CODE_ACTION_NEXT = -8;
public static final int CODE_ACTION_PREVIOUS = -9;
public static final int CODE_LANGUAGE_SWITCH = -10;
public static final int CODE_RESEARCH = -11;
// Code value representing the code is not specified.
public static final int CODE_UNSPECIFIED = -12;
②候选词视图。 当用户输入一些字符之后,输入法可能需要提供给用户一些可用的候选词的列表。这个视图的管理和输入视图不大一样,因为这个视图是非常的短暂的,它只是在有候选词的时候才会被显示。可以用setCandidatesViewShow来设置是否需要显示这个视图。正是因为这个显示的频繁性,所以它一般不会被销毁,而且不会改变你的应用程序的视图。
候选词视图CandidatesView,我们一般直接继承于View来自定义。
加载输入法布局
一般整个输入法布局,我们继承RelativeLayout。
布局文件:
<?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:background="@color/transparent"
android:padding="5dp"
android:orientation="vertical">
<com.xxx.xxx.view.CandidateView
android:id="@+id/candidate"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.inputmethodservice.KeyboardView
android:id="@+id/keyboard_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/white"
android:focusable="true"
android:focusableInTouchMode="true"
android:keyBackground="@drawable/btn_keyboard_key"
android:keyTextColor="@color/white"
android:visibility="visible" />
</LinearLayout>
其中的一些属性:
android:background=”@color/white”整个输入法的背景
android:keyBackground代表按键的背景
android:keyPreviewHeight按下后预览字符的高度
android:keyPreviewLayout按下后预览字符的布局(有默认的)
android:keyPreviewOffset偏移量,调整预览时显示的位置
android:keyTextColor 键盘按钮上文字颜色
输入法按键的点击事件,需要我们写 keyboardView.setOnKeyboardActionListener(listener);
传入 KeyboardView.OnKeyboardActionListener的对象,在重写的onKey( int primaryCode, int[] keyCodes)中写具体的逻辑。其中的参数primaryCode就是按键key的codes属性的值。
在点击事件中想要改变其中一个key的一些属性:
List<Keyboard.Key> keys = keyboard.getKeys();
for (Keyboard.Key key : keys) {
if (key.codes[0] == -2006) {
//具体的逻辑
}
}
向输入框发送文本信息
当用户使用你开发的输入法输入文本时,你可以发送独立的键盘事件或者编辑光标所在的文本,来将文本发送到应用程序。在任何一种情况中,你可以使用一个InputConncetion
实例来发送文本,可以通过调用 InputMethodService.getCurrentInputConnection()
来获得实例。
①在一个文本框中处理文本的时候, BaseInputConnection类中包含了一些有用的方法: getTextBeforeCursor()
返回一个包含光标前指定个数的字符 CharSequence getTextAfterCursor()
返回一个包含光标后面指定个数的字符 CharSequence deleteSurroundingText()
删除光标前后指定个数的字符 commitText(text,0)
提交文本并且重新设置光标的位置。
②发送前组织文本
如果你的输入法支持文本预测或者需要多个步骤来组成一个符号或者单词,你可以在文本编辑区域展示这个过程,直到用户最终提交了这个单词,然后你可以用完整的文本替换掉部分的编辑中的文本。你可以在间断的通过调用 InputConnection#setComposingText(text,1)
提交文本。