輸入法修改記錄

android studio 運行LatinIME工程

網(wǎng)上找了篇 http://www.reibang.com/p/5278addbf99c

  1. 從android源碼里copy下這個app需要的部分代碼
    路徑:源碼目錄/frameworks/opt/inputmethodcommon/java/com/android/inputmethodcommon
    copy下上邊opt目錄下的inputmethodcommon文件夾,復(fù)制到LatinIME目錄下即可

  2. LatinIME目錄下新建build.gradle文件,如下

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.1"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28

    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName = "1.0"
        
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
        }
    }
    sourceSets {
        main {
            manifest.srcFile 'java/AndroidManifest.xml'
            java.srcDirs = ['java/src', 'java-overridable/src', 'common/src', 'inputmethodcommon/java']
            res.srcDirs = ['java/res']
        }
    }
    externalNativeBuild {
        cmake {
            path 'native/CMakeLists.txt'
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    lintOptions {
        htmlReport false
        abortOnError false
        disable 'MissingTranslation'
        disable 'ExtraTranslation'
    }
}

dependencies {
    implementation 'com.android.support:appcompat-v7:28.0.0'
    
    implementation "com.google.code.findbugs:jsr305:3.0.1"
}

gradle的版本可以改成自己本地有的颁独,就不用下載了,如果提示gradle版本不匹配啥的改基,可以自己copy下對應(yīng)的gradle文件夾到工程目錄下阳液,如下圖紅框


image.png
  1. 在源碼的native目錄下新建 CMakeLists.txt文件怕敬,加入cmake的配置

cmake_minimum_required(VERSION 3.4.1)

# 二進制碼剝除
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")

add_library(jni_latinime SHARED
            jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
            jni/com_android_inputmethod_latin_BinaryDictionary.cpp
            jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp
            jni/com_android_inputmethod_latin_DicTraverseSession.cpp
            jni/jni_common.cpp
            jni/src/dictionary/header/header_policy.cpp
            jni/src/dictionary/header/header_read_write_utils.cpp
            jni/src/dictionary/property/ngram_context.cpp
            jni/src/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
            jni/src/dictionary/structure/backward/v402/ver4_dict_buffers.cpp
            jni/src/dictionary/structure/backward/v402/ver4_dict_constants.cpp
            jni/src/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.cpp
            jni/src/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp
            jni/src/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
            jni/src/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.cpp
            jni/src/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp
            jni/src/dictionary/structure/backward/v402/ver4_pt_node_array_reader.cpp
            jni/src/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp
            jni/src/dictionary/structure/backward/v402/content/bigram_dict_content.cpp
            jni/src/dictionary/structure/backward/v402/content/probability_dict_content.cpp
            jni/src/dictionary/structure/backward/v402/content/shortcut_dict_content.cpp
            jni/src/dictionary/structure/backward/v402/content/sparse_table_dict_content.cpp
            jni/src/dictionary/structure/backward/v402/content/terminal_position_lookup_table.cpp
            jni/src/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp
            jni/src/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp
            jni/src/dictionary/structure/pt_common/dynamic_pt_reading_utils.cpp
            jni/src/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp
            jni/src/dictionary/structure/pt_common/dynamic_pt_writing_utils.cpp
            jni/src/dictionary/structure/pt_common/patricia_trie_reading_utils.cpp
            jni/src/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp
            jni/src/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.cpp
            jni/src/dictionary/structure/v2/patricia_trie_policy.cpp
            jni/src/dictionary/structure/v2/ver2_patricia_trie_node_reader.cpp
            jni/src/dictionary/structure/v2/ver2_pt_node_array_reader.cpp
            jni/src/dictionary/structure/v4/ver4_dict_buffers.cpp
            jni/src/dictionary/structure/v4/ver4_dict_constants.cpp
            jni/src/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp
            jni/src/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
            jni/src/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
            jni/src/dictionary/structure/v4/ver4_patricia_trie_reading_utils.cpp
            jni/src/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
            jni/src/dictionary/structure/v4/ver4_pt_node_array_reader.cpp
            jni/src/dictionary/structure/v4/content/dynamic_language_model_probability_utils.cpp
            jni/src/dictionary/structure/v4/content/language_model_dict_content.cpp
            jni/src/dictionary/structure/v4/content/language_model_dict_content_global_counters.cpp
            jni/src/dictionary/structure/v4/content/shortcut_dict_content.cpp
            jni/src/dictionary/structure/v4/content/sparse_table_dict_content.cpp
            jni/src/dictionary/structure/v4/content/terminal_position_lookup_table.cpp
            jni/src/dictionary/utils/buffer_with_extendable_buffer.cpp
            jni/src/dictionary/utils/byte_array_utils.cpp
            jni/src/dictionary/utils/dict_file_writing_utils.cpp
            jni/src/dictionary/utils/file_utils.cpp
            jni/src/dictionary/utils/forgetting_curve_utils.cpp
            jni/src/dictionary/utils/format_utils.cpp
            jni/src/dictionary/utils/mmapped_buffer.cpp
            jni/src/dictionary/utils/multi_bigram_map.cpp
            jni/src/dictionary/utils/probability_utils.cpp
            jni/src/dictionary/utils/sparse_table.cpp
            jni/src/dictionary/utils/trie_map.cpp
            jni/src/suggest/core/suggest.cpp
            jni/src/suggest/core/dicnode/dic_node.cpp
            jni/src/suggest/core/dicnode/dic_node_utils.cpp
            jni/src/suggest/core/dicnode/dic_nodes_cache.cpp
            jni/src/suggest/core/dictionary/dictionary.cpp
            jni/src/suggest/core/dictionary/dictionary_utils.cpp
            jni/src/suggest/core/dictionary/digraph_utils.cpp
            jni/src/suggest/core/dictionary/error_type_utils.cpp
            jni/src/suggest/core/layout/additional_proximity_chars.cpp
            jni/src/suggest/core/layout/proximity_info.cpp
            jni/src/suggest/core/layout/proximity_info_params.cpp
            jni/src/suggest/core/layout/proximity_info_state.cpp
            jni/src/suggest/core/layout/proximity_info_state_utils.cpp
            jni/src/suggest/core/policy/weighting.cpp
            jni/src/suggest/core/result/suggestion_results.cpp
            jni/src/suggest/core/result/suggestions_output_utils.cpp
            jni/src/suggest/core/session/dic_traverse_session.cpp
            jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
            jni/src/suggest/policyimpl/typing/scoring_params.cpp
            jni/src/suggest/policyimpl/typing/typing_scoring.cpp
            jni/src/suggest/policyimpl/typing/typing_suggest_policy.cpp
            jni/src/suggest/policyimpl/typing/typing_traversal.cpp
            jni/src/suggest/policyimpl/typing/typing_weighting.cpp
            jni/src/utils/autocorrection_threshold_utils.cpp
            jni/src/utils/char_utils.cpp
            jni/src/utils/jni_data_utils.cpp
            jni/src/utils/log_utils.cpp
            jni/src/utils/time_keeper.cpp)

include_directories(jni/src/)

target_link_libraries(jni_latinime
                      android
                      log
                      z)

ok,這時候工程應(yīng)該可以正常運行了帘皿。對了东跪,就像帖子里說的,你的studio需要下載配置好ndk
補充說明
上邊改完debug模式?jīng)]啥問題了鹰溜,release build的時候會出問題
清單文件里會提示下邊的有問題虽填,因為gradle配置里已經(jīng)設(shè)置了最小版本,目標(biāo)版本了

<!--    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />-->

簽名完apk無法安裝曹动,網(wǎng)上搜到的解決辦法是在application標(biāo)簽下添加

android:extractNativeLibs="true"

這些可能都是gradle編譯的時候會出現(xiàn)的問題滔悉,如果你是放在系統(tǒng)源碼里一起編譯到image里的书在,估計不用改這些了吧盖奈。因為我也沒有image埋嵌,只有別人從 image里提取出來的這個LatinIME工程。贡必。
我這里改的是8.0的輸入法.

ui修改

目前只考慮英文的兔港,我這里測試的是平板,所以只修改了xml-sw600dp 這個文件夾下相關(guān)的資源文件仔拟,如果是給手機用的衫樊,那修改默認(rèn)的xml下的文件即可

  1. 默認(rèn)的英文字母界面 rows_kwerty.xml
    總共有4個Row也就是4行,最后一行通過include引用了另外一個通用的文件 row_kwerty4.xml
    比如想刪除一個key,添加一個key都可以科侈,不過需要注意按鍵的總寬度不能大于100载佳,否則有的按鈕就不顯示了

我這里是把刪除按鍵和action按鍵放到最后兩行了


默認(rèn)界面左下角點擊那個123的按鍵,會跳到 數(shù)字符號界面

  1. 數(shù)字符號界面 rows_symbols.xml

在這個界面點擊第三行第一個按鈕臀栈,切換到更多符號界面

  1. 對應(yīng)的就是rows_symbols_shift.xml
    如下最后兩行的代碼刚盈,
        <Key
            latin:keySpec="&#x00BF;" />
        <Key //替換原來和左側(cè)一樣的鍵為刪除鍵
            latin:keyStyle="deleteKeyStyle"
            latin:keyWidth="fillRight" />
    </Row>
    <Row
        latin:keyWidth="9.0%p"
        latin:backgroundType="functional"
    >
        <Key
            latin:keyStyle="toAlphaKeyStyle"
            latin:keyWidth="10%p" />
        <include
            latin:keyboardLayout="@xml/row_symbols_shift4" />
<!--        <include-->
<!--            latin:keyboardLayout="@xml/key_emoji" />-->
        <Key  //新加的
            latin:keyStyle="enterKeyStyle"
            latin:keyWidth="fillRight" />
    </Row>

布局修改

這里只測試英文的,rows_qwerty.xml
table上測試挂脑,用的是default屬性
修改keyWidth為10%p ,這里的百分比是除去key之間的間隔的,10個key平分欲侮,所以就是10%

    <Row>
        <switch>
            <!-- Split keyboard layout for the first row -->
            <case
                latin:isSplitLayout="true"
            >
//...
            <default>
                <include
                    latin:keyboardLayout="@xml/rowkeys_qwerty1"
                    latin:keyWidth="10%p" />
<!--                <Key-->
<!--                    latin:keyStyle="deleteKeyStyle"-->
<!--                    latin:keyWidth="fillRight" />-->
            </default>
        </switch>
    </Row>

第二行,這個和我想的效果差距太大崭闲,我這百分比是加上間隔算的,現(xiàn)在發(fā)現(xiàn)不對

                <include
                    latin:keyboardLayout="@xml/rowkeys_qwerty2"
                    latin:keyXPos="6.275%p"
                    latin:keyWidth="8.57%p" />
image.png

測試結(jié)果發(fā)現(xiàn)這里的百分比是除去間隔的威蕉,所以上邊第二行修改如下
9個字母占90%刁俭,每個10%,剩下兩邊各5%韧涨,也就是默認(rèn)起始x偏移5%牍戚,效果如下

                <include
                    latin:keyboardLayout="@xml/rowkeys_qwerty2"
                    latin:keyXPos="5%p"
                    latin:keyWidth="10%p" />
image.png

rows_qwerty.xml 修改字母界面沒啥問題,
修改數(shù)字界面虑粥,應(yīng)該是rows_symbols.xml結(jié)果發(fā)現(xiàn)改完沒變化如孝,那就是改錯地方了。

我測試的是橫屏板子輸入法娩贷,所以最先找的是 xml-sw600dp-land 沒找到第晰,我就去xml下去找了,
現(xiàn)在想起來還有個xml-sw600dp文件夾彬祖,land下沒有應(yīng)該先找這個的茁瘦。這次修改完就生效了,如下


image.png

數(shù)字界面切換符號界面
rows_symboles_shift.xml
簡單修改完成储笑,目前只是把key的位置修改完了甜熔。


image.png

鍵盤高度修改

我們的需求是鍵盤高度增大了,所以根據(jù)下邊的邏輯突倍,我就修改了下最小高度腔稀,最后使用的就是這個值了。
在config.xml 下看到這個名字,需要注意下羽历,你要適配的大小烧颖,找對應(yīng)的,這個文件有很多個窄陡,找對應(yīng)的修改

    <dimen name="config_default_keyboard_height">365.4dp</dimen>
    <fraction name="config_min_keyboard_height">55%p</fraction>

搜下哪里用到了上邊的配置炕淮,ResourceUtils.java

    public static int getDefaultKeyboardHeight(final Resources res) {
        final DisplayMetrics dm = res.getDisplayMetrics();
        final String keyboardHeightInDp = getDeviceOverrideValue(
                res, R.array.keyboard_heights, null /* defaultValue */);
        final float keyboardHeight;
        if (TextUtils.isEmpty(keyboardHeightInDp)) {//目前數(shù)組是空的,所以會走這里了
            keyboardHeight = res.getDimension(R.dimen.config_default_keyboard_height);
        } else {
            keyboardHeight = Float.parseFloat(keyboardHeightInDp) * dm.density;
        }
        final float maxKeyboardHeight = res.getFraction(//最大高度
                R.fraction.config_max_keyboard_height, dm.heightPixels, dm.heightPixels);
        float minKeyboardHeight = res.getFraction(//最小高度
                R.fraction.config_min_keyboard_height, dm.heightPixels, dm.heightPixels);
        if (minKeyboardHeight < 0.0f) {//如果是負(fù)的跳夭,弄成正的涂圆,而且負(fù)的標(biāo)志百分比是按照寬來算的
            // Specified fraction was negative, so it should be calculated against display
            // width.
            minKeyboardHeight = -res.getFraction(
                    R.fraction.config_min_keyboard_height, dm.widthPixels, dm.widthPixels);
        }
        // Keyboard height will not exceed maxKeyboardHeight and will not be less than
        // minKeyboardHeight. 首先固定高度和最大高度取最小值们镜,然后再和最小高度比較取最大值,所以我們修改下最小高度就行了.
        return (int)Math.max(Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
    }

具體這個高度的百分比弄多少润歉,看需求了模狭,我們的需求是按鍵key的寬高是一樣的,所以這里需要根據(jù)屏幕的寬高計算下踩衩。
方法如下:

因為我們要求普通按鍵的寬高是一樣的嚼鹉,所以鍵盤的高度需要計算出來。
首先需要定義好按鍵之間的間隔驱富,因為用到的都是百分比锚赤。而且p指的是鍵盤的寬高,不是屏幕的寬高褐鸥,因為水平方向一般是鋪滿屏幕的线脚,所以寬和屏幕寬一樣,但屏幕高和鍵盤高差距很大叫榕,別理解錯了浑侥。
以下邊設(shè)置為例
    <fraction name="config_keyboard_top_padding_holo">2.0%p</fraction>
    <fraction name="config_keyboard_bottom_padding_holo">2.0%p</fraction>
    <fraction name="config_key_vertical_gap_holo">2.0%p</fraction>
    <fraction name="config_key_horizontal_gap_holo">1%p</fraction>

水平方向的間隔總共是10%p,那么key的寬度就是 90%p除10 ,每個key的寬度就是 9%p 也就是====== 寬度*9%

垂直方向晰绎,間隔總共是10%p【這里的p指的是鍵盤的高度寓落,不是屏幕的高度】, 假設(shè) 鍵盤高度是x%p荞下,那么
假設(shè)屏幕高度為h零如,寬為w
x%*h=10%*(x%*h)+ w*9%*4;
x=w*4/h
知道設(shè)備的寬高,那么這個鍵盤高度百分比x也就算出來了锄弱。
實際測試發(fā)現(xiàn)上邊的算法有問題考蕾,因為間隔獲取的時候直接強轉(zhuǎn)為int了,比如結(jié)果是7.88強轉(zhuǎn)以后就是7了会宪,所以上邊算出來有誤差
比如肖卧,寬度440,間隔2%p掸鹅,我們會認(rèn)為10個間隔是20%p塞帐,也就是88,實際結(jié)果不是巍沙,單個2%p是8.8取整就是8葵姥,
那么10個間隔實際是80,和上邊的公式就差了8句携,

如果沒有這要求榔幸,其實高度設(shè)置多少都行,反正key的高度最后是平分鍵盤高度的.
需要注意下,我只修改英文鍵盤削咆,所以是按照4行算的牍疏,如果是泰文,那是5行的拨齐,所以上邊只是說明了下計算的邏輯鳞陨,具體的還得看實際需求,如果不想修改配置文件瞻惋,你也可以直接在上邊的getDefaultKeyboardHeight方法里厦滤,根據(jù)寬度來算下高度

主題

我用的8.0的源碼,這個有提供4種主題歼狼,如下

image.png

修改的時候要找到對應(yīng)的主題修改掏导,別修改錯了地方,源碼里主題都在這了
image.png

看到light和dark蹂匹,那么
themes-lxx-dark 對應(yīng)的 Material Dark
themes-lxx-light 對應(yīng)的 Material Light
themes-lxx 是上邊兩個通用的部分
themes-klp 是 Holo White
themes-ics 是Holo Blue(區(qū)分很簡單,這里的顏色有藍色的)
themes-holo 是上邊兩個通用的
themes-common 是所有主題通用的部分
目前我以holo white 為默認(rèn)的做修改凹蜈,其他幾種先不管

    <style
        name="KeyboardView.KLP"
        parent="KeyboardView.Holo"
    >
        <item name="android:background">#F2F2F2</item>//鍵盤背景限寞,不包括上邊提示那部分
        <item name="keyBackground">@drawable/btn_keyboard_key_klp_test</item>//默認(rèn)按鍵的背景,就是那些字母數(shù)字
        <item name="functionalKeyBackground">@drawable/btn_keyboard_key_klp_test</item>//功能性的按鍵仰坦,xml里的Key有聲明的
        <item name="spacebarBackground">@drawable/btn_keyboard_key_klp_test</item>//空格鍵的背景
        <item name="keyTextColor">@color/key_text_color_holo</item>// key上字母的顏色
        <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_holo</item>
        <item name="functionalTextColor">@color/key_text_color_holo</item>//功能性按鍵上的文字顏色
        <item name="keyHintLetterColor">@color/key_hint_letter_color_holo</item>//第一行字母右上角那數(shù)字的顏色
        <item name="keyHintLabelColor">@color/key_hint_label_color_holo</item>//最后一行按鈕右下角三個點的顏色
        <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_holo</item>
        <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_holo</item>
        <item name="keyPreviewTextColor">@color/key_text_color_holo</item>
    </style>

修改按鍵背景

按鍵的背景系統(tǒng)默認(rèn)的有3種履植, 普通按鈕,功能性的按鍵悄晃,空格鍵
根據(jù)需求我們把這三種顏色修改成我們要的.然后還得增加一種深色的玫霎,如下圖。


image.png

默認(rèn)最底下一行除了空格都是functional背景的妈橄,如果不需要庶近,可以修改下latin:backgroundType為normal即可,我們的需求那個 , -, = 都不需要顏色眷蚓,那么改成normal就行,背景就是普通背景了.

    <Key
        latin:backgroundType="normal"
        latin:keySpec="/" />

查看attrs.xml 下定義的 latin:backgroundType="functional"

<declare-styleable name="Keyboard_Key">
        <attr name="backgroundType" format="enum">
            <!-- This should be aligned with
                 {@link com.android.inputmethod.keyboard.Key#BACKGROUND_TYPE_NORMAL} etc. -->
            <enum name="empty" value="0" />
            <enum name="normal" value="1" />
            <enum name="functional" value="2" />
            <enum name="stickyOff" value="3" />
            <enum name="stickyOn" value="4" />
            <enum name="action" value="5" />
            <enum name="spacebar" value="6" />
        </attr>

// 系統(tǒng)默認(rèn)就定義了三種background鼻种,我們在下邊加一種新的

 <declare-styleable name="KeyboardView">
        <!-- Background image for the key. This image needs to be a
            {@link android.graphics.drawable.StateListDrawable}, with the following possible states:
             normal, pressed, checkable, checkable+pressed, checkable+checked,
             checkable+checked+pressed. -->
        <attr name="keyBackground" format="reference" />
        <!-- Background image for the functional key. This image needs to be a
             {@link android.graphics.drawable.StateListDrawable}, with the following possible
             states: normal, pressed. -->
        <attr name="functionalKeyBackground" format="reference" />
        <!-- Background image for the spacebar.  This image needs to be a
             {@link android.graphics.drawable.StateListDrawable}, with the following possible
             states: normal, pressed. -->
        <attr name="spacebarBackground" format="reference" />

先看下背景哪里用到了,Key.java
可以看到,就分了3種情況沙热,

    public final Drawable selectBackgroundDrawable(@Nonnull final Drawable keyBackground,
            @Nonnull final Drawable functionalKeyBackground,
            @Nonnull final Drawable spacebarBackground) {
        final Drawable background;
        if (mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL) {
            background = functionalKeyBackground;
        } else if (mBackgroundType == BACKGROUND_TYPE_SPACEBAR) {
            background = spacebarBackground;
        } else {
            background = keyBackground;
        }
        final int[] state = KeyBackgroundState.STATES[mBackgroundType].getState(mPressed);
        background.setState(state);
        return background;
    }

圖片是在KeyboardView.java 里加載的叉钥,所以需要去主題里設(shè)置

    public KeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);

        final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
                R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
        mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground);
        mKeyBackground.getPadding(mKeyBackgroundPadding);
        final Drawable functionalKeyBackground = keyboardViewAttr.getDrawable(
                R.styleable.KeyboardView_functionalKeyBackground);
        mFunctionalKeyBackground = (functionalKeyBackground != null) ? functionalKeyBackground
                : mKeyBackground;
        final Drawable spacebarBackground = keyboardViewAttr.getDrawable(
                R.styleable.KeyboardView_spacebarBackground);
        mSpacebarBackground = (spacebarBackground != null) ? spacebarBackground : mKeyBackground;

現(xiàn)在假設(shè)增加了一種attrs.xml下添加

<declare-styleable name="KeyboardView">
<attr name="functionalKeyBackground2" format="reference" /> //for dark blue action key

theme里添加
如果四個主題你都要改的話,那就都加篙贸,或者加載默認(rèn)的主題下邊投队,我這里就只處理一種主題

        <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_klp</item>
        <item name="functionalKeyBackground2">@drawable/btn_keyboard_key_functional_klp2</item>

KeyboardView里獲取圖片

        // newly added
        final Drawable functionalKeyBackground2 = keyboardViewAttr.getDrawable(
                R.styleable.KeyboardView_functionalKeyBackground2);
        mFunctionalKeyBackground2 = (functionalKeyBackground2 != null) ? functionalKeyBackground2
                : mKeyBackground;

那么哪里用這圖片了?先給Key增加一種類型

        <attr name="backgroundType" format="enum">
            <!-- This should be aligned with
                 {@link com.android.inputmethod.keyboard.Key#BACKGROUND_TYPE_NORMAL} etc. -->
            <enum name="empty" value="0" />
            <enum name="normal" value="1" />
            <enum name="functional" value="2" />
            <enum name="stickyOff" value="3" />
            <enum name="stickyOn" value="4" />
            <enum name="action" value="5" />
            <enum name="spacebar" value="6" />
<enum name="functional2" value="7" /> //new added
        </attr>

然后修改下Key的latin:backgroundType屬性

                <Key
                    latin:backgroundType="functional2"
                    latin:keyStyle="enterKeyStyle"
                    latin:keyWidth="fillRight" />

好了爵川,在key.java里處理下
如下敷鸦,下個方法增加一個參數(shù),從KeyboardView里傳過來的, 多加個if條件

//
    public static final int BACKGROUND_TYPE_FUNCTIONAL2 = 7;
//
        case BACKGROUND_TYPE_SPACEBAR: return "spacebar";
        case BACKGROUND_TYPE_FUNCTIONAL2:return "functional2";

public final Drawable selectBackgroundDrawable(@Nonnull final Drawable keyBackground,
            @Nonnull final Drawable functionalKeyBackground,
            @Nonnull final Drawable spacebarBackground,@Nonnull final Drawable functionalKeyBackground2) {
        final Drawable background;
        if (mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL) {
            background = functionalKeyBackground;
        }else if(mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL2){
            background = functionalKeyBackground2;
        }else if (mBackgroundType == BACKGROUND_TYPE_SPACEBAR) {
            background = spacebarBackground;
        } else {
            background = keyBackground;
        }
        final int[] state = KeyBackgroundState.STATES[mBackgroundType].getState(mPressed);
        background.setState(state);
        return background;
    }

想著ok了,運行了下掛了轧膘,下邊這行掛了钞螟,數(shù)組越界,
java.lang.ArrayIndexOutOfBoundsException: length=7; index=7

final int[] state = KeyBackgroundState.STATES[mBackgroundType].getState(mPressed);

點進去一看谎碍,果然鳞滨,這個忘了處理了,如下蟆淀,最后位置添加一個新的

        public static final KeyBackgroundState[] STATES = {
            // 0: BACKGROUND_TYPE_EMPTY
            new KeyBackgroundState(android.R.attr.state_empty),
            // 1: BACKGROUND_TYPE_NORMAL
            new KeyBackgroundState(),
            // 2: BACKGROUND_TYPE_FUNCTIONAL
            new KeyBackgroundState(),
            // 3: BACKGROUND_TYPE_STICKY_OFF
            new KeyBackgroundState(android.R.attr.state_checkable),
            // 4: BACKGROUND_TYPE_STICKY_ON
            new KeyBackgroundState(android.R.attr.state_checkable, android.R.attr.state_checked),
            // 5: BACKGROUND_TYPE_ACTION
            new KeyBackgroundState(android.R.attr.state_active),
            // 6: BACKGROUND_TYPE_SPACEBAR
            new KeyBackgroundState(),
                // 7: BACKGROUND_TYPE_FUNCTIONAL2
                new KeyBackgroundState(),
        };

這次就ok了

刪除頂部的提示view

@layout/main_keyboard_frame
在這個布局里拯啦,我以為把提示的view設(shè)置為gone就完事了,結(jié)果沒效果熔任。然后搜了下這個控件用到的地方褒链,發(fā)現(xiàn)java代碼里有控制它的可見性

    <com.android.inputmethod.latin.suggestions.SuggestionStripView
        android:id="@+id/suggestion_strip_view"
        android:layoutDirection="ltr"
        android:layout_width="match_parent"
        android:layout_height="@dimen/config_suggestions_strip_height"
        android:visibility="gone"
        android:gravity="center_vertical"
        style="?attr/suggestionStripViewStyle" />

LatinIME.java里有

        final boolean shouldShowImportantNotice =
                ImportantNoticeUtils.shouldShowImportantNotice(this, currentSettingsValues);
        final boolean shouldShowSuggestionCandidates =
                currentSettingsValues.mInputAttributes.mShouldShowSuggestions
                && currentSettingsValues.isSuggestionsEnabledPerUserSettings();
        final boolean shouldShowSuggestionsStripUnlessPassword = shouldShowImportantNotice
                || currentSettingsValues.mShowsVoiceInputKey
                || shouldShowSuggestionCandidates
                || currentSettingsValues.isApplicationSpecifiedCompletionsOn();
        final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword
                && !currentSettingsValues.mInputAttributes.mIsPasswordField;
        mSuggestionStripView.updateVisibility(shouldShowSuggestionsStrip, isFullscreenMode());

//go on

    public void updateVisibility(final boolean shouldBeVisible, final boolean isFullscreenMode) {
        final int visibility = shouldBeVisible ? VISIBLE : (isFullscreenMode ? GONE : INVISIBLE);
        setVisibility(visibility);
        final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent();
        //mVoiceKey.setVisibility(currentSettingsValues.mShowsVoiceInputKey ? VISIBLE : INVISIBLE);
        mVoiceKey.setVisibility(View.GONE);
    }

主要就是setting里有個設(shè)置是否提示【第一個參數(shù)】,以及當(dāng)前是否全屏【第二個參數(shù)】
我們不需要顯示這個布局疑苔,那么上邊代碼直接改成如下即可

mSuggestionStripView.updateVisibility(false,true);

如果需要修改顏色啥的甫匹,那去主題里修改下,應(yīng)該是以suggestion開頭的一些屬性

    <style
        name="SuggestionWord.KLP"
        parent="SuggestionWord"
    >
        <item name="android:background">@drawable/btn_suggestion_klp</item>
        <item name="android:textColor">@color/highlight_color_klp</item>
    </style>

長按有彈框的惦费,處理下彈框顏色

    <style
        name="MoreKeysKeyboardView.KLP"
        parent="KeyboardView.KLP"
    >
        <item name="android:background">#F2F2F2</item>//彈框整體背景
        <item name="keyBackground">@drawable/btn_keyboard_key_popup_klp</item>//每個key的背景
        <item name="divider">@drawable/more_keys_divider</item>//
        <item name="keyTypeface">normal</item>
        <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item>
    </style>

字體style修改

默認(rèn)是bold兵迅,改下

        <item name="keyTypeface">normal</item>

折騰到現(xiàn)在長這樣,現(xiàn)在問題就是圖標(biāo)看起來有點小


image.png

圖標(biāo)大小如何修改

先來回顧下之前研究的薪贫,這些Key的內(nèi)容咋定義的

  1. 普通的字母
    第一種是第一行那種長按可以選擇數(shù)字的恍箭,第二種是第二行那普通的字母
    <Key
        latin:keySpec="!text/keyspec_q"
        latin:keyHintLabel="1"
        latin:additionalMoreKeys="1"
        latin:moreKeys="!text/morekeys_q" />

    <Key
        latin:keySpec="k" />
  1. 功能按鍵
    這些一般都是簡單寫個KeyStyle,具體的屬性在 <include latin:keyboardLayout="@xml/key_styles_common" />
                <Key
                    latin:keyStyle="deleteKeyStyle"
                    latin:keyWidth="fillRight" />
//key_styles_common 里找到styleName一樣的
    <key-style
        latin:styleName="deleteKeyStyle"
        latin:keySpec="!icon/delete_key|!code/key_delete"
        latin:keyActionFlags="isRepeatable|noKeyPreview"
        latin:backgroundType="functional" />

有的還帶有parent屬性的

            <key-style
                latin:styleName="shiftKeyStyle"
                latin:keySpec="!icon/shift_key|!code/key_shift"
                latin:backgroundType="stickyOff"
                latin:parentStyle="baseForShiftKeyStyle" />

    <key-style
        latin:styleName="baseForShiftKeyStyle"
        latin:keyActionFlags="noKeyPreview"
        latin:keyLabelFlags="preserveCase"
        latin:moreKeys="!noPanelAutoMoreKey!, |!code/key_capslock" />

其實可以看到key的內(nèi)容是定義在keySpec里的
目前有以下幾種

!icon/shift_key|!code/key_shift
!text/keyspec_q

去Key.java里看下咋解析這些值的

 final String keySpec = keyStyle.getString(keyAttr, R.styleable.Keyboard_Key_keySpec);

 public Key(@Nullable final String keySpec, @Nonnull final TypedArray keyAttr,
            @Nonnull final KeyStyle style, @Nonnull final KeyboardParams params,
            @Nonnull final KeyboardRow row) {

mIconId = KeySpecParser.getIconId(keySpec);

final int code = KeySpecParser.getCode(keySpec);
 final String label = KeySpecParser.getLabel(keySpec);
String outputText = KeySpecParser.getOutputText(keySpec);

看下這些方法瞧省,可以得出一些結(jié)論
1.1. keySpec可以有多個扯夭,以豎杠分開
1.2. 如果有icon的話,icon必須放在首位
1.3. 文字圖片都是根據(jù)前綴來區(qū)分的
1.4. 解析出對應(yīng)的名字鞍匾,比如shift_key交洗,這個是icon,那么會去KeyboardIconsSet這個類里找橡淑,這里有初始化所有的icon
1.5 定義了icon的話藕筋,label就無效了

    public static final String PREFIX_ICON = "!icon/";
    private static final char BACKSLASH = Constants.CODE_BACKSLASH; // 反斜杠 \ 
    private static final char VERTICAL_BAR = Constants.CODE_VERTICAL_BAR; //豎杠 |
    private static final String PREFIX_HEX = "0x";


   private static boolean hasIcon(@Nonnull final String keySpec) {
        return keySpec.startsWith(KeyboardIconsSet.PREFIX_ICON);
    }

    public static int getIconId(@Nullable final String keySpec) {
        if (keySpec == null) {
            // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
            return KeyboardIconsSet.ICON_UNDEFINED;
        }
        if (!hasIcon(keySpec)) {
            return KeyboardIconsSet.ICON_UNDEFINED;
        }
        final int labelEnd = indexOfLabelEnd(keySpec);
        final String iconName = getBeforeLabelEnd(keySpec, labelEnd)
                .substring(KeyboardIconsSet.PREFIX_ICON.length());
//iconName取的就是 
        return KeyboardIconsSet.getIconId(iconName);
    }

//code

        if (text.startsWith(KeyboardCodesSet.PREFIX_CODE)) {
            return KeyboardCodesSet.getCode(text.substring(KeyboardCodesSet.PREFIX_CODE.length()));
        }
// 有icon的話,lable直接返回null了
    public static String getLabel(@Nullable final String keySpec) {
        if (keySpec == null) {
            // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
            return null;
        }
        if (hasIcon(keySpec)) {
            return null;
        }
//有code的話 text就返回null了

    public static String getOutputText(@Nullable final String keySpec) {
        if (keySpec == null) {
            // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
            return null;
        }
        final int labelEnd = indexOfLabelEnd(keySpec);
        if (hasCode(keySpec, labelEnd)) {
            return null;
        }

看下已定義的icon有哪些 KeyboardIconsSet.java
看名字大概就知道是哪些按鍵了梳码,每個key都對應(yīng)一個style的隐圾,我們?nèi)ゲ橄聅tyle

    public static final String PREFIX_ICON = "!icon/";
    private static final String NAME_UNDEFINED = "undefined";
    public static final String NAME_SHIFT_KEY = "shift_key";//
    public static final String NAME_SHIFT_KEY_SHIFTED = "shift_key_shifted";//
    public static final String NAME_DELETE_KEY = "delete_key";//
    public static final String NAME_SETTINGS_KEY = "settings_key";
    public static final String NAME_SPACE_KEY = "space_key";
    public static final String NAME_SPACE_KEY_FOR_NUMBER_LAYOUT = "space_key_for_number_layout";
    public static final String NAME_ENTER_KEY = "enter_key";
    public static final String NAME_GO_KEY = "go_key";
    public static final String NAME_SEARCH_KEY = "search_key";
    public static final String NAME_SEND_KEY = "send_key";
    public static final String NAME_NEXT_KEY = "next_key";
    public static final String NAME_DONE_KEY = "done_key";
    public static final String NAME_PREVIOUS_KEY = "previous_key";
    public static final String NAME_TAB_KEY = "tab_key";
    public static final String NAME_SHORTCUT_KEY = "shortcut_key";
    public static final String NAME_SHORTCUT_KEY_DISABLED = "shortcut_key_disabled";
    public static final String NAME_LANGUAGE_SWITCH_KEY = "language_switch_key";
    public static final String NAME_ZWNJ_KEY = "zwnj_key";
    public static final String NAME_ZWJ_KEY = "zwj_key";
    public static final String NAME_EMOJI_ACTION_KEY = "emoji_action_key";
    public static final String NAME_EMOJI_NORMAL_KEY = "emoji_normal_key";

    private static final Object[] NAMES_AND_ATTR_IDS = {
        NAME_UNDEFINED,                   ATTR_UNDEFINED,
        NAME_SHIFT_KEY,                   R.styleable.Keyboard_iconShiftKey,
        NAME_DELETE_KEY,                  R.styleable.Keyboard_iconDeleteKey,
        NAME_SETTINGS_KEY,                R.styleable.Keyboard_iconSettingsKey,
        NAME_SPACE_KEY,                   R.styleable.Keyboard_iconSpaceKey,
        NAME_ENTER_KEY,                   R.styleable.Keyboard_iconEnterKey,
        NAME_GO_KEY,                      R.styleable.Keyboard_iconGoKey,
        NAME_SEARCH_KEY,                  R.styleable.Keyboard_iconSearchKey,
        NAME_SEND_KEY,                    R.styleable.Keyboard_iconSendKey,
        NAME_NEXT_KEY,                    R.styleable.Keyboard_iconNextKey,
        NAME_DONE_KEY,                    R.styleable.Keyboard_iconDoneKey,
        NAME_PREVIOUS_KEY,                R.styleable.Keyboard_iconPreviousKey,
        NAME_TAB_KEY,                     R.styleable.Keyboard_iconTabKey,
        NAME_SHORTCUT_KEY,                R.styleable.Keyboard_iconShortcutKey,
        NAME_SPACE_KEY_FOR_NUMBER_LAYOUT, R.styleable.Keyboard_iconSpaceKeyForNumberLayout,
        NAME_SHIFT_KEY_SHIFTED,           R.styleable.Keyboard_iconShiftKeyShifted,
        NAME_SHORTCUT_KEY_DISABLED,       R.styleable.Keyboard_iconShortcutKeyDisabled,
        NAME_LANGUAGE_SWITCH_KEY,         R.styleable.Keyboard_iconLanguageSwitchKey,
        NAME_ZWNJ_KEY,                    R.styleable.Keyboard_iconZwnjKey,
        NAME_ZWJ_KEY,                     R.styleable.Keyboard_iconZwjKey,
        NAME_EMOJI_ACTION_KEY,            R.styleable.Keyboard_iconEmojiActionKey,
        NAME_EMOJI_NORMAL_KEY,            R.styleable.Keyboard_iconEmojiNormalKey,
    };

//加載主題里的圖片
    public void loadIcons(final TypedArray keyboardAttrs) {
        final int size = ATTR_ID_TO_ICON_ID.size();
        for (int index = 0; index < size; index++) {
            final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index);
            try {
                final Drawable icon = keyboardAttrs.getDrawable(attrId);
                setDefaultBounds(icon);
                final Integer iconId = ATTR_ID_TO_ICON_ID.get(attrId);
                mIcons[iconId] = icon;
                mIconResourceIds[iconId] = keyboardAttrs.getResourceId(attrId, 0);
            } catch (Resources.NotFoundException e) {
                Log.w(TAG, "Drawable resource for icon #"
                        + keyboardAttrs.getResources().getResourceEntryName(attrId)
                        + " not found");
            }
        }
    }

現(xiàn)在分析下主題
有這么一個KeyboardTheme類的,這里就包含了4種主題了,自己用的哪種掰茶,去對應(yīng)下邊找就行了

    static final KeyboardTheme[] KEYBOARD_THEMES = {
        new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS,
                // This has never been selected because we support ICS or later.
                VERSION_CODES.BASE),
        new KeyboardTheme(THEME_ID_KLP, "KLP", R.style.KeyboardTheme_KLP,
                // Default theme for ICS, JB, and KLP.
                VERSION_CODES.ICE_CREAM_SANDWICH),
        new KeyboardTheme(THEME_ID_LXX_LIGHT, "LXXLight", R.style.KeyboardTheme_LXX_Light,
                // Default theme for LXX.
                Build.VERSION_CODES.LOLLIPOP),
        new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark,
                // This has never been selected as default theme.
                VERSION_CODES.BASE),
    };

如下圖片定義在這里

    <style name="KeyboardIcons.Holo">
        <!-- Keyboard icons -->
        <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo_dark</item>
        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo_dark</item>
        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo_dark</item>
        <item name="iconSpaceKey">@null</item>
        <item name="iconEnterKey">@drawable/sym_keyboard_return_holo_dark</item>
        <item name="iconSearchKey">@drawable/sym_keyboard_search_holo_dark</item>
        <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item>
        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo_dark</item>
        <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item>
        <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo_dark</item>
        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo_dark</item>
        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item>
        <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item>
        <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item>
        <item name="iconEmojiActionKey">@drawable/sym_keyboard_smiley_holo_dark</item>
        <item name="iconEmojiNormalKey">@drawable/sym_keyboard_smiley_holo_dark</item>
    </style>

結(jié)果發(fā)現(xiàn)ics和klp主題下的圖片不全暇藏,少5個,lxx主題下的圖片是全的濒蒋,這樣的話klp主題下那5個缺失的圖片咋獲妊渭睢把兔?少的那5個是動態(tài)變化的那個enter key

<Key
                    latin:backgroundType="functional2"
                    latin:keyStyle="enterKeyStyle"
                    latin:keyWidth="fillRight" />

然后去key_styles_common里找了下,沒有styleName叫enterKeyStyle的瓮顽,不可能啊县好,沒有的話會掛的,完事全局搜了下暖混,發(fā)現(xiàn)這東西定義在另外一個文件里缕贡,然后在key_styles_common里include進來

    <include
        latin:keyboardLayout="@xml/key_styles_enter" />

如下,簡單復(fù)制了幾個action拣播,根據(jù)imeAction區(qū)分晾咪,parentStyle里定義了其他信息,也是include的其他文件

        <case
            latin:imeAction="actionGo"
        >
            <key-style
                latin:styleName="enterKeyStyle"
                latin:parentStyle="goActionKeyStyle" />
        </case>
        <case
            latin:imeAction="actionNext"
        >
            <key-style
                latin:styleName="enterKeyStyle"
                latin:parentStyle="nextActionKeyStyle" />
        </case>

注意一下,這個幾個action的code名字都是key_enter
可以看到贮配,兩種case 谍倦,第一個先判斷icon有沒有定義 latin:isIconDefined,沒有的話就走下邊那種泪勒,那種里邊定義了text了昼蛀,

    <!-- Go key -->
    <switch>
        <case latin:isIconDefined="go_key">
            <key-style
                latin:styleName="goActionKeyStyle"
                latin:keySpec="!icon/go_key|!code/key_enter"
                latin:parentStyle="defaultEnterKeyStyle" />
        </case>
        <default>
            <key-style
                latin:styleName="goActionKeyStyle"
                latin:keySpec="!text/label_go_key|!code/key_enter"
                latin:parentStyle="defaultEnterKeyStyle" />
        </default>
    </switch>
    <!-- Next key -->
    <switch>
        <case latin:isIconDefined="next_key">
            <key-style
                latin:styleName="nextActionKeyStyle"
                latin:keySpec="!icon/next_key|!code/key_enter"
                latin:parentStyle="defaultEnterKeyStyle" />
        </case>
        <default>
            <key-style
                latin:styleName="nextActionKeyStyle"
                latin:keySpec="!text/label_next_key|!code/key_enter"
                latin:parentStyle="defaultEnterKeyStyle" />
        </default>
    </switch>

最終測試了,因為沒定義這5個圖片圆存,所以在這種主題下叼旋,輸入法的action確實不顯示圖片,顯示的是文字
go ,send ,next,prev,done 這種 辽剧,資源文件里有定義

<resources xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <string name="label_go_key" msgid="4033615332628671065">"Go"</string>
    <string name="label_next_key" msgid="5586407279258592635">"Next"</string>
    <string name="label_previous_key" msgid="1421141755779895275">"Prev"</string>
    <string name="label_done_key" msgid="7564866296502630852">"Done"</string>
    <string name="label_send_key" msgid="482252074224462163">"Send"</string>
    <string name="label_search_key" msgid="7965186050435796642">"Search"</string>
    <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string>
    <string name="label_wait_key" msgid="5891247853595466039">"Wait"</string>
</resources>

和KeyboardIconsSet一樣也有一個KeyboardTextsSet 的類來解析文字

public final class KeyboardTextsSet {
    public static final String PREFIX_TEXT = "!text/";
    private static final String PREFIX_RESOURCE = "!string/";

text獲取還得另外一個類KeyboardTextsTable.java
說明下 text 定義的字段是在KeyboardTextsTable里存著的送淆,string是資源文件string.xml里的
相關(guān)部分代碼

        final String name = text.substring(pos + prefixLength, end);
        if (prefix.equals(PREFIX_TEXT)) {//text文本
            sb.append(getText(name));
        } else { // PREFIX_RESOURCE
            final String resourcePackageName = mResourcePackageName;
            final RunInLocale<String> getTextJob = new RunInLocale<String>() {
                @Override
                protected String job(final Resources res) {
                    final int resId = res.getIdentifier(name, "string", resourcePackageName);//獲取string
                    return res.getString(resId);
                }
            };
            sb.append(getTextJob.runInLocale(mResources, mResourceLocale));
        }
//text開頭的文本是通過table查找的
    public String getText(final String name) {
        return KeyboardTextsTable.getText(name, mTextsTable);
    }

下邊簡單截取一部分table數(shù)據(jù)

    private static final String[] NAMES = {
    //  /* index:histogram */ "name",
        /*   0:33 */ "morekeys_a",
        /*   1:33 */ "morekeys_o",
        /*   2:32 */ "morekeys_e",
        /*   3:31 */ "morekeys_u",
        /*   4:31 */ "keylabel_to_alpha",
        /*   5:30 */ "morekeys_i",
//

    /* Default texts */
    private static final String[] TEXTS_DEFAULT = {
        /* morekeys_a ~ */
        EMPTY, EMPTY, EMPTY, EMPTY,
        /* ~ morekeys_u */
        // Label for "switch to alphabetic" key.
        /* keylabel_to_alpha */ "ABC",
        /* morekeys_i ~ */
        EMPTY, EMPTY, EMPTY,
繼續(xù)看下如何修改按鍵上圖片大小
  1. 直接去Key里找用到icon的地方
    public int getIconId() {
        return mIconId;
    }

    @Nullable
    public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha)

KeyboardView.java 里有用到getIcon方法
下邊的方法里税产,自己處理下iconWidth和iconHeight的計算邏輯怕轿,看你要放大還是縮小。

    // Draw key top visuals.
    protected void onDrawKeyTopVisuals(@Nonnull final Key key, @Nonnull final Canvas canvas,
            @Nonnull final Paint paint, @Nonnull final KeyDrawParams params) {
//
        final Drawable icon = (keyboard == null) ? null
                : key.getIcon(keyboard.mIconsSet, params.mAnimAlpha);
//
if (label != null) {
//這里是畫label的辟拷,如果要修改label大小顏色啥可以這里處理下
}
        // Draw key icon.
        if (label == null && icon != null) {
            final int iconWidth;
            if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) {
                iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio);
            } else {
                iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth);
            }
            final int iconHeight = icon.getIntrinsicHeight();
            final int iconY;
            if (key.isAlignIconToBottom()) {
                iconY = keyHeight - iconHeight;
            } else {
                iconY = (keyHeight - iconHeight) / 2; // Align vertically center.
            }
            final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center.
            drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight);
        }

如何去掉三個點

想了兩種情況:
第一種長按功能去掉撞羽,這個比較簡單,
第二種衫冻,長按功能還在诀紊,只是三個點不可見,找下三個點哪里設(shè)置的隅俘,隱藏掉或者顏色修改為透明
如下邻奠,把//delete 注釋的那行代碼刪了就看不到三個點了.

//空格
            <Key
                latin:keyStyle="spaceKeyStyle"
                latin:keyWidth="50.0%p" />
    <key-style
        latin:styleName="spaceKeyStyle"
        latin:keySpec="!icon/space_key|!code/key_space"
        latin:backgroundType="spacebar"
        latin:keyActionFlags="noKeyPreview|enableLongPress" />//delete
//逗號
            <Key
                latin:keySpec="!text/keyspec_comma"
                latin:keyLabelFlags="hasPopupHint"
                latin:keyStyle="settingsMoreKeysStyle" />
            <key-style
                latin:styleName="settingsMoreKeysStyle"
                latin:keyLabelFlags="hasPopupHint"http://delete
                latin:additionalMoreKeys="!text/keyspec_settings"
                latin:backgroundType="functional" />
//句號
                <Key
                    latin:keySpec="!text/keyspec_period"
                    latin:keyHintLabel="!text/keyhintlabel_period"
                    latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint"http://delete
                    latin:moreKeys="!text/morekeys_period"
                    latin:backgroundType="functional" />
//還有那個enter鍵也有三個點

最后發(fā)現(xiàn)三個點是在thems-holo.xml里定義的,可以把這里的字符串改成空为居,自然看不見了

        <!-- U+2026: "…" HORIZONTAL ELLIPSIS -->
        <item name="keyPopupHintLetter">&#x2026;</item>

也可以代碼里不畫了,還是KeyboardView.java里碌宴,這里兩個條件都滿足才畫,這樣我們就知道xml里的key屬性咋修改了蒙畴。刪除 latin:keyLabelFlags="hasPopupHint"即可

        if (key.hasPopupHint() && key.getMoreKeys() != null) {
            drawKeyPopupHint(key, canvas, paint, params);
        }

//
   public final boolean hasPopupHint() {
        return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0; // 0x200
    }

逗號和問號沒啥問題了贰镣,不過空格好像不太一樣呜象,空格好像不滿足上邊的if條件,那為啥也畫了三個點碑隆?
看space的屬性有個enableLongPress 恭陡,把這個刪了發(fā)現(xiàn)那三個點也沒了,搜了下關(guān)聯(lián)的代碼

//Key.java
    public final boolean isLongPressEnabled() {
        // We need not start long press timer on the key which has activated shifted letter.
        return (mActionFlags & ACTION_FLAGS_ENABLE_LONG_PRESS) != 0
                && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0;
    }
//MainKeyboardView.java
//可以看到上煤,對space單獨做了處理休玩,下邊有調(diào)用drawKeyPopupHint
    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
            final KeyDrawParams params) {
        if (key.altCodeWhileTyping() && key.isEnabled()) {
            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
        }
        super.onDrawKeyTopVisuals(key, canvas, paint, params);
        final int code = key.getCode();
        if (code == Constants.CODE_SPACE) {
            // If input language are explicitly selected.
            if (mLanguageOnSpacebarFormatType != LanguageOnSpacebarUtils.FORMAT_TYPE_NONE) {
                drawLanguageOnSpacebar(key, canvas, paint);
            }
            // Whether space key needs to show the "..." popup hint for special purposes
            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
                drawKeyPopupHint(key, canvas, paint, params);//space不想畫三個點把這里注釋也行
            }
        } else if (code == Constants.CODE_LANGUAGE_SWITCH) {
            drawKeyPopupHint(key, canvas, paint, params);
        }
    }

key間隙大小修改

  1. 水平方向的間隔
    <fraction name="config_key_horizontal_gap_holo">1.030%p</fraction>

上邊的值改成5%p,效果如下,可以看到楼入,兩個key之間的間隙大小就是這個值哥捕,那么對于左右兩端的,就是這個值的一半了


image.png

那么左右兩邊的間隔不可控嗎嘉熊?不是的遥赚,只是默認(rèn)的值是0而已

    <fraction name="config_keyboard_left_padding">0%p</fraction>
    <fraction name="config_keyboard_right_padding">0%p</fraction>

如果要水平方向所有間隔都一樣,咋辦阐肤?簡單
gap如果是10凫佛,那么最左邊的key默認(rèn)也有個5的間隔,這樣的話left padding也弄成5就ok了孕惜。

<fraction name="config_key_horizontal_gap_holo">10%p</fraction>
 <fraction name="config_keyboard_left_padding">5%p</fraction>
    <fraction name="config_keyboard_right_padding">5%p</fraction>
  1. 垂直方向的間隔
    <fraction name="config_min_keyboard_height">83%p</fraction>
    <fraction name="config_key_vertical_gap_holo">13.0%p</fraction>

如上愧薛,我把鍵盤整體高度弄的很大83%p,vertical gap 也不小衫画,效果如下毫炉,可以看出,這個不影響頂部和底部的間隔


image.png

那么頂部和底部的間隔咋控制削罩,下邊兩個值

    <fraction name="config_keyboard_top_padding_holo">2.0%p</fraction>
    <fraction name="config_keyboard_bottom_padding_holo">2.0%p</fraction>

這些都是主題里配置的,不同的分辨率可能有不同的值而已瞄勾,自己修改下自己需要的即可

    <style name="Keyboard">
        <item name="rowHeight">25%p</item>
        <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item>
        <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item>
        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item>
        <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item>
        <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item>
        <item name="keyboardLeftPadding">@fraction/config_keyboard_left_padding</item>
        <item name="keyboardRightPadding">@fraction/config_keyboard_right_padding</item>
        <item name="moreKeysTemplate">@xml/kbd_more_keys_keyboard_template</item>
        <item name="maxMoreKeysColumn">@integer/config_max_more_keys_column</item>
    </style>

那么這里的2%p的p指的是誰?就是對應(yīng)的鍵盤的寬和高,
那么如何讓水平和垂直的間隔一樣弥激?拿到鍵盤的寬高自己算下就ok了进陡。

        mKeyVerticalGap = (int) res.getFraction(R.fraction.config_key_vertical_gap_holo,
                defaultKeyboardHeight, defaultKeyboardHeight);
        mBottomPadding = (int) res.getFraction(R.fraction.config_keyboard_bottom_padding_holo,
                defaultKeyboardHeight, defaultKeyboardHeight);
        mTopPadding = (int) res.getFraction(R.fraction.config_keyboard_top_padding_holo,
                defaultKeyboardHeight, defaultKeyboardHeight);
        mKeyHorizontalGap = (int) (res.getFraction(R.fraction.config_key_horizontal_gap_holo,
                defaultKeyboardWidth, defaultKeyboardWidth));

問題記錄

  1. 輸入法主題里有4種,可測試發(fā)現(xiàn)只有第三種可以選微服,選其他的沒效果【我拿到的源碼是被修改過的】
    看下源碼趾疚,查找問題
    進入ThemeSettingsFragment類,查看主題存儲查找的邏輯
    public static KeyboardTheme getKeyboardTheme(final Context context) {
        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        final KeyboardTheme[] availableThemeArray = getAvailableThemeArray(context);
        return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray);
    }

//
    static KeyboardTheme[] getAvailableThemeArray(final Context context) {
        if (AVAILABLE_KEYBOARD_THEMES == null) {
            final int[] availableThemeIdStringArray = context.getResources().getIntArray(
                    R.array.keyboard_theme_ids);
            final ArrayList<KeyboardTheme> availableThemeList = new ArrayList<>();
            for (final int id : availableThemeIdStringArray) {
                final KeyboardTheme theme = searchKeyboardThemeById(id, KEYBOARD_THEMES);
                if (theme != null) {
                    availableThemeList.add(theme);
                }
            }
            AVAILABLE_KEYBOARD_THEMES = availableThemeList.toArray(
                    new KeyboardTheme[availableThemeList.size()]);
            Arrays.sort(AVAILABLE_KEYBOARD_THEMES);
        }
        return AVAILABLE_KEYBOARD_THEMES;
    }
//可以發(fā)現(xiàn)以蕴,只有themeid是2的返回了數(shù)據(jù)糙麦,其他都是null,所以最終AVAILABLE_KEYBOARD_THEMES只有一條數(shù)據(jù).
    static KeyboardTheme searchKeyboardThemeById(final int themeId,
            final KeyboardTheme[] availableThemeIds) {
        // TODO: This search algorithm isn't optimal if there are many themes.
        for (final KeyboardTheme theme : availableThemeIds) {
            if (theme.mThemeId == 2) {
                return theme;
            }
        }
        return null;
    }

more keys 彈框相關(guān)內(nèi)容修改記錄

MoreKeysKeyboardView 這個類就是用來顯示更多keys彈框的View丛肮,用到的地方如下

    <com.android.inputmethod.keyboard.MoreKeysKeyboardView
        android:id="@+id/more_keys_keyboard_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        style="?attr/moreKeysKeyboardViewStyle" />

搜下上邊用到的style赡磅,對應(yīng)主題下有這個屬性

<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.ICS</item>

//
    <style
        name="MoreKeysKeyboardView.ICS"
        parent="KeyboardView.ICS"
    >
        <item name="android:background">@drawable/keyboard_popup_panel_background_ics</item>
        <item name="keyBackground">@drawable/btn_keyboard_key_popup_ics</item>
        <item name="divider">@drawable/more_keys_divider</item>
        <item name="keyTypeface">normal</item>
        <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item>
    </style>

可以看到,有morekeysboard的背景腾供,key的背景仆邓,divider鲜滩,
那么這個彈框的大小位置咋確定的?
看MoreKeysKeyboard.java 里有個Builder

        public Builder(final Context context, final Key key, final Keyboard keyboard,
                final boolean isSingleMoreKeyWithPreview, final int keyPreviewVisibleWidth,
                final int keyPreviewVisibleHeight, final Paint paintToMeasure) {
            super(context, new MoreKeysKeyboardParams());
            load(keyboard.mMoreKeysTemplate, keyboard.mId);

            // TODO: More keys keyboard's vertical gap is currently calculated heuristically.
            // Should revise the algorithm.
            mParams.mVerticalGap = keyboard.mVerticalGap / 2;
            // This {@link MoreKeysKeyboard} is invoked from the <code>key</code>.
            mParentKey = key;

            final int keyWidth, rowHeight;//morekeys里的key的寬度這里可以自己處理
            if (isSingleMoreKeyWithPreview) {
                // Use pre-computed width and height if this more keys keyboard has only one key to
                // mitigate visual flicker between key preview and more keys keyboard.
                // Caveats for the visual assets: To achieve this effect, both the key preview
                // backgrounds and the more keys keyboard panel background have the exact same
                // left/right/top paddings. The bottom paddings of both backgrounds don't need to
                // be considered because the vertical positions of both backgrounds were already
                // adjusted with their bottom paddings deducted.
                keyWidth = keyPreviewVisibleWidth;
                rowHeight = keyPreviewVisibleHeight + mParams.mVerticalGap;
            } else {
                final float padding = context.getResources().getDimension(
                        R.dimen.config_more_keys_keyboard_key_horizontal_padding)
                        + (key.hasLabelsInMoreKeys()
                                ? mParams.mDefaultKeyWidth * LABEL_PADDING_RATIO : 0.0f);
                keyWidth = getMaxKeyWidth(key, mParams.mDefaultKeyWidth, padding, paintToMeasure);
                rowHeight = keyboard.mMostCommonKeyHeight;
            }
            final int dividerWidth;
            if (key.needsDividersInMoreKeys()) {
                dividerWidth = (int)(keyWidth /9f);
            } else {
                dividerWidth = 0;//key之間的分割線寬度节值,
            }
            final MoreKeySpec[] moreKeys = key.getMoreKeys();
            mParams.setParameters(moreKeys.length, key.getMoreKeysColumnNumber(), keyWidth,
                    rowHeight, key.getX() + key.getWidth() / 2, keyboard.mId.mWidth,
                    key.isMoreKeysFixedColumn(), key.isMoreKeysFixedOrder(), dividerWidth);
        }

下邊就是計算morekeys彈框的其他屬性了徙硅,比如寬高位置,有需要修改的可以自行修改

        public void setParameters(final int numKeys, final int numColumn, final int keyWidth,
                final int rowHeight, final int coordXInParent, final int parentKeyboardWidth,
                final boolean isMoreKeysFixedColumn, final boolean isMoreKeysFixedOrder,
                final int dividerWidth) {
            mIsMoreKeysFixedOrder = isMoreKeysFixedOrder;
            if (parentKeyboardWidth / keyWidth < Math.min(numKeys, numColumn)) {
                throw new IllegalArgumentException("Keyboard is too small to hold more keys: "
                        + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + numColumn);
            }
            mDefaultKeyWidth = keyWidth;
            mDefaultRowHeight = rowHeight;

            final int numRows = (numKeys + numColumn - 1) / numColumn;
            mNumRows = numRows;
            final int numColumns = isMoreKeysFixedColumn ? Math.min(numKeys, numColumn)
                    : getOptimizedColumns(numKeys, numColumn);
            mNumColumns = numColumns;
            final int topKeys = numKeys % numColumns;
            mTopKeys = topKeys == 0 ? numColumns : topKeys;

            final int numLeftKeys = (numColumns - 1) / 2;
            final int numRightKeys = numColumns - numLeftKeys; // including default key.
            // Maximum number of keys we can layout both side of the parent key
            final int maxLeftKeys = coordXInParent / keyWidth;
            final int maxRightKeys = (parentKeyboardWidth - coordXInParent) / keyWidth;
            int leftKeys, rightKeys;
            if (numLeftKeys > maxLeftKeys) {
                leftKeys = maxLeftKeys;
                rightKeys = numColumns - leftKeys;
            } else if (numRightKeys > maxRightKeys + 1) {
                rightKeys = maxRightKeys + 1; // include default key
                leftKeys = numColumns - rightKeys;
            } else {
                leftKeys = numLeftKeys;
                rightKeys = numRightKeys;
            }
            // If the left keys fill the left side of the parent key, entire more keys keyboard
            // should be shifted to the right unless the parent key is on the left edge.
            if (maxLeftKeys == leftKeys && leftKeys > 0) {
                leftKeys--;
                rightKeys++;
            }
            // If the right keys fill the right side of the parent key, entire more keys
            // should be shifted to the left unless the parent key is on the right edge.
            if (maxRightKeys == rightKeys - 1 && rightKeys > 1) {
                leftKeys++;
                rightKeys--;
            }
            mLeftKeys = leftKeys;
            mRightKeys = rightKeys;

            // Adjustment of the top row.
            mTopRowAdjustment = isMoreKeysFixedOrder ? getFixedOrderTopRowAdjustment()
                    : getAutoOrderTopRowAdjustment();
            mDividerWidth = dividerWidth;
            mColumnWidth = mDefaultKeyWidth + mDividerWidth;
            mBaseWidth = mOccupiedWidth = mNumColumns * mColumnWidth ;
            // Need to subtract the bottom row's gutter only.
            mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
                    + mTopPadding + mBottomPadding;
        }

臨時記錄

  1. 我們的需求隱藏了上邊的suggestion部分,只顯示key的部分搞疗,現(xiàn)在我想在keyboard右上角加個按鈕嗓蘑,點擊可以隱藏輸入法,如下,加個imageView匿乃,問題很多
    首先發(fā)現(xiàn)imageView如果不是match的話桩皿,那么下邊的MainKeyboardView的寬度和imageView一樣寬。
    其次幢炸,給imageView設(shè)置的點擊事件無效.
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"
    android:orientation="vertical" >

    <!-- To ensure that key preview popup is correctly placed when the current system locale is
         one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
    <com.android.inputmethod.latin.suggestions.SuggestionStripView
        android:id="@+id/suggestion_strip_view"
        android:layoutDirection="ltr"
        android:layout_width="match_parent"
        android:layout_height="@dimen/config_suggestions_strip_height"
        android:gravity="center_vertical"
        style="?attr/suggestionStripViewStyle" />

    <ImageView
        android:id="@+id/iv_hidden"
        android:src="@drawable/ic_shift_on"
        android:layout_width="match_parent"
        android:layout_height="80dp"/>
    <!-- To ensure that key preview popup is correctly placed when the current system locale is
         one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
    <com.android.inputmethod.keyboard.MainKeyboardView
        android:id="@+id/keyboard_view"
        android:layoutDirection="ltr"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

點擊無效泄隔,那么猜測touch事件被攔截了,應(yīng)該是父容器處理過宛徊?
查下代碼,很明顯佛嬉,要去看下InputView干啥了。

<com.android.inputmethod.latin.InputView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="?attr/inputViewStyle">
    <include
        android:id="@+id/main_keyboard_frame"
        layout="@layout/main_keyboard_frame" />
    <include
        android:id="@+id/emoji_palettes_view"
        layout="@layout/emoji_palettes_view" />
</com.android.inputmethod.latin.InputView>

測試結(jié)果闸天,我在InputView的幾個相關(guān)的touch方法里都添加了日志暖呕,可是點擊那個按鈕,這里邊一個方法都沒走苞氮,這就奇怪了湾揽,容器touch方法一個沒走是幾個意思。
然后我想著難道是InputView的父容器處理了笼吟?就是那個dialog库物,去dialog里查了下也沒處理。
最后在粗略瀏覽InputMethodService的時候看到了一個Insets的東西赞厕,看到了touchableRegion艳狐,setTouchableInsets定硝,看名字就是設(shè)置觸摸區(qū)域皿桑,觸摸范圍的,感覺有點意思蔬啡,就打印了下相關(guān)的代碼诲侮。結(jié)果證實就是這玩意整的。

 info.contentInsets.top = mTmpInsets.contentTopInsets;
            info.visibleInsets.top = mTmpInsets.visibleTopInsets;
            info.touchableRegion.set(mTmpInsets.touchableRegion);
            info.setTouchableInsets(mTmpInsets.touchableInsets);

整理下相關(guān)的代碼

  1. InputMethodService里rootView[就是dialog的根布局]注冊了一個回調(diào)
        mRootView = mInflater.inflate(
                com.android.internal.R.layout.input_method, null);
        mWindow.setContentView(mRootView);//window就是輸入法對應(yīng)的dialog
        mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);

回調(diào)處理

    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
        if (isExtractViewShown()) {
            // In true fullscreen mode, we just say the window isn't covering
            // any content so we don't impact whatever is behind.
            View decor = getWindow().getWindow().getDecorView();
            info.contentInsets.top = info.visibleInsets.top = decor.getHeight();
            info.touchableRegion.setEmpty();
            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
        } else {//我們用到的走這里
            onComputeInsets(mTmpInsets);
            info.contentInsets.top = mTmpInsets.contentTopInsets;
            info.visibleInsets.top = mTmpInsets.visibleTopInsets;
            info.touchableRegion.set(mTmpInsets.touchableRegion);
            info.setTouchableInsets(mTmpInsets.touchableInsets);
        }
    };

用到回調(diào)的地方
ViewRootIml.java

            final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
            insets.reset();

            // Compute new insets in place.
            mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);

然后ViewTreeObserver里箱蟆,下邊調(diào)用了回調(diào)

    final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
        // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
        // perform the dispatching. The iterator is a safe guard against listeners that
        // could mutate the list by calling the various add/remove methods. This prevents
        // the array from being modified while we iterate it.
        final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
                mOnComputeInternalInsetsListeners;
        if (listeners != null && listeners.size() > 0) {
            CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
            try {
                int count = access.size();
                for (int i = 0; i < count; i++) {
                    access.get(i).onComputeInternalInsets(inoutInfo);
                }
            } finally {
                listeners.end();
            }
        }
    }

onComputeInsets(mTmpInsets); 這個方法InputMethodService里有沟绪,子類LatinIME也有,我們只看下touchableRegion的設(shè)置

LatinIME.java

final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight;
        mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
           // Need to set expanded touchable region only if a keyboard view is being shown.
        if (visibleKeyboardView.isShown()) {
            final int touchLeft = 0;
            final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
            final int touchRight = visibleKeyboardView.getWidth();
            final int touchBottom = inputHeight
                    // Extend touchable region below the keyboard.
                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
            outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
        }

由此可見,我們只需要修改下visibleTopY即可空猜。因為我們的需求是把suggestions布局隱藏了绽慈,所以上邊的suggestionsHeight就是0了恨旱,最終topY就是mainKeyboard的頂部位置了,現(xiàn)在要加個按鈕在mainKeyboard上坝疼,那么需要再減去箭頭的高度搜贤,以確保箭頭也在可觸摸范圍。

        /**
         * Option for {@link #setTouchableInsets(int)}: the entire window frame
         * can be touched.
         */
        public static final int TOUCHABLE_INSETS_FRAME = 0;

        /**
         * Option for {@link #setTouchableInsets(int)}: the area inside of
         * the content insets can be touched.
         */
        public static final int TOUCHABLE_INSETS_CONTENT = 1;

        /**
         * Option for {@link #setTouchableInsets(int)}: the area inside of
         * the visible insets can be touched.
         */
        public static final int TOUCHABLE_INSETS_VISIBLE = 2;

        /**
         * Option for {@link #setTouchableInsets(int)}: the area inside of
         * the provided touchable region in {@link #touchableRegion} can be touched.
         */
        public static final int TOUCHABLE_INSETS_REGION = 3;

這種效果


image.png

箭頭所在的背景是透明的钝凶,需要注意的地方仪芒,還是LatinIME里的onComputeInsets方法
2行加注釋的地方,outinsets只修改touch的top耕陷,確保點擊圖標(biāo)有效掂名,content的top位置不修改,確保上邊的icon沒有背景哟沫,是透明的饺蔑。

        if (visibleKeyboardView.isShown()) {
            final int touchLeft = 0;
            final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
            final int touchRight = visibleKeyboardView.getWidth();
            final int touchBottom = inputHeight
                    // Extend touchable region below the keyboard.
                    + EXTENDED_TOUCHABLE_REGION_HEIGHT;
            outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
            outInsets.touchableRegion.set(touchLeft, touchTop-60, touchRight, touchBottom);//觸摸范圍top增加icon的高度哨苛,60是箭頭的高度
        }
        outInsets.contentTopInsets = visibleTopY;//content的top不修改慷荔,還是mainkeyboard的頂部
        outInsets.visibleTopInsets = visibleTopY;

雜記

  1. 自己寫一個輸入法的話,要做啥竹勉?
    首先要成為輸入法裹虫,需要一個服務(wù),這個服務(wù)在你切換當(dāng)前輸入法為默認(rèn)輸入法的時候就啟動了肿嘲。
    meta-data是必須的,name是固定的筑公,有這個才會被識別為輸入法雳窟。
        <service android:name=".july.MyTestService"
            android:label="test input"http://系統(tǒng)設(shè)置里顯示的輸入法的名字就是這個
            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>

method.xml

<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"http://系統(tǒng)設(shè)置里輸入法選項會跳轉(zhuǎn)這個類
    android:isDefault="true"
    android:supportsSwitchingToNextInputMethod="true">
    <subtype android:icon="@drawable/abc_vector_test"
        android:label="subtype label"
        android:name="name"
        android:isAsciiCapable="true"
        />
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市匣屡,隨后出現(xiàn)的幾起案子封救,更是在濱河造成了極大的恐慌,老刑警劉巖捣作,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件誉结,死亡現(xiàn)場離奇詭異,居然都是意外死亡券躁,警方通過查閱死者的電腦和手機惩坑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來也拜,“玉大人以舒,你說我怎么就攤上這事÷” “怎么了蔓钟?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卵贱。 經(jīng)常有香客問我滥沫,道長侣集,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任兰绣,我火速辦了婚禮肚吏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狭魂。我一直安慰自己罚攀,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布雌澄。 她就那樣靜靜地躺著斋泄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪镐牺。 梳的紋絲不亂的頭發(fā)上炫掐,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音睬涧,去河邊找鬼募胃。 笑死,一個胖子當(dāng)著我的面吹牛畦浓,可吹牛的內(nèi)容都是我干的痹束。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼讶请,長吁一口氣:“原來是場噩夢啊……” “哼祷嘶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起夺溢,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤论巍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后风响,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘉汰,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年状勤,在試婚紗的時候發(fā)現(xiàn)自己被綠了鞋怀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡荧降,死狀恐怖接箫,靈堂內(nèi)的尸體忽然破棺而出攒读,到底是詐尸還是另有隱情朵诫,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布薄扁,位于F島的核電站剪返,受9級特大地震影響废累,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脱盲,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一邑滨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钱反,春花似錦掖看、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尚卫,卻和暖如春归榕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吱涉。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工刹泄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怎爵。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓特石,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鳖链。 傳聞我的和親對象是個殘疾皇子县匠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一乞旦、安裝中文輸入,谷歌拼音 1. 命令行下輸入 sudo apt-get install fcitx-google...
    JoshXXX閱讀 13,720評論 1 16
  • 輸入法編輯器(IME)是一個便于用戶輸入文本的控件题山。Android提供了一個可擴展的輸入法框架兰粉,允許應(yīng)用程序提供可...
    凱玲之戀閱讀 26,685評論 6 17
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒顶瞳。表情可以傳達很多信息玖姑。高興了當(dāng)然就笑了,難過就哭了慨菱。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,143評論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險厭惡者焰络,不喜歡去冒險,但是人生放棄了冒險符喝,也就放棄了無數(shù)的可能闪彼。 ...
    yichen大刀閱讀 6,054評論 0 4