美文网首页
输入法修改记录

输入法修改记录

作者: 有点健忘 | 来源:发表于2021-06-18 17:41 被阅读0次

    android studio 运行LatinIME工程

    网上找了篇 https://www.jianshu.com/p/5278addbf99c

    1. 从android源码里copy下这个app需要的部分代码
      路径:源码目录/frameworks/opt/inputmethodcommon/java/com/android/inputmethodcommon
      copy下上边opt目录下的inputmethodcommon文件夹,复制到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下对应的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,这时候工程应该可以正常运行了。对了,就像帖子里说的,你的studio需要下载配置好ndk
    补充说明
    上边改完debug模式没啥问题了,release build的时候会出问题
    清单文件里会提示下边的有问题,因为gradle配置里已经设置了最小版本,目标版本了

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

    签名完apk无法安装,网上搜到的解决办法是在application标签下添加

    android:extractNativeLibs="true"
    

    这些可能都是gradle编译的时候会出现的问题,如果你是放在系统源码里一起编译到image里的,估计不用改这些了吧。因为我也没有image,只有别人从 image里提取出来的这个LatinIME工程。。
    我这里改的是8.0的输入法.

    ui修改

    目前只考虑英文的,我这里测试的是平板,所以只修改了xml-sw600dp 这个文件夹下相关的资源文件,如果是给手机用的,那修改默认的xml下的文件即可

    1. 默认的英文字母界面 rows_kwerty.xml
      总共有4个Row也就是4行,最后一行通过include引用了另外一个通用的文件 row_kwerty4.xml
      比如想删除一个key,添加一个key都可以,不过需要注意按键的总宽度不能大于100,否则有的按钮就不显示了

    我这里是把删除按键和action按键放到最后两行了

    
    

    默认界面左下角点击那个123的按键,会跳到 数字符号界面

    1. 数字符号界面 rows_symbols.xml
    
    

    在这个界面点击第三行第一个按钮,切换到更多符号界面

    1. 对应的就是rows_symbols_shift.xml
      如下最后两行的代码,
            <Key
                latin:keySpec="&#x00BF;" />
            <Key //替换原来和左侧一样的键为删除键
                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>
    

    第二行,这个和我想的效果差距太大,我这百分比是加上间隔算的,现在发现不对

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

    测试结果发现这里的百分比是除去间隔的,所以上边第二行修改如下
    9个字母占90%,每个10%,剩下两边各5%,也就是默认起始x偏移5%,效果如下

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

    rows_qwerty.xml 修改字母界面没啥问题,
    修改数字界面,应该是rows_symbols.xml结果发现改完没变化,那就是改错地方了。

    我测试的是横屏板子输入法,所以最先找的是 xml-sw600dp-land 没找到,我就去xml下去找了,
    现在想起来还有个xml-sw600dp文件夹,land下没有应该先找这个的。这次修改完就生效了,如下


    image.png

    数字界面切换符号界面
    rows_symboles_shift.xml
    简单修改完成,目前只是把key的位置修改完了。


    image.png

    键盘高度修改

    我们的需求是键盘高度增大了,所以根据下边的逻辑,我就修改了下最小高度,最后使用的就是这个值了。
    在config.xml 下看到这个名字,需要注意下,你要适配的大小,找对应的,这个文件有很多个,找对应的修改

        <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)) {//目前数组是空的,所以会走这里了
                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) {//如果是负的,弄成正的,而且负的标志百分比是按照宽来算的
                // 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的宽高是一样的,所以这里需要根据屏幕的宽高计算下。
    方法如下:

    因为我们要求普通按键的宽高是一样的,所以键盘的高度需要计算出来。
    首先需要定义好按键之间的间隔,因为用到的都是百分比。而且p指的是键盘的宽高,不是屏幕的宽高,因为水平方向一般是铺满屏幕的,所以宽和屏幕宽一样,但屏幕高和键盘高差距很大,别理解错了。
    以下边设置为例
        <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指的是键盘的高度,不是屏幕的高度】, 假设 键盘高度是x%p,那么
    假设屏幕高度为h,宽为w
    x%*h=10%*(x%*h)+ w*9%*4;
    x=w*4/h
    知道设备的宽高,那么这个键盘高度百分比x也就算出来了。
    实际测试发现上边的算法有问题,因为间隔获取的时候直接强转为int了,比如结果是7.88强转以后就是7了,所以上边算出来有误差
    比如,宽度440,间隔2%p,我们会认为10个间隔是20%p,也就是88,实际结果不是,单个2%p是8.8取整就是8,
    那么10个间隔实际是80,和上边的公式就差了8,
    

    如果没有这要求,其实高度设置多少都行,反正key的高度最后是平分键盘高度的.
    需要注意下,我只修改英文键盘,所以是按照4行算的,如果是泰文,那是5行的,所以上边只是说明了下计算的逻辑,具体的还得看实际需求,如果不想修改配置文件,你也可以直接在上边的getDefaultKeyboardHeight方法里,根据宽度来算下高度

    主题

    我用的8.0的源码,这个有提供4种主题,如下

    image.png
    修改的时候要找到对应的主题修改,别修改错了地方,源码里主题都在这了
    image.png
    看到light和dark,那么
    themes-lxx-dark 对应的 Material Dark
    themes-lxx-light 对应的 Material Light
    themes-lxx 是上边两个通用的部分
    themes-klp 是 Holo White
    themes-ics 是Holo Blue(区分很简单,这里的颜色有蓝色的)
    themes-holo 是上边两个通用的
    themes-common 是所有主题通用的部分
    目前我以holo white 为默认的做修改,其他几种先不管
        <style
            name="KeyboardView.KLP"
            parent="KeyboardView.Holo"
        >
            <item name="android:background">#F2F2F2</item>//键盘背景,不包括上边提示那部分
            <item name="keyBackground">@drawable/btn_keyboard_key_klp_test</item>//默认按键的背景,就是那些字母数字
            <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>//第一行字母右上角那数字的颜色
            <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>
    

    修改按键背景

    按键的背景系统默认的有3种, 普通按钮,功能性的按键,空格键
    根据需求我们把这三种颜色修改成我们要的.然后还得增加一种深色的,如下图。


    image.png

    默认最底下一行除了空格都是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>
    
    // 系统默认就定义了三种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 里加载的,所以需要去主题里设置

        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;
    

    现在假设增加了一种attrs.xml下添加

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

    theme里添加
    如果四个主题你都要改的话,那就都加,或者加载默认的主题下边,我这里就只处理一种主题

            <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里处理下
    如下,下个方法增加一个参数,从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了,运行了下挂了,下边这行挂了,数组越界,
    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设置为gone就完事了,结果没效果。然后搜了下这个控件用到的地方,发现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里有个设置是否提示【第一个参数】,以及当前是否全屏【第二个参数】
    我们不需要显示这个布局,那么上边代码直接改成如下即可

    mSuggestionStripView.updateVisibility(false,true);
    

    如果需要修改颜色啥的,那去主题里修改下,应该是以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修改

    默认是bold,改下

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

    折腾到现在长这样,现在问题就是图标看起来有点小


    image.png

    图标大小如何修改

    先来回顾下之前研究的,这些Key的内容咋定义的

    1. 普通的字母
      第一种是第一行那种长按可以选择数字的,第二种是第二行那普通的字母
        <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的内容是定义在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);
    

    看下这些方法,可以得出一些结论
    1.1. keySpec可以有多个,以竖杠分开
    1.2. 如果有icon的话,icon必须放在首位
    1.3. 文字图片都是根据前缀来区分的
    1.4. 解析出对应的名字,比如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都对应一个style的,我们去查下style

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

    现在分析下主题
    有这么一个KeyboardTheme类的,这里就包含了4种主题了,自己用的哪种,去对应下边找就行了

        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>
    

    结果发现ics和klp主题下的图片不全,少5个,lxx主题下的图片是全的,这样的话klp主题下那5个缺失的图片咋获取?少的那5个是动态变化的那个enter key

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

    然后去key_styles_common里找了下,没有styleName叫enterKeyStyle的,不可能啊,没有的话会挂的,完事全局搜了下,发现这东西定义在另外一个文件里,然后在key_styles_common里include进来

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

    如下,简单复制了几个action,根据imeAction区分,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里的
    相关部分代码

            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数据

        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,
    
    继续看下如何修改按键上图片大小
    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);
            }
    
    

    如何去掉三个点

    想了两种情况:
    第一种长按功能去掉,这个比较简单,
    第二种,长按功能还在,只是三个点不可见,找下三个点哪里设置的,隐藏掉或者颜色修改为透明
    如下,把//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"//delete
                    latin:additionalMoreKeys="!text/keyspec_settings"
                    latin:backgroundType="functional" />
    //句号
                    <Key
                        latin:keySpec="!text/keyspec_period"
                        latin:keyHintLabel="!text/keyhintlabel_period"
                        latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint"//delete
                        latin:moreKeys="!text/morekeys_period"
                        latin:backgroundType="functional" />
    //还有那个enter键也有三个点
    

    最后发现三个点是在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 ,把这个删了发现那三个点也没了,搜了下关联的代码

    //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单独做了处理,下边有调用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

    那么左右两边的间隔不可控吗?不是的,只是默认的值是0而已

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

    如果要水平方向所有间隔都一样,咋办?简单
    gap如果是10,那么最左边的key默认也有个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指的是谁?就是对应的键盘的宽和高,
    那么如何让水平和垂直的间隔一样?拿到键盘的宽高自己算下就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种,可测试发现只有第三种可以选,选其他的没效果【我拿到的源码是被修改过的】
      看下源码,查找问题
      进入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;
        }
    //可以发现,只有themeid是2的返回了数据,其他都是null,所以最终AVAILABLE_KEYBOARD_THEMES只有一条数据.
        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 弹框相关内容修改记录

    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,对应主题下有这个属性

    <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的部分,现在我想在keyboard右上角加个按钮,点击可以隐藏输入法,如下,加个imageView,问题很多
      首先发现imageView如果不是match的话,那么下边的MainKeyboardView的宽度和imageView一样宽。
      其次,给imageView设置的点击事件无效.
    <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事件被拦截了,应该是父容器处理过?
    查下代码,很明显,要去看下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>
    

    测试结果,我在InputView的几个相关的touch方法里都添加了日志,可是点击那个按钮,这里边一个方法都没走,这就奇怪了,容器touch方法一个没走是几个意思。
    然后我想着难道是InputView的父容器处理了?就是那个dialog,去dialog里查了下也没处理。
    最后在粗略浏览InputMethodService的时候看到了一个Insets的东西,看到了touchableRegion,setTouchableInsets,看名字就是设置触摸区域,触摸范围的,感觉有点意思,就打印了下相关的代码。结果证实就是这玩意整的。

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

    整理下相关的代码

    1. InputMethodService里rootView[就是dialog的根布局]注册了一个回调
            mRootView = mInflater.inflate(
                    com.android.internal.R.layout.input_method, null);
            mWindow.setContentView(mRootView);//window就是输入法对应的dialog
            mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
            mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
    

    回调处理

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

    用到回调的地方
    ViewRootIml.java

                final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
                insets.reset();
    
                // Compute new insets in place.
                mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
    

    然后ViewTreeObserver里,下边调用了回调

        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的设置

    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的顶部位置了,现在要加个按钮在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,确保点击图标有效,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. 自己写一个输入法的话,要做啥?
      首先要成为输入法,需要一个服务,这个服务在你切换当前输入法为默认输入法的时候就启动了。
      meta-data是必须的,name是固定的,有这个才会被识别为输入法。
            <service android:name=".july.MyTestService"
                android:label="test input"//系统设置里显示的输入法的名字就是这个
                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"//系统设置里输入法选项会跳转这个类
        android:isDefault="true"
        android:supportsSwitchingToNextInputMethod="true">
        <subtype android:icon="@drawable/abc_vector_test"
            android:label="subtype label"
            android:name="name"
            android:isAsciiCapable="true"
            />
    

    相关文章

      网友评论

          本文标题:输入法修改记录

          本文链接:https://www.haomeiwen.com/subject/ivzdeltx.html