【Android Studio】自作キーボードの作成2【日本語】

Input Method Editorについて理解する必要が出てきたので、練習がてらAndroidStudioで自作キーボードを作成します。

前回(こちら)でキーボードの作成は終わっているので、今回はその内容を自分なりに紐解いていきます。

前提として、前回のキーボードの作成が終わってることとして、記載します。

 

尚、自作キーボードの作成はこちらのサイトの写経です(ありがとうございます!)

 

他のプログラミングに関する記事はこちら

スポンサーリンク


 

【AndroidManifest.xml】

まず初めにAndroidManifext.xmlの設定についてみていきます。

applicationタグのオプション(allowBackupとかiconとか)は、キーボード作成に特に関係ないので無視します。

そうなると、残るはserviceタグの内容についてです。

<service
            android:name=".NewKeyboard"
            android:label="@string/keyboard_name"
            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>

そもそもserviceタグとは一体何なのか。

公式には以下の通り掲載されていました。

サービス(Service サブクラス)をアプリのコンポーネントの 1 つとして宣言します。
アクティビティと異なり、サービスには視覚的ユーザー インターフェースがありません。
サービスは、長時間にわたるバックグラウンド オペレーションか、他のアプリから呼び出せるリッチ コミュニケーション API を実装するために使用されます。

すべてのサービスは、マニフェスト ファイルの <service> 要素で表現する必要があります。
宣言しなければ、システムによって認識されず、実行されません。

要はそれ単体で動くアプリではなく、何か補助的な役割を行うアプリの開発時に宣言するという感じでしょうかね(多分)

nameで最初に呼び出すクラス名。

labelでユーザーに表示するサービス名。

permissionでこのサービスを利用するのに必要な他の機能の宣言(自作キーボードの場合はインプットメソッド)をしています。

 

次にintent-filterタグです。

そもそもこの「インテント」とは、どのアクティビティを起動させるかの指定(明示的インテント)と、インテントフィルターに設定した情報からアクティビティを起動させる暗黙的インテントがあるようです。

それで、この「intent-filter」タグで、暗黙的インテントの情報としてInputMethodが呼び出されるように設定しているのだと思います。

 

meta-dataタグの内容は、InputMethodを使用する場合には以下の通りで書くお決まり文句っぽい。

<meta-data
                android:name="android.view.im"
                android:resource="@xml/method" />

 

【NewKeyboardクラス】

次に、AndroidManifext.xmlで指定した最初に呼び出されるNewKyeboardクラスの内容を見ていきます。

package com.sample.customkeyboard;

import android.os.Bundle;
import android.util.Log;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;

public class NewKeyboard extends InputMethodService implements KeyboardView.OnKeyboardActionListener {

    private KeyboardView keyboardView;
    private Keyboard keyboard;

    //初回だけ呼ばれる
    @Override
    public void onCreate() {
        super.onCreate();
    }

    //初回だけ呼ばれる
    @Override
    public View onCreateInputView() {
        super.onCreateInputView();

        keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view, null);
        keyboard = new Keyboard(this, R.xml.keyboard);
        keyboardView.setKeyboard(keyboard);
        keyboardView.setOnKeyboardActionListener(this);
        keyboardView.setPreviewEnabled(false);
        return keyboardView;
    }

    //キーボードが表示されるたびに呼ばれるメソッド
    @Override
    public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
        //なんらかの処理
    }

    //キーボードが閉じる時に呼ばれるメソッド
    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    //キーを押した時
    @Override
    public void onKey(int primaryCode, int[] keyCodes) {
        InputConnection ic = getCurrentInputConnection();
        switch (primaryCode) {
            case KeyEvent.KEYCODE_1:
                ic.commitText("1", 1);
                break;
            case KeyEvent.KEYCODE_2:
                ic.commitText("2", 1);
                break;
            case KeyEvent.KEYCODE_3:
                ic.commitText("3", 1);
                break;
            case KeyEvent.KEYCODE_4:
                ic.commitText("4", 1);
                break;
            case KeyEvent.KEYCODE_5:
                ic.commitText("5", 1);
                break;
            case KeyEvent.KEYCODE_6:
                ic.commitText("6", 1);
                break;
            case KeyEvent.KEYCODE_7:
                ic.commitText("7", 1);
                break;
            case KeyEvent.KEYCODE_8:
                ic.commitText("8", 1);
                break;
            case KeyEvent.KEYCODE_9:
                ic.commitText("9", 1);
                break;
            case KeyEvent.KEYCODE_0:
                ic.commitText("0", 1);
                break;
            case Keyboard.KEYCODE_DELETE:
                ic.deleteSurroundingText(1, 0);
                break;
            case KeyEvent.KEYCODE_ENTER:
                ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
                break;
            default:
                break;
        }
    }

    @Override
    public void onPress(int primaryCode) {
    }

    @Override
    public void onRelease(int primaryCode) {
    }

    @Override
    public void onText(CharSequence text) {
    }

    @Override
    public void swipeLeft() {
    }

    @Override
    public void swipeRight() {
    }

    @Override
    public void swipeDown() {
    }

    @Override
    public void swipeUp() {
    }

}

 

色々なメソッドが宣言されてますね、onCreate()とか、onCreateInputViewだとか。

これらのメソッド名は固定で、メソッド名によっていつ呼び出されるかが決まっています。

詳しくは公式の「IMEのライフサイクル」を見ていただければと思います。

ライフサイクル、というと難しく聞こえますけども、要はいつ呼び出されるのかってことですね。

onCreateが呼ばれて、onCreateInputViewが呼ばれて、onCreateCandidateViewsが呼ばれて、……というような一連の流れのことをライフサイクルって言うようです。

 

では次に、一番最初に呼び出されるonCreateの中身についてみてみます。

onCreate()の中身は以下の通りです。

super.onCreate();

superというのは継承元のクラス(今回はextendsしているInputMethodServiceクラス)のonCreateを呼び出しています。

これはInputMethodServiceを使う場合のお決まり文句のようです。

 

次に呼び出される(且つコード記載がある)のは、onCreateInputViewですね。

public View onCreateInputView() {
        super.onCreateInputView();

        keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view, null);
        keyboard = new Keyboard(this, R.xml.keyboard);
        keyboardView.setKeyboard(keyboard);
        keyboardView.setOnKeyboardActionListener(this);
        keyboardView.setPreviewEnabled(false);
        return keyboardView;
    }

このメソッドは「public View oncreateInputView()」とある通り、View型を戻すメソッドです。

super.onCreateInputViewで、継承元のクラスのonCreateInputViewを呼び出してますね。これも定型文と思います。

keyboardView変数には何やら色々記載がありますが、R.layout.keyboard_viewとあるので、res>layout内にある「keyboard_view」を設定しています。「keyboard_view」はキーボードの土台となる部分といいますか、キーボードからボタンを抜いた部分の情報があります。

keyboard変数には、res>xml内にある「keyboard」を設定しています。「keyboard」には、キーボードのボタンの部分の情報があります。

keyboardView.setKeyboard(keyboard)で、keyboard_view(土台)にkeyboard(ボタン)を設置しています。

keyboardView.setOnKeyboardActionListener(this);でキーが押された時の処理を発火できるようにしています(setOnKeyboardActionListenerはAPIレベル29から廃止とのこと)

keyboardView.setPreviewEnabled(false);でキーフィードバックポップアップを有効または無効にします。これは、押されたキーの拡大バージョンを示すポップアップです。デフォルトではプレビューが有効になっています。

最後に、これらを設定したkeyboardViewを返してます。

 

次に説明するのはonKeyメソッドです。

これはキーボードに入力があったときに呼ばれるメソッドです。

public void onKey(int primaryCode, int[] keyCodes) {
        InputConnection ic = getCurrentInputConnection();
        switch (primaryCode) {
            case KeyEvent.KEYCODE_1:
                ic.commitText("1", 1);
                break;
            case KeyEvent.KEYCODE_2:
                ic.commitText("2", 1);
                break;
            case KeyEvent.KEYCODE_3:
                ic.commitText("3", 1);
                break;
            case KeyEvent.KEYCODE_4:
                ic.commitText("4", 1);
                break;
            case KeyEvent.KEYCODE_5:
                ic.commitText("5", 1);
                break;
            case KeyEvent.KEYCODE_6:
                ic.commitText("6", 1);
                break;
            case KeyEvent.KEYCODE_7:
                ic.commitText("7", 1);
                break;
            case KeyEvent.KEYCODE_8:
                ic.commitText("8", 1);
                break;
            case KeyEvent.KEYCODE_9:
                ic.commitText("9", 1);
                break;
            case KeyEvent.KEYCODE_0:
                ic.commitText("0", 1);
                break;
            case Keyboard.KEYCODE_DELETE:
                ic.deleteSurroundingText(1, 0);
                break;
            case KeyEvent.KEYCODE_ENTER:
                ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
                break;
            default:
                break;
        }
    }

最初にInputConnection型のic変数を宣言して、内容にgetCurrentInputConnectionを設定してます。

getCurrentInputConnectionは、現在アクティブなインプットコネクション(カーソルが当たってる場所)を取得してます。

次はprimaryCodeの値でswitch文を行ってます。

primaryCodeは押されたキーによって自動で取得される変数で、res>xml内のkeyboard.xmlで設定した「codes」が入ります(例えば、1を押されたら「8」が返ってきます。

それぞれの条件に記載されているKeyEvent.KEYCODE_~~~というのは、元々Androidのキー毎に決められている固定値にようなものです。例えば、0キーが押された時は7です(こちらをご参考ください)

最後に、押されたキーに応じてcommitTextを行います。

commitTextの第1引数は入力する文字で、第2引数は入力後のカーソルの位置を指定しています。

第2引数が1だと、常にカーソルは最後に移ります。

 

 

他、Keyboard.xmlやkeyboard_view.xml等については、そこまで難しい記述はないため省略します。

キーボードの見た目やキーが持つコードをKeyboard.xmlやkeyboard_view.xmlで指定してあげて、それぞれのコードに応じた処理をAndroidManifest.xmlで指定したクラスのonKeyメソッドの部分で処理を分岐させてあげれば、自作キーボードは出来るのかな、とおもいます。

 

他のプログラミングに関する記事はこちら

スポンサーリンク