How can you make a custom keyboard in Android?

前端 未结 9 2125
隐瞒了意图╮
隐瞒了意图╮ 2020-11-22 04:42

I want to make a custom keyboard. I don\'t know how to do it using XML and Java. The following picture is a model of the keyboard I want to make. It only needs numbers.

9条回答
  •  情深已故
    2020-11-22 05:26

    In-App Keyboard

    This answer tells how to make a custom keyboard to use exclusively within your app. If you want to make a system keyboard that can be used in any app, then see my other answer.

    The example will look like this. You can modify it for any keyboard layout.

    1. Start a new Android project

    I named my project InAppKeyboard. Call yours whatever you want.

    2. Add the layout files

    Keyboard layout

    Add a layout file to res/layout folder. I called mine keyboard. The keyboard will be a custom compound view that we will inflate from this xml layout file. You can use whatever layout you like to arrange the keys, but I am using a LinearLayout. Note the merge tags.

    res/layout/keyboard.xml

    
    
        
    
            
    
                

    Activity layout

    For demonstration purposes our activity has a single EditText and the keyboard is at the bottom. I called my custom keyboard view MyKeyboard. (We will add this code soon so ignore the error for now.) The benefit of putting all of our keyboard code into a single view is that it makes it easy to reuse in another activity or app.

    res/layout/activity_main.xml

    
    
    
        
    
        
    
    
    

    3. Add the Keyboard Java file

    Add a new Java file. I called mine MyKeyboard.

    The most important thing to note here is that there is no hard link to any EditText or Activity. This makes it easy to plug it into any app or activity that needs it. This custom keyboard view also uses an InputConnection, which mimics the way a system keyboard communicates with an EditText. This is how we avoid the hard links.

    MyKeyboard is a compound view that inflates the view layout we defined above.

    MyKeyboard.java

    public class MyKeyboard extends LinearLayout implements View.OnClickListener {
    
        // constructors
        public MyKeyboard(Context context) {
            this(context, null, 0);
        }
    
        public MyKeyboard(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MyKeyboard(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context, attrs);
        }
    
        // keyboard keys (buttons)
        private Button mButton1;
        private Button mButton2;
        private Button mButton3;
        private Button mButton4;
        private Button mButton5;
        private Button mButton6;
        private Button mButton7;
        private Button mButton8;
        private Button mButton9;
        private Button mButton0;
        private Button mButtonDelete;
        private Button mButtonEnter;
    
        // This will map the button resource id to the String value that we want to 
        // input when that button is clicked.
        SparseArray keyValues = new SparseArray<>();
    
        // Our communication link to the EditText
        InputConnection inputConnection;
    
        private void init(Context context, AttributeSet attrs) {
    
            // initialize buttons
            LayoutInflater.from(context).inflate(R.layout.keyboard, this, true);
            mButton1 = (Button) findViewById(R.id.button_1);
            mButton2 = (Button) findViewById(R.id.button_2);
            mButton3 = (Button) findViewById(R.id.button_3);
            mButton4 = (Button) findViewById(R.id.button_4);
            mButton5 = (Button) findViewById(R.id.button_5);
            mButton6 = (Button) findViewById(R.id.button_6);
            mButton7 = (Button) findViewById(R.id.button_7);
            mButton8 = (Button) findViewById(R.id.button_8);
            mButton9 = (Button) findViewById(R.id.button_9);
            mButton0 = (Button) findViewById(R.id.button_0);
            mButtonDelete = (Button) findViewById(R.id.button_delete);
            mButtonEnter = (Button) findViewById(R.id.button_enter);
    
            // set button click listeners
            mButton1.setOnClickListener(this);
            mButton2.setOnClickListener(this);
            mButton3.setOnClickListener(this);
            mButton4.setOnClickListener(this);
            mButton5.setOnClickListener(this);
            mButton6.setOnClickListener(this);
            mButton7.setOnClickListener(this);
            mButton8.setOnClickListener(this);
            mButton9.setOnClickListener(this);
            mButton0.setOnClickListener(this);
            mButtonDelete.setOnClickListener(this);
            mButtonEnter.setOnClickListener(this);
    
            // map buttons IDs to input strings
            keyValues.put(R.id.button_1, "1");
            keyValues.put(R.id.button_2, "2");
            keyValues.put(R.id.button_3, "3");
            keyValues.put(R.id.button_4, "4");
            keyValues.put(R.id.button_5, "5");
            keyValues.put(R.id.button_6, "6");
            keyValues.put(R.id.button_7, "7");
            keyValues.put(R.id.button_8, "8");
            keyValues.put(R.id.button_9, "9");
            keyValues.put(R.id.button_0, "0");
            keyValues.put(R.id.button_enter, "\n");
        }
    
        @Override
        public void onClick(View v) {
    
            // do nothing if the InputConnection has not been set yet
            if (inputConnection == null) return;
    
            // Delete text or input key value
            // All communication goes through the InputConnection
            if (v.getId() == R.id.button_delete) {
                CharSequence selectedText = inputConnection.getSelectedText(0);
                if (TextUtils.isEmpty(selectedText)) {
                    // no selection, so delete previous character
                    inputConnection.deleteSurroundingText(1, 0);
                } else {
                    // delete the selection
                    inputConnection.commitText("", 1);
                }
            } else {
                String value = keyValues.get(v.getId());
                inputConnection.commitText(value, 1);
            }
        }
    
        // The activity (or some parent or controller) must give us 
        // a reference to the current EditText's InputConnection
        public void setInputConnection(InputConnection ic) {
            this.inputConnection = ic;
        }
    }
    

    4. Point the keyboard to the EditText

    For system keyboards, Android uses an InputMethodManager to point the keyboard to the focused EditText. In this example, the activity will take its place by providing the link from the EditText to our custom keyboard to.

    Since we aren't using the system keyboard, we need to disable it to keep it from popping up when we touch the EditText. Second, we need to get the InputConnection from the EditText and give it to our keyboard.

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            EditText editText = (EditText) findViewById(R.id.editText);
            MyKeyboard keyboard = (MyKeyboard) findViewById(R.id.keyboard);
    
            // prevent system keyboard from appearing when EditText is tapped
            editText.setRawInputType(InputType.TYPE_CLASS_TEXT);
            editText.setTextIsSelectable(true);
    
            // pass the InputConnection from the EditText to the keyboard
            InputConnection ic = editText.onCreateInputConnection(new EditorInfo());
            keyboard.setInputConnection(ic);
        }
    }
    

    If your Activity has multiple EditTexts, then you will need to write code to pass the right EditText's InputConnection to the keyboard. (You can do this by adding an OnFocusChangeListener and OnClickListener to the EditTexts. See this article for a discussion of that.) You may also want to hide or show your keyboard at appropriate times.

    Finished

    That's it. You should be able to run the example app now and input or delete text as desired. Your next step is to modify everything to fit your own needs. For example, in some of my keyboards I've used TextViews rather than Buttons because it is easier to customize them.

    Notes

    • In the xml layout file, you could also use a TextView rather a Button if you want to make the keys look better. Then just make the background be a drawable that changes the appearance state when pressed.
    • Advanced custom keyboards: For more flexibility in keyboard appearance and keyboard switching, I am now making custom key views that subclass View and custom keyboards that subclass ViewGroup. The keyboard lays out all the keys programmatically. The keys use an interface to communicate with the keyboard (similar to how fragments communicate with an activity). This is not necessary if you only need a single keyboard layout since the xml layout works fine for that. But if you want to see an example of what I have been working on, check out all the Key* and Keyboard* classes here. Note that I also use a container view there whose function it is to swap keyboards in and out.

提交回复
热议问题