`
亚当爱上java
  • 浏览: 706098 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

android 之输入法

阅读更多
输入法的例子和源代码看了不少时间了,看得头很晕,很郁闷。静下心来把整个代码想了一遍,其实大部分代码都在处理绘制界面,以及事件的处理,核心代码很少,都被底层封装得很完美了。
先看看一般输入法的界面:





分为两个部分,上部分是候选窗口(CandidateView),用来显示候选词,现在常用的输入法都有这个功能,如在搜狗、google输入法中输入abc,输入框中就会显示很多相关联的中文词。下部分就是软键盘了,这个没什么好说的。


输入法中核心类是InputMethodService
其次就是:CandidateView 和KeyboardView

CandidateView为候选窗口,InputMethodService在启动过程中会调用接口public View onCreateCandidatesView() ,在这个方法

中把CandidateView对象返回后,InputMethodService内部会将其布局到相应的位置。

在android中没有CandidateView父类,得自己从头写,一般的做法是:

通过方法public void setService(InputMethodService listener) 将Service类传进来,然后再通过public void setSuggestions(List<String> suggestions…) 方法将候选词列表传递过来,CandidateView将其显示到界面上,用户选择结束后,再通过service的方法pickSuggestionManually(mSelectedIndex) ,将选择的候选词在列表中的序号传递回去。至此, CandidateView 就完成了它神圣的使命。

android中KeyboardView有一个通用类,继承它可以通过简单的配置文件就显示出很专业软键盘。在源代码中,它绝大部分代码都在做绘制工作和事件处理,不过就其本质功能来说是相当地简单,用户摁下软键盘上的某个键后,它把这个键所代表的值传递给InputMethodService类也完成了它的使命。InputMethodService在public View onCreateInputView() 方法中获得该View。

InputMethodService就是输入法的核心了,该类是一个Service,跟其它默默无闻的Service不同的是,它是一个带有View的Service。其内部有几个个重要的接口:

InputMethodImpl

InputMethodSessionImpl

InputConnection


InputMethodService通过这几个个类跟系统和输入框进行交互的。

输入框从InputMethodService获取信息是通过InputConnection来实现的, 在启动输入法时,InputConnection由客户端控件创建,并传递给输入法应用,由输入法应用调用,进行信息反馈




InputMethod接口定义了一套操纵输入法应用的方法。如,bindInput, hideInput, startInput等。为了系统安全,这类接口只有系统可以访问,客户端控件无法直接调用这个接口。所有的输入法应用都需要客户端控件具有 BIND_INPUT_METHOD权限,作为系统的安全机制,否则将无法与输入法服务交互。




InputMethodSession作为InputMethod的辅助接口类,为客户端控件开放了可直接调用的函数接口。包括向输入法应用分发键盘事件,更新光标位置,更新编辑区域内选择的问题信息等。客户端控件通过IIputMethodSession对于输入法应用的交互是单向的,即只能向输入法应用传递信息,无法获取信息

以上几个点是从网上copy过来的,感觉这几点对于理解InputMethodService特别有用。

代码看得太多反而看不清本质,这几个类中最实用的是InputConnection的

public boolean commitText(CharSequence text, int newCursorPosition) 。

通过KeyboardView和CandidateView, InputMethodService类已经获得了想要的内容,然后通过这个方法把值传递给输入框。

按奈不住心中读源代码的郁闷,先来写一个输入法发泄一下:

先来一个CandidateView,设想的布局如下:




这个View中不进行任何自绘制,用android现有的View,两边各一个按钮(Button),用来滚动多个候选词,中间显示候选词(TextView),为了方便CandidateView继承RelativeLayout的内部类,便于加入子控件和控制,setService和 setSuggestions两个方法可以不用,反正是内部类,不过为了配合上面的说明,思量再三还是加上了:

public class helloIme extends InputMethodService {

class CandidateView extends RelativeLayout{
    TextView tv;                 // 中间显示候选词
    Button btLeft, btRight; // 左右按钮
    helloIme listener;         // helloIme 用于返回选中的 候选词下标
    List<String> suggestions; // 候选词列表, KeyboardView 不同的键按下后会设置相关的列表
    int mSelectedIndex = -1;  // 当前 候选词下标
   
public CandidateView(Context context) {
    super(context);
   
    tv = new TextView(context);
    tv.setId(1);
    RelativeLayout.LayoutParams lpCenter = new RelativeLayout.LayoutParams(200, ViewGroup.LayoutParams.WRAP_CONTENT);
    lpCenter.addRule(RelativeLayout.CENTER_IN_PARENT);
    addView(tv, lpCenter);
    tv.setOnClickListener(new OnClickListener(){
        public void onClick(View v) {
            listener.pickSuggestionManually(mSelectedIndex);
        }
    });
   
    btLeft = new Button(context);
    btLeft.setText("<");
    btLeft.setOnClickListener(new OnClickListener(){
        public void onClick(View arg0) {
            mSelectedIndex = mSelectedIndex > 0 ? (mSelectedIndex - 1) : 0;
            tv.setText(suggestions.get(mSelectedIndex));
        }
    });
   
    RelativeLayout.LayoutParams lpLeft = new RelativeLayout.LayoutParams(60, ViewGroup.LayoutParams.WRAP_CONTENT);
    lpLeft.addRule(RelativeLayout.LEFT_OF, 1);
    addView(btLeft, lpLeft);
   
    btRight = new Button(context);
    btRight.setText(">");
    btRight.setOnClickListener(new OnClickListener(){
        public void onClick(View v) {
            mSelectedIndex = mSelectedIndex >= suggestions.size() - 1 ? suggestions.size() - 1 : mSelectedIndex + 1;
            tv.setText(suggestions.get(mSelectedIndex));
        }
    });
   
    RelativeLayout.LayoutParams lpRight = new RelativeLayout.LayoutParams(60, ViewGroup.LayoutParams.WRAP_CONTENT);
    lpRight.addRule(RelativeLayout.RIGHT_OF, 1);
    addView(btRight, lpRight);
}

public void setService(helloIme listener){
    this.listener = listener;
}

public void setSuggestions(List<String> suggestions) {
    mSelectedIndex = 0;
    tv.setText(suggestions.get(mSelectedIndex));
    this.suggestions = suggestions;
}
}

上面最重要的是粗体的那两行,View的布局还是花费了很多代码:(

KeyboardView的布局预想如下:





就两个按钮,点if时往输入框中输出if(){}, if(){}else if(){}...,whie时往输入框中输出whie(){},这个类同样是继承于RelativeLayout的内部类:

class KeyboardView extends RelativeLayout{
  public KeyboardView(Context context) {
        super(context);
       
        Button btIf = new Button(context);
        btIf.setText("if");
        btIf.setId(1);
        RelativeLayout.LayoutParams lpIf = new RelativeLayout.LayoutParams(100, 50);
        lpIf.addRule(RelativeLayout.CENTER_HORIZONTAL);
       
        btIf.setOnClickListener(new OnClickListener(){
            public void onClick(View v) {
                setCandidatesViewShown(true); // 显示 CandidateView
                helloIme.this.onKey("if"); // 将点击按钮的值传回给 InputMethodService
            }
        });
        addView(btIf, lpIf);
       
        Button btWhile = new Button(context);
        btWhile.setText("while");
        RelativeLayout.LayoutParams lpWhile = new RelativeLayout.LayoutParams(100, 50);
        lpWhile.addRule(RelativeLayout.BELOW , 1);
        lpWhile.addRule(RelativeLayout.ALIGN_LEFT, 1);
       
        btWhile.setOnClickListener(new OnClickListener(){
            public void onClick(View v) {
                setCandidatesViewShown(true);
                helloIme.this.onKey("while");
            }
        });
        addView(btWhile, lpWhile);
    }
}

CandidateView默认是不显示的,所以需要调用InputMethodService 的setCandidatesViewShown()方法。

接下来把helloIme的代码贴出来:

public class helloIme extends InputMethodService {
    private List<String>suggestionlist; // 当前候选词表
    private Hashtable<String, List<String>> data; // 词典数据
    private KeyboardView mkeyView;
    private CandidateView mCandView;
   
    public void onInitializeInterface() { //InputMethodService在启动时,系统会调用该方法,具体内容下回再表
        // 初始化 词典数据

        data = new Hashtable<String, List<String>>();
        List<String> list = new ArrayList<String>();
        list.add("if(){}");
        list.add("if(){}else if(){}");
        list.add("if(){}else{}");
        data.put("if", list);
       
        list = new ArrayList<String>();
        list.add("while(){}");
        data.put("while", list);
    }
   
    public View onCreateInputView() {
        mkeyView = new KeyboardView(this);
        return mkeyView;
    }

    public View onCreateCandidatesView() {
        mCandView = new CandidateView(this);
        mCandView.setService(this);
        return mCandView;
    }
   
    public void pickSuggestionManually(int mSelectedIndex){
        getCurrentInputConnection().commitText(suggestionlist.get(mSelectedIndex), 0); // 往输入框输出内容
        setCandidatesViewShown(false); // 隐藏 CandidatesView
    }
   
    public void onKey(CharSequence text){
        // 根据按下的按钮设置候选词列表


        suggestionlist = data.get(text);
        mCandView.setSuggestions(suggestionlist);
    }




    class KeyboardView extends RelativeLayout{

     //......

    }





    class CandidateView extends RelativeLayout{

    //......

    }

}

代码写完,再来写配置文件,

在res目录下面建立一个新目录xml,然后创建一个method.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- The attributes in this XML file provide configuration information -->
<!-- for the Search Manager. -->

<input-method
    xmlns:android="http://schemas.android.com/apk/res/android" />


设置Manifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="test.helloIme"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <service android:name="helloIme"
        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>
    </application>
    <uses-sdk android:minSdkVersion="5" />

</manifest>

直接运行程序,eclipse输出如下Log:

[2010-08-25 17:16:48 - helloIme]Installing helloIme.apk...
[2010-08-25 17:16:50 - helloIme]Success!
[2010-08-25 17:16:50 - helloIme]\helloIme\bin\helloIme.apk installed on device
[2010-08-25 17:16:50 - helloIme]Done!




嗯,安装成功了!呵呵,革命尚未成功,还需在模拟器上进行设置:

点击settings->Language & keyboard,在下部出现了一个test,右边有个checkbox,选上它。

找一个有输入框的应用,最简单到写短消息的画面,左键长按输入框,会弹出一个输入法选择提示框,点进去就会看到刚才创建的输入法了,点击右边的单选框,oh,my ime,漂亮的hello输入法就展现在面前了:





android果然很强大啊。

  • 大小: 39 KB
  • 大小: 3.6 KB
  • 大小: 3.1 KB
  • 大小: 10.7 KB
分享到:
评论
3 楼 stonewoodfish 2012-11-01  
哥们,太给力了。能发个源码参考下吗,正需要这个,不甚感激。shifeng@coship.com,这时我邮箱,谢谢了
2 楼 wukongwork 2012-08-29  
你好,看了你的文章,自己也写了一个(当然是工作需要,呵呵),不过遇到了一个问题,我有一个按键是关闭输入法,就相当于系统输入法的完成,一点完成后输入完毕,
不知道这个方法你用过没requestHideSelf(0);,说是可以关闭,但只是闪了一下,输入法又打开了,真不知道怎么弄了。
你写的KeyboardView是extends RelativeLayout,而系统的KeyboardView中有一个closing()方法,我实在不知道该怎么处理,请教博主
1 楼 xihuan&java 2011-05-27  
楼主请问怎么实现不同输入框,设置使用相应的默认输入法

相关推荐

    Android之输入法开发简单说明.pdf

    通过分析【部分内容】,我们可以提炼出关于Android输入法开发的以下几个关键知识点: 1. Android输入法服务(InputMethodService):InputMethodService是Android平台上用于创建自定义输入法的基础类。它继承自...

    Android之输入法开发简单说明[参照].pdf

    总的来说,Android输入法开发涉及到大量的自定义和事件处理,开发者需要深入理解Android系统的输入框架以及UI组件的交互机制。通过熟练掌握这些知识点,你就能创建出功能丰富且个性化的输入法应用,满足用户的特定...

    Android自定义输入法软键盘

    在Android平台上,自定义输入法软键盘是一种常见的需求,它允许开发者为特定的应用或设备创建个性化的输入体验。本文将深入探讨如何在Android系统中实现一个自定义输入法,主要基于提供的博客链接——《Android...

    Android IMF输入法总结

    Android IMF输入法总结 Android IMF 输入法总结是 Android 1.5 新添加的一个重要功能,用来支持软键盘、各种的输入法。 IMF Input Method Framework 的主要组件包括 Input Method Manager (IMM)、Input Method (IME...

    Android输入法的打开关闭

    本文将深入探讨Android输入法的打开和关闭机制,以及如何通过编程方式实现这一功能。 首先,理解Android系统的输入法管理机制至关重要。Android系统使用InputMethodManager来管理输入法的状态,包括显示和隐藏。...

    Android简单输入法 源码

    本项目名为“Android简单输入法 源码”,是一个实现26键布局的Android输入法示例,主要用于教学目的。下面将详细介绍这个项目涉及的关键知识点。 1. **自定义输入法服务(InputMethodService)**: Android提供了...

    android OpenWnnIM 开源输入法

    安卓(Android)系统本身的确没有原生的日文输入法,但是谷歌公司在安卓SDK中包含了日本omronsoft软件公司的开源输入法OpenWnn。OpenWnn是omronsoft公司的手机输入法iWnn IME的精简开源版本。OpenWnn在官方各个版本...

    android 全键盘输入法源码

    在Android系统中,全键盘输入法是用户与设备交互的重要组成部分,特别是在移动设备上,它提供了文字输入的方式。本文将深入探讨"android 全键盘输入法源码"的相关知识点,帮助开发者理解输入法的工作原理,并从中...

    Android 类似搜狗输入法android源码.zip

    本压缩包"Android 类似搜狗输入法android源码.zip"包含了开发类似功能的Android输入法的一些关键源代码和相关资源,对于学习和研究Android输入法的开发具有很高的参考价值。 首先,源码中的关键组件可能包括以下几...

    Android中文输入法实现-杨武

    ### Android中文输入法实现的关键知识点 #### 一、中文输入法的重要性及复杂性 在《Android中文输入法实现》这一主题下,杨武深入探讨了为何开发者应当关注中文输入法的实现。中文作为一种复杂的语言,拥有...

    android控制输入法是否隐藏

    总结来说,这个Demo提供了处理Android输入法显示和隐藏问题的解决方案,包括防止输入法自动弹出、控制自定义输入法行为以及在操作过程中提供反馈。通过学习和应用这些方法,开发者可以更好地控制应用程序与输入法的...

    Android平台输入法源码汇总

    "Android平台输入法源码汇总"是一个资源集合,对于想要深入理解输入法实现机制或者开发自己的Android输入法的人来说,具有很高的参考价值。 首先,我们来探讨Android输入法的基本架构。Android输入法主要由服务端...

    基于Android的输入法系统的设计与实现

    【基于Android的输入法系统的设计与实现】 随着社会的快速发展和科技的日新月异,智能手机已经成为我们生活中不可或缺的一部分。从最初的通信工具发展到如今的多功能设备,手机输入法也经历了从单一的英文输入到...

    Android修改输入法Enter的文本

    通过阅读该博客,开发者可以学习到如何根据需求定制Android输入法的Enter键,以适应各种应用场景,比如聊天应用、搜索框等。 文件名称"EnterText"可能是指包含有关如何修改Enter键文本的资源文件,比如XML布局文件...

    Android谷歌拼音输入法源码

    开发环境:android studio Chipmunk 2021.2.1版本 内容概要:谷歌拼音输入法,支持中英文切换。C++代码,cmake编译通过,整个工程可以编译成一个输入法apk,安装后需要在设置里添加此输入法,再选择使用即可。 适合...

    ANDROID搜狗输入法客户端

    ANDROID搜狗输入法客户端 ANDROID搜狗输入法客户端 ANDROID搜狗输入法客户端

    android7.1.2内置搜狐输入法

    在Android系统中,内置输入法是用户与设备交互的关键组件之一。Android 7.1.2版本中的内置搜狐输入法提供了高效、智能的文本输入体验,尤其针对中文用户设计。这款输入法集成了SogouInput.apk和Android.mk文件,它们...

    Android处理输入法隐藏后不再显示问题

    在Android开发过程中,有时会遇到一个常见的问题:输入法在被隐藏后无法再次显示。这个问题主要出现在用户与应用交互时,比如点击一个EditText输入框触发输入法弹出,然后通过程序逻辑或者用户操作隐藏了输入法,但...

    android 讯飞输入法

    在**Android输入法**领域,讯飞输入法的竞争优势在于其用户友好的界面设计和丰富的个性化设置。用户可以根据个人喜好调整键盘布局、皮肤、字体大小等,同时提供多种输入模式,如全键盘、九宫格、笔画、拼音等,满足...

Global site tag (gtag.js) - Google Analytics