如何开发Android的软键盘?
我想玩一些想法,并开发Android的软键盘来取代默认的。
-
有没有关于Android软键盘开发的一般信息? 任何最佳实践或指导方针?
-
我可以使用我的键盘应用程序来处理一些正常的Android应用程序吗?
-
我可以通过HTTP连接将键盘数据与云端数据库和其他手机进行同步吗?
-
我是否可以通过按键打开其他窗口/屏幕,例如显示与正常QWERTY不同的自定义input界面。 如果这不起作用,我可以使用popup对话框吗?
一些技巧:
- 阅读本教程: 创buildinput法
- 克隆这个回购: LatinIME
关于你的问题:
inputMethod基本上是一个Android Service
,所以是的,你可以做HTTP和所有你可以在一个Service
做的事情。
您可以从InputMethod
打开Activities
和对话框。 再次,这只是一个Service
。
我一直在开发一个IME,所以再问一次是否遇到问题。
开发人员文档提供的示例应用程序是一个很好的开始。
- 指导方针应该是尽可能使其可用。 看看市场上的其他人,看看你应该瞄准什么
- 是的,服务可以做大部分事情,包括互联网; 前提是您已经要求这些权限
- 如果您在键盘上执行某些操作时遇到问题,您可以打开活动并执行任何您喜欢的操作。 例如HTC的键盘有一个打开设置活动的button,另一个打开一个对话框来更改语言。
看看其他IME,看看你应该瞄准什么。 有些(如官方的)是开源的。
为自己的EditText创build自定义键盘
下载整个代码
在这篇文章中,我创build简单的键盘,其中包含一些特殊的键(法国键),它支持大写字母和小写字母和数字键和一些符号。
package sra.keyboard; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.View.OnTouchListener; import android.widget.Button; import android.widget.EditText; import android.widget.RelativeLayout; public class Main extends Activity implements OnTouchListener, OnClickListener, OnFocusChangeListener { private EditText mEt, mEt1; // Edit Text boxes private Button mBSpace, mBdone, mBack, mBChange, mNum; private RelativeLayout mLayout, mKLayout; private boolean isEdit = false, isEdit1 = false; private String mUpper = "upper", mLower = "lower"; private int w, mWindowWidth; private String sL[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "ç", "à", "é", "è", "û", "î" }; private String cL[] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "ç", "à", "é", "è", "û", "î" }; private String nS[] = { "!", ")", "'", "#", "3", "$", "%", "&", "8", "*", "?", "/", "+", "-", "9", "0", "1", "4", "@", "5", "7", "(", "2", "\"", "6", "_", "=", "]", "[", "<", ">", "|" }; private Button mB[] = new Button[32]; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { setContentView(R.layout.main); // adjusting key regarding window sizes setKeys(); setFrow(); setSrow(); setTrow(); setForow(); mEt = (EditText) findViewById(R.id.xEt); mEt.setOnTouchListener(this); mEt.setOnFocusChangeListener(this); mEt1 = (EditText) findViewById(R.id.et1); mEt1.setOnTouchListener(this); mEt1.setOnFocusChangeListener(this); mEt.setOnClickListener(this); mEt1.setOnClickListener(this); mLayout = (RelativeLayout) findViewById(R.id.xK1); mKLayout = (RelativeLayout) findViewById(R.id.xKeyBoard); } catch (Exception e) { Log.w(getClass().getName(), e.toString()); } } @Override public boolean onTouch(View v, MotionEvent event) { if (v == mEt) { hideDefaultKeyboard(); enableKeyboard(); } if (v == mEt1) { hideDefaultKeyboard(); enableKeyboard(); } return true; } @Override public void onClick(View v) { if (v == mBChange) { if (mBChange.getTag().equals(mUpper)) { changeSmallLetters(); changeSmallTags(); } else if (mBChange.getTag().equals(mLower)) { changeCapitalLetters(); changeCapitalTags(); } } else if (v != mBdone && v != mBack && v != mBChange && v != mNum) { addText(v); } else if (v == mBdone) { disableKeyboard(); } else if (v == mBack) { isBack(v); } else if (v == mNum) { String nTag = (String) mNum.getTag(); if (nTag.equals("num")) { changeSyNuLetters(); changeSyNuTags(); mBChange.setVisibility(Button.INVISIBLE); } if (nTag.equals("ABC")) { changeCapitalLetters(); changeCapitalTags(); } } } @Override public void onFocusChange(View v, boolean hasFocus) { if (v == mEt && hasFocus == true) { isEdit = true; isEdit1 = false; } else if (v == mEt1 && hasFocus == true) { isEdit = false; isEdit1 = true; } } private void addText(View v) { if (isEdit == true) { String b = ""; b = (String) v.getTag(); if (b != null) { // adding text in Edittext mEt.append(b); } } if (isEdit1 == true) { String b = ""; b = (String) v.getTag(); if (b != null) { // adding text in Edittext mEt1.append(b); } } } private void isBack(View v) { if (isEdit == true) { CharSequence cc = mEt.getText(); if (cc != null && cc.length() > 0) { { mEt.setText(""); mEt.append(cc.subSequence(0, cc.length() - 1)); } } } if (isEdit1 == true) { CharSequence cc = mEt1.getText(); if (cc != null && cc.length() > 0) { { mEt1.setText(""); mEt1.append(cc.subSequence(0, cc.length() - 1)); } } } } private void changeSmallLetters() { mBChange.setVisibility(Button.VISIBLE); for (int i = 0; i < sL.length; i++) mB[i].setText(sL[i]); mNum.setTag("12#"); } private void changeSmallTags() { for (int i = 0; i < sL.length; i++) mB[i].setTag(sL[i]); mBChange.setTag("lower"); mNum.setTag("num"); } private void changeCapitalLetters() { mBChange.setVisibility(Button.VISIBLE); for (int i = 0; i < cL.length; i++) mB[i].setText(cL[i]); mBChange.setTag("upper"); mNum.setText("12#"); } private void changeCapitalTags() { for (int i = 0; i < cL.length; i++) mB[i].setTag(cL[i]); mNum.setTag("num"); } private void changeSyNuLetters() { for (int i = 0; i < nS.length; i++) mB[i].setText(nS[i]); mNum.setText("ABC"); } private void changeSyNuTags() { for (int i = 0; i < nS.length; i++) mB[i].setTag(nS[i]); mNum.setTag("ABC"); } // enabling customized keyboard private void enableKeyboard() { mLayout.setVisibility(RelativeLayout.VISIBLE); mKLayout.setVisibility(RelativeLayout.VISIBLE); } // Disable customized keyboard private void disableKeyboard() { mLayout.setVisibility(RelativeLayout.INVISIBLE); mKLayout.setVisibility(RelativeLayout.INVISIBLE); } private void hideDefaultKeyboard() { getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); } private void setFrow() { w = (mWindowWidth / 13); w = w - 15; mB[16].setWidth(w); mB[22].setWidth(w + 3); mB[4].setWidth(w); mB[17].setWidth(w); mB[19].setWidth(w); mB[24].setWidth(w); mB[20].setWidth(w); mB[8].setWidth(w); mB[14].setWidth(w); mB[15].setWidth(w); mB[16].setHeight(50); mB[22].setHeight(50); mB[4].setHeight(50); mB[17].setHeight(50); mB[19].setHeight(50); mB[24].setHeight(50); mB[20].setHeight(50); mB[8].setHeight(50); mB[14].setHeight(50); mB[15].setHeight(50); } private void setSrow() { w = (mWindowWidth / 10); mB[0].setWidth(w); mB[18].setWidth(w); mB[3].setWidth(w); mB[5].setWidth(w); mB[6].setWidth(w); mB[7].setWidth(w); mB[26].setWidth(w); mB[9].setWidth(w); mB[10].setWidth(w); mB[11].setWidth(w); mB[26].setWidth(w); mB[0].setHeight(50); mB[18].setHeight(50); mB[3].setHeight(50); mB[5].setHeight(50); mB[6].setHeight(50); mB[7].setHeight(50); mB[9].setHeight(50); mB[10].setHeight(50); mB[11].setHeight(50); mB[26].setHeight(50); } private void setTrow() { w = (mWindowWidth / 12); mB[25].setWidth(w); mB[23].setWidth(w); mB[2].setWidth(w); mB[21].setWidth(w); mB[1].setWidth(w); mB[13].setWidth(w); mB[12].setWidth(w); mB[27].setWidth(w); mB[28].setWidth(w); mBack.setWidth(w); mB[25].setHeight(50); mB[23].setHeight(50); mB[2].setHeight(50); mB[21].setHeight(50); mB[1].setHeight(50); mB[13].setHeight(50); mB[12].setHeight(50); mB[27].setHeight(50); mB[28].setHeight(50); mBack.setHeight(50); } private void setForow() { w = (mWindowWidth / 10); mBSpace.setWidth(w * 4); mBSpace.setHeight(50); mB[29].setWidth(w); mB[29].setHeight(50); mB[30].setWidth(w); mB[30].setHeight(50); mB[31].setHeight(50); mB[31].setWidth(w); mBdone.setWidth(w + (w / 1)); mBdone.setHeight(50); } private void setKeys() { mWindowWidth = getWindowManager().getDefaultDisplay().getWidth(); // getting // window // height // getting ids from xml files mB[0] = (Button) findViewById(R.id.xA); mB[1] = (Button) findViewById(R.id.xB); mB[2] = (Button) findViewById(R.id.xC); mB[3] = (Button) findViewById(R.id.xD); mB[4] = (Button) findViewById(R.id.xE); mB[5] = (Button) findViewById(R.id.xF); mB[6] = (Button) findViewById(R.id.xG); mB[7] = (Button) findViewById(R.id.xH); mB[8] = (Button) findViewById(R.id.xI); mB[9] = (Button) findViewById(R.id.xJ); mB[10] = (Button) findViewById(R.id.xK); mB[11] = (Button) findViewById(R.id.xL); mB[12] = (Button) findViewById(R.id.xM); mB[13] = (Button) findViewById(R.id.xN); mB[14] = (Button) findViewById(R.id.xO); mB[15] = (Button) findViewById(R.id.xP); mB[16] = (Button) findViewById(R.id.xQ); mB[17] = (Button) findViewById(R.id.xR); mB[18] = (Button) findViewById(R.id.xS); mB[19] = (Button) findViewById(R.id.xT); mB[20] = (Button) findViewById(R.id.xU); mB[21] = (Button) findViewById(R.id.xV); mB[22] = (Button) findViewById(R.id.xW); mB[23] = (Button) findViewById(R.id.xX); mB[24] = (Button) findViewById(R.id.xY); mB[25] = (Button) findViewById(R.id.xZ); mB[26] = (Button) findViewById(R.id.xS1); mB[27] = (Button) findViewById(R.id.xS2); mB[28] = (Button) findViewById(R.id.xS3); mB[29] = (Button) findViewById(R.id.xS4); mB[30] = (Button) findViewById(R.id.xS5); mB[31] = (Button) findViewById(R.id.xS6); mBSpace = (Button) findViewById(R.id.xSpace); mBdone = (Button) findViewById(R.id.xDone); mBChange = (Button) findViewById(R.id.xChange); mBack = (Button) findViewById(R.id.xBack); mNum = (Button) findViewById(R.id.xNum); for (int i = 0; i < mB.length; i++) mB[i].setOnClickListener(this); mBSpace.setOnClickListener(this); mBdone.setOnClickListener(this); mBack.setOnClickListener(this); mBChange.setOnClickListener(this); mNum.setOnClickListener(this); } }
首先你应该定义一个.xml文件并在其中制作键盘UI:
<?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:keyWidth="12.50%p" android:keyHeight="7%p"> <!-- android:horizontalGap="0.50%p" android:verticalGap="0.50%p" NOTE When we add a horizontalGap in pixels, this interferes with keyWidth in percentages adding up to 100% NOTE When we have a horizontalGap (on Keyboard level) of 0, this make the horizontalGap (on Key level) to move from after the key to before the key... (I consider this a bug) --> <Row> <Key android:codes="-5" android:keyLabel="remove" android:keyEdgeFlags="left" /> <Key android:codes="48" android:keyLabel="0" /> <Key android:codes="55006" android:keyLabel="clear" /> </Row> <Row> <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left" /> <Key android:codes="50" android:keyLabel="2" /> <Key android:codes="51" android:keyLabel="3" /> </Row> <Row> <Key android:codes="52" android:keyLabel="4" android:keyEdgeFlags="left" /> <Key android:codes="53" android:keyLabel="5" /> <Key android:codes="54" android:keyLabel="6" /> </Row> <Row> <Key android:codes="55" android:keyLabel="7" android:keyEdgeFlags="left" /> <Key android:codes="56" android:keyLabel="8" /> <Key android:codes="57" android:keyLabel="9" /> </Row>
在这个例子中你有4行,每行有3个键。 你也可以在你想要的每个键上放一个图标。
那么你应该在你的活动用户界面中添加xml标签,如下所示:
<android.inputmethodservice.KeyboardView android:id="@+id/keyboardview1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/white" android:focusable="true" android:focusableInTouchMode="true" android:visibility="visible" />
同样在.java活动文件中,您应该定义键盘并将其分配给EditText:
CustomKeyboard mCustomKeyboard1 = new CustomKeyboard(this, R.id.keyboardview1, R.xml.horizontal_keyboard); mCustomKeyboard1.registerEditText(R.id.inputSearch);
这个代码将inputSearch(这是一个EditText)分配给你的键盘。
import android.app.Activity; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; import android.inputmethodservice.KeyboardView.OnKeyboardActionListener; import android.text.Editable; import android.text.InputType; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.View.OnTouchListener; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; public class CustomKeyboard { /** A link to the KeyboardView that is used to render this CustomKeyboard. */ private KeyboardView mKeyboardView; /** A link to the activity that hosts the {@link #mKeyboardView}. */ private Activity mHostActivity; /** The key (code) handler. */ private OnKeyboardActionListener mOnKeyboardActionListener = new OnKeyboardActionListener() { public final static int CodeDelete = -5; // Keyboard.KEYCODE_DELETE public final static int CodeCancel = -3; // Keyboard.KEYCODE_CANCEL public final static int CodePrev = 55000; public final static int CodeAllLeft = 55001; public final static int CodeLeft = 55002; public final static int CodeRight = 55003; public final static int CodeAllRight = 55004; public final static int CodeNext = 55005; public final static int CodeClear = 55006; @Override public void onKey(int primaryCode, int[] keyCodes) { // NOTE We can say '<Key android:codes="49,50" ... >' in the xml // file; all codes come in keyCodes, the first in this list in // primaryCode // Get the EditText and its Editable View focusCurrent = mHostActivity.getWindow().getCurrentFocus(); if (focusCurrent == null || focusCurrent.getClass() != EditText.class) return; EditText edittext = (EditText) focusCurrent; Editable editable = edittext.getText(); int start = edittext.getSelectionStart(); // Apply the key to the edittext if (primaryCode == CodeCancel) { hideCustomKeyboard(); } else if (primaryCode == CodeDelete) { if (editable != null && start > 0) editable.delete(start - 1, start); } else if (primaryCode == CodeClear) { if (editable != null) editable.clear(); } else if (primaryCode == CodeLeft) { if (start > 0) edittext.setSelection(start - 1); } else if (primaryCode == CodeRight) { if (start < edittext.length()) edittext.setSelection(start + 1); } else if (primaryCode == CodeAllLeft) { edittext.setSelection(0); } else if (primaryCode == CodeAllRight) { edittext.setSelection(edittext.length()); } else if (primaryCode == CodePrev) { View focusNew = edittext.focusSearch(View.FOCUS_BACKWARD); if (focusNew != null) focusNew.requestFocus(); } else if (primaryCode == CodeNext) { View focusNew = edittext.focusSearch(View.FOCUS_FORWARD); if (focusNew != null) focusNew.requestFocus(); } else { // insert character editable.insert(start, Character.toString((char) primaryCode)); } } @Override public void onPress(int arg0) { } @Override public void onRelease(int primaryCode) { } @Override public void onText(CharSequence text) { } @Override public void swipeDown() { } @Override public void swipeLeft() { } @Override public void swipeRight() { } @Override public void swipeUp() { } }; /** * Create a custom keyboard, that uses the KeyboardView (with resource id * <var>viewid</var>) of the <var>host</var> activity, and load the keyboard * layout from xml file <var>layoutid</var> (see {@link Keyboard} for * description). Note that the <var>host</var> activity must have a * <var>KeyboardView</var> in its layout (typically aligned with the bottom * of the activity). Note that the keyboard layout xml file may include key * codes for navigation; see the constants in this class for their values. * Note that to enable EditText's to use this custom keyboard, call the * {@link #registerEditText(int)}. * * @param host * The hosting activity. * @param viewid * The id of the KeyboardView. * @param layoutid * The id of the xml file containing the keyboard layout. */ public CustomKeyboard(Activity host, int viewid, int layoutid) { mHostActivity = host; mKeyboardView = (KeyboardView) mHostActivity.findViewById(viewid); mKeyboardView.setKeyboard(new Keyboard(mHostActivity, layoutid)); mKeyboardView.setPreviewEnabled(false); // NOTE Do not show the preview // balloons mKeyboardView.setOnKeyboardActionListener(mOnKeyboardActionListener); // Hide the standard keyboard initially mHostActivity.getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); } /** Returns whether the CustomKeyboard is visible. */ public boolean isCustomKeyboardVisible() { return mKeyboardView.getVisibility() == View.VISIBLE; } /** * Make the CustomKeyboard visible, and hide the system keyboard for view v. */ public void showCustomKeyboard(View v) { mKeyboardView.setVisibility(View.VISIBLE); mKeyboardView.setEnabled(true); if (v != null) ((InputMethodManager) mHostActivity .getSystemService(Activity.INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow(v.getWindowToken(), 0); } /** Make the CustomKeyboard invisible. */ public void hideCustomKeyboard() { mKeyboardView.setVisibility(View.GONE); mKeyboardView.setEnabled(false); } /** * Register <var>EditText<var> with resource id <var>resid</var> (on the * hosting activity) for using this custom keyboard. * * @param resid * The resource id of the EditText that registers to the custom * keyboard. */ public void registerEditText(int resid) { // Find the EditText 'resid' EditText edittext = (EditText) mHostActivity.findViewById(resid); // Make the custom keyboard appear edittext.setOnFocusChangeListener(new OnFocusChangeListener() { // NOTE By setting the on focus listener, we can show the custom // keyboard when the edit box gets focus, but also hide it when the // edit box loses focus @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) showCustomKeyboard(v); else hideCustomKeyboard(); } }); edittext.setOnClickListener(new OnClickListener() { // NOTE By setting the on click listener, we can show the custom // keyboard again, by tapping on an edit box that already had focus // (but that had the keyboard hidden). @Override public void onClick(View v) { showCustomKeyboard(v); } }); // Disable standard keyboard hard way // NOTE There is also an easy way: // 'edittext.setInputType(InputType.TYPE_NULL)' (but you will not have a // cursor, and no 'edittext.setCursorVisible(true)' doesn't work ) edittext.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { EditText edittext = (EditText) v; int inType = edittext.getInputType(); // Backup the input type edittext.setInputType(InputType.TYPE_NULL); // Disable standard // keyboard edittext.onTouchEvent(event); // Call native handler edittext.setInputType(inType); // Restore input type return true; // Consume touch event } }); // Disable spell check (hex strings look like words to Android) edittext.setInputType(edittext.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); } } // NOTE How can we change the background color of some keys (like the // shift/ctrl/alt)? // NOTE What does android:keyEdgeFlags do/mean