写一个Android输入法02——候选窗、转换
上一篇介绍了完成Android输入法的最小化步骤,它只能将按键对应的字符上屏。一般的东亚语言都有一个转换的过程,比如汉语输入拼音,需要由拼音转成汉字再上屏。本文将在前文基础上加入完成转换过程所必需的候选窗。本文代码可参见https://github.com/palanceli/AndroidXXIME/tree/v2。
如下图所示,用红框框出来的窗体是候选窗,其内的字符创叫做候选串,点击候选窗使之进入输入控件叫做上屏。没有输入的时候隐藏候选窗,当输入字串还未上屏时显示候选窗:
引入候选窗需要完成两个步骤:
一、创建CandidateView,该窗口需要覆盖如下两个方法,已完成自绘:
- onDraw(Canvas canvas);
- onMeasure(int widthMeasureSpec, int heightMeasureSpec);
二、覆盖AndroidXXIME类的如下两个方法:
- onCreateCandidateView();
在该方法中创建CandidateView。
- onKey(int primaryCode, int [] keyCodes);
在该方法中响应按键消息,如:当按下字母键,则展现候选窗以及候选字串;当按下空格,则上屏候选字串,等等。
创建CandidateView
public CandidateView(Context context) {
super(context);
Log.d(this.getClass().toString(), "CandidateView: "); // 设置前景、背景色、字体、字号
Resources r = context.getResources(); setBackgroundColor(getResources().getColor(R.color.candidate_background, null)); mColorNormal = r.getColor(R.color.candidate_normal, null);
mVerticalPadding = r.getDimensionPixelSize(R.dimen.candidate_vertical_padding); mPaint = new Paint();
mPaint.setColor(mColorNormal);
mPaint.setAntiAlias(true);
mPaint.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_font_height));
mPaint.setStrokeWidth(0); setWillNotDraw(false); // 覆盖了onDraw函数应清除该标记
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(this.getClass().toString(), "onMeasure: ");
int wMode = MeasureSpec.getMode(widthMeasureSpec);
int wSize = MeasureSpec.getSize(widthMeasureSpec); int measuredWidth = resolveSize(50, widthMeasureSpec); final int desiredHeight = ((int)mPaint.getTextSize()) + mVerticalPadding; // 系统会根据返回值确定窗体的大小
setMeasuredDimension(measuredWidth, resolveSize(desiredHeight, heightMeasureSpec));
} @Override
protected void onDraw(Canvas canvas) {
Log.d(this.getClass().toString(), "onDraw: ");
super.onDraw(canvas); if (mSuggestions == null)
return; // 依次绘制每组候选字串
int x = 0;
final int count = mSuggestions.size();
final int height = getHeight();
final int y = (int) (((height - mPaint.getTextSize()) / 2) - mPaint.ascent()); for (int i = 0; i < count; i++) {
String suggestion = mSuggestions.get(i);
float textWidth = mPaint.measureText(suggestion);
final int wordWidth = (int) textWidth + X_GAP * 2; canvas.drawText(suggestion, x + X_GAP, y, mPaint);
x += wordWidth;
}
} public void setSuggestions(List<String> suggestions) {
// 设置候选字串列表
if (suggestions != null) {
mSuggestions = new ArrayList<String>(suggestions);
}
invalidate();
requestLayout();
} }
覆盖onCreateCandidateView()方法
该方法会在每次输入法被呼出的时候调用,如函数名所示,在这里创建候选窗口。
public class AndroidXXIME extends InputMethodService
implements KeyboardView.OnKeyboardActionListener {
…… @Override public View onCreateCandidatesView(){
Log.d(this.getClass().toString(), "onCreateCandidatesView: ");
candidateView = new CandidateView(this);
return candidateView;
}
……
}
覆盖onKey(int primaryCode, int [] keyCodes)方法
public class AndroidXXIME extends InputMethodService
implements KeyboardView.OnKeyboardActionListener {
……
@Override
public void onKey(int primaryCode, int[] keyCodes) {
InputConnection ic = getCurrentInputConnection();
playClick(primaryCode);
switch(primaryCode){
case Keyboard.KEYCODE_DELETE :
// 如果收到的是DELETE键,则删除光标前的一个字符
ic.deleteSurroundingText(1, 0);
break;
case Keyboard.KEYCODE_DONE:
// 如果收到的是DONE键,则执行回车
ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
break;
default:
char code = (char)primaryCode;
if(code == ' '){ // 如果收到的是空格
if(m_composeString.length() > 0) { // 如果有写作串,则将首个候选提交上屏
ic.commitText(m_composeString, m_composeString.length());
m_composeString.setLength(0);
}else{ // 如果没有写作串,则直接将空格上屏
ic.commitText(" ", 1);
}
}else { // 否则,将字符计入写作串
m_composeString.append(code);
ic.setComposingText(m_composeString, 1);
}
updateCandidates();
}
}
}
在updateCandidates()函数中向CandidateView塞入候选字串列表,并触发该窗口更新。
当在系统“语言和输入法”-“更改键盘”中选择输入法时,
系统会调用该输入法InputMethodService的如下方法:
- onCreate()
- onInitializeInterface() 可以在该方法中完成与输入法相关的初始化操作,比如加载词库。
- onStartInput() 每次切换输入焦点的时候,都会调用该方法,在这里可以完成和会话相关的初始化操作,后面还会介绍。
当一个输入控件获得焦点,呼出输入法,到它失去焦点,这期间成为一次会话。当一个会话开始时,系统会调用输入法InputMethodService的如下方法:
- onStartInput() 负责会话相关的初始化工作。输入法要负责在会话切换时,清除上次会话的中间数据,以防止前一个会话的中间数据窜入下个会话。这和Windows平台下的输入法有很大区别,在Windows下,输入法一个DLL,它依附在切出输入法的进程中,因此,每个输入法进程保存各自的输入法上下文,如果需要在进程间共享数据(比如词库),则需要采用共享内存机制;而在Android平台下,输入法是一个独立的进程,所有的数据仅在该进程中保存一份,此时则需要考虑如何隔离不同进程间的私有数据,比如前一个进程输入一半但未上屏的数据,切到另一个进程或输入控件后,就应该清除掉。该回调函数用来做这类工作。
- onCreateInputView() 创建输入法键盘布局。
- onCreateCandidatesView() 创建候选窗。
完成以上步骤之后,输入法就多出了候选窗口,下图中浅蓝色窗体既是:
在处理上还是很简陋,比如退格还不支持删除输入串,还不支持点击上屏,等等。这些属于业务逻辑的细节了,可以慢慢精耕细作。
写一个Android输入法02——候选窗、转换的更多相关文章
- 写一个Android输入法01——最简步骤
本文演示用Android Studio写一个最简单的输入法.界面和交互都很简陋,只为剔肉留骨,彰显写一个Android输入法的要点. 1.打开Android Studio创建项目,该项目和普通APP的 ...
- 为PhoneGap写一个android插件
为PhoneGap写一个android插件,要怎么做? 其实这句话应该反过来说,为android写一个PhoneGap插件,要怎么做? 这里以最简单的Hello World!为例,做个说明: 1.第一 ...
- 写一个比较全的进制转换函数--ic
//写一个比较全的进制转换函数-----未完成 #include <stdio.h> //D进制转换后 (比如10-2进制) 结果可能会很大 需要很长的字符串来存 #include < ...
- 教你写一个Android可快速复用的小键盘输入控件
引子 在Android项目开发中特别是一些稍大型的项目,面对需求文档的时候你经常会发现很多地方用到了同样的组件,但是又略有不同.比如这个: 右边是一个小键盘输入板,左边当焦点不同的时候分别用右边的小键 ...
- 【Android开发经验】来,咱们自己写一个Android的IOC框架!
到眼下位置.afinal开发框架也是用了好几个月了,还记得第一次使用凝视完毕控件的初始化和事件绑定的时候,当时的心情是多么的兴奋- -代码居然能够这样写!然后随着不断的学习,也慢慢的对IOC框架和注解 ...
- 一起写一个Android图片加载框架
本文会从内部原理到具体实现来详细介绍如何开发一个简洁而实用的Android图片加载缓存框架,并在内存占用与加载图片所需时间这两个方面与主流图片加载框架之一Universal Image Loader做 ...
- 假设写一个android桌面滑动切换屏幕的控件(一)
首先这个控件应该是继承ViewGroup: 初始化: public class MyGroup extends ViewGroup{ private Scroller mScroller; priva ...
- 一起写一个Android图片轮播控件
注:本文提到的Android轮播控件Demo地址: Android图片轮播控件 1. 轮播控件的组成部分 我们以知乎日报Android客户端的轮播控件为例,分析一下轮播控件的主要组成: 首先我们要有用 ...
- 写一个android内置android程序
当我们编译完毕android源代码之后,就须要对他做点什么事情,我如今正在看老罗的"Android源代码情景分析"一书.在这里主要是记录一些书中没有说清楚的地方. 相同.我们创建一 ...
随机推荐
- apache配置详解 apache安装路径
http://www.linuxidc.com/Linux/2015-02/113921.htm 不同apache的安装方式 的安装目录示例 http://www.121down.com/articl ...
- Android实现推送方式解决方案 - 长连接+心跳机制(MQTT协议)
本文介绍在Android中实现推送方式的基础知识及相关解决方案.推送功能在手机开发中应用的场景是越来起来了,不说别的,就我们手机上的新闻客户端就时不j时的推送过来新的消息,很方便的阅读最新的新闻信息. ...
- laravel 导出导入excel和csv文件的 使用
在项目中用到的常用功能数据导入导出 在laravel有插件可以直接使用 方便快捷 学习源头: https://www.cnblogs.com/martianShu/p/5869270.html htt ...
- (转)AppCan中调用系统浏览器打开网页
<!DOCTYPE html> <html> <head> <style>body{ background:#fff; font-size:30px;} ...
- leetcode Single Number II - 位运算处理数组中的数
题目描述: 给定一个包含n个整数的数组,除了一个数出现一次外所有的整数均出现三次,找出这个只出现一次的整数. 题目来源: http://oj.leetcode.com/problems/single- ...
- python window使用paramiko简单监控数据指标数据采集
#!/usr/bin/python #-*- coding: utf-8 -*- #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ...
- 关于SQL结构化查询语言中(+)的用法
一.概述 (+):从符号上理解为添加一些内容进入结果集中,那么自然会问到这么几个问题 1.添加什么内容 2.怎么添加 3.添加到什么结果集中 以下内容将以实例说明上面3个问题. 二.实例 以Oracl ...
- 前端seo小结,网页代码优化
seo的目的:提高网站流量 search engine optimization 搜索引擎优化seo search engine marketing 搜索引擎营销sem 权重10个等级 等级越大,权重 ...
- LinearLayout线性布局搭配权重属性的使用
在开发中,我们是通过布局来完成应用界面的搭配的,通过各种布局,我们可以完成各种复杂的界面设计.而LinearLayout也就是我们说的线性布局,这个比较简单而且使用很广泛的一种布局.下面我们通过一个D ...
- struct-config.xml配置文件的解析
//定义了xml文件的版本和编码<?xml version="1.0" encoding="UTF-8"?>//配置文件中的元素必须按照下述doc指 ...