¿Cómo desarrollar un teclado suave para Android?

Me gustaría jugar con algunas ideas y desarrollar un teclado suave para Android para reemplazar el predeterminado.

Algunos consejos:

  • Lee este tutorial: Creando un Método de Entrada
  • clonar este repository: LatinIME

Sobre tus preguntas:

Un inputMethod es básicamente un Service Android, entonces sí, puedes hacer HTTP y todo lo que puedes hacer en un Service .

Puede abrir Activities y cuadros de diálogo desde InputMethod . Una vez más, es solo un Service .

He estado desarrollando un IME, así que pregunta de nuevo si te encuentras con un problema.

Crear un tablero de teclas personalizado para Own EditText

Descargar todo el código

En esta publicación creé un teclado simple que contiene algunas teclas especiales como (teclas de Francia) y es compatible con letras mayúsculas y minúsculas y teclas numéricas y algunos símbolos.

 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); } } 

Un buen lugar para comenzar es la aplicación de muestra proporcionada en los documentos del desarrollador .

  • Las pautas serían simplemente hacer que sea lo más utilizable posible. Eche un vistazo a los otros disponibles en el mercado para ver a qué debería aspirar
  • Sí, los servicios pueden hacer la mayoría de las cosas, incluido Internet; siempre que haya solicitado esos permisos
  • Puedes abrir actividades y hacer lo que quieras, si tienes problemas para hacer algunas cosas en el teclado. Por ejemplo, el teclado de HTC tiene un botón para abrir la actividad de configuración y otro para abrir un diálogo para cambiar los idiomas.

Eche un vistazo a otros IME para ver a qué debe aspirar. Algunos (como el oficial) son de código abierto.

en primer lugar, debe definir un archivo .xml y hacer una IU de teclado en él:

 < ?xml version="1.0" encoding="utf-8"?>                       

En este ejemplo, tiene 4 filas y en cada fila tiene 3 claves. también puedes poner un ícono en cada tecla que desees.

Luego debe agregar la etiqueta xml en su UI de actividad de esta manera:

  

También en su archivo de actividad .java debe definir el teclado y asignarlo a EditText:

 CustomKeyboard mCustomKeyboard1 = new CustomKeyboard(this, R.id.keyboardview1, R.xml.horizontal_keyboard); mCustomKeyboard1.registerEditText(R.id.inputSearch); 

Este código asigna inputSearch (que es un EditText) a su teclado.

 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 '' 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 * viewid) of the host activity, and load the keyboard * layout from xml file layoutid (see {@link Keyboard} for * description). Note that the host activity must have a * KeyboardView 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 EditText with resource id resid (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