【Android游戏开发-笨木头】抛弃自带输入框,自己实现~自由自在~!

笨木头  2012-03-4 23:01   Android   阅读(5,356)   12条评论

 我是一个很喜欢炫耀的人,我最近解决了一个问题,所以来炫耀了~嘿嘿~
希望各位被我炫耀的同时,也把我学到的知识偷走,随便偷,不要钱哈~

在看我的帖子之前,请确保你已经看过或者掌握了这个帖子(在没有输入焦点类控件上调用输入法):
http://www.android-fans.net/forum.php?mod=viewthread&tid=38&extra=page%3D1
是这位朋友发了这个帖子之后我才得以解决这个问题的~再次感谢他~

好了,描述一下我的问题,游戏开发的过程中,很少甚至可以说是没有用到系统自带的控件~
反正我是没有用到,不知道是不是我井底之蛙了,嘿嘿~
按钮、标签、窗口、滑动窗口之类的控件,虽说实现得不是很好,但是起码还是能自己实现的。
唯独这个输入框,整晕我了,困扰了我好久~
我不知道怎么弹出输入法,就算后来我知道了,我还是不知道怎么去获取输入法的输入。

不知道有没有人有着和我一样的困扰呢?~
是不是发现我好多废话?~别着急,你还要忍受我的很多废话呢,嘻嘻~

好喇,开始~

先看截图,免得浮躁,嘻嘻:

 

 

整个例子的代码都在附件里,我的注释应该算蛮清晰的,嘿嘿,所以大家看代码就好,在这里我只是简单说几个要注意的地方~

再次提醒,记得先看懂了上面提到的那个帖子再往下看哈~

1. 首先来看看游戏的主界面,GameView里要重写一个方法:

 

@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
	return new XInputConnection(this, false);
}

是的,这个就是上面提到的那个帖子里教我们的,重写这个方法就能监听到输入法的一些小动作,这家伙,逃不过我们的手掌的,嘿嘿~

 

2. 当然,必须得自己实现一个XInputConnection类,先别看代码,写看看我的解释,哈哈:

public class XInputConnection extends BaseInputConnection {

	public XInputConnection(View targetView, boolean fullEditor) {
		super(targetView, fullEditor);
	}

	@Override
		public boolean commitText(CharSequence text, int newCursorPosition) {
		/* 发送每一个输入的字符到自定义的输入框控件 */
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("sChar", (String)text);
		GlobalClient.messageDispatch.dispatchMessage(EnumMsgDispatchType.EN_COMMIT_TEXT, map);
		return true;
	}

	@Override
	public boolean sendKeyEvent(KeyEvent event) {
		/* 发送键盘事件到自定义的输入框控件 */
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("keyEvent", event);
		GlobalClient.messageDispatch.dispatchMessage(EnumMsgDispatchType.EN_SEND_KEY_EVENT, map);
		return super.sendKeyEvent(event);
	}
}

这个类很简单,抓住了输入法的两个小动作,一个是输入字符,一个是软键盘的事件(可能我说错了,大概是这样的,可以纠正我错误~)。

抓住了输入字符的事件,我们就可以让自己的输入框显示这些字符,这个很重要,输入框必须得有的功能嘛~
抓住键盘事件又是做什么的呢?那大家想想,我只能输入,不能删除,那这个输入框要来做嘛?~
是的,有了键盘事件,我们就能监听到删除键,然后就能删除字符。

OK~输入框要的两个基本功能已经有条件实现了~

注意喇,别去研究两个方法的具体实现先,那只是我实现的方法,大家都可以用自己的方式去实现的~
有了杯子和水,至于怎么把水装进杯子,就因人而异了,对不对?~

3. 主角出场~我自己实现的EditText控件(只贴出部分代码):

/**
* 自定义的输入框
* @author 笨木头
*
*/
public class XEditText implements IMessageDispatchSink, ISoldHold {

	public XEditText(Context _context, Point _point, Bitmap _background) {
		this.context = _context;
		this.point = _point;
		this.background = _background;

		initOnClickListener();
	}

	public void drawMe(Canvas canvas) {
		/* 输入框背景 */
		canvas.drawBitmap(background, point.x, point.y, paint);

		/* 输入框文字的坐标调整了,调整的算法是临时的,所以可以忽略 */
		canvas.drawText(sText, point.x + iFontSize / 2, 
		point.y + iFontSize / 2 + background.getHeight() / 2, paint);

	}

	@Override
	public void onRecv(EnumMsgDispatchType enMsgDispatchType, HashMap<String, Object> map) {
		switch(enMsgDispatchType) {
		case EN_COMMIT_TEXT: /* 输入字符 */
			commitText(map);
			break;
		case EN_SEND_KEY_EVENT: /* 键盘事件 */
			sendKeyEvent(map);
			break;
		}
	}

	@Override
	public void onClickCMD() {
		listener.onClick();
	}

	@Override
	public void setOnClickListener(IMsgOnClickListener listener) {
		this.listener = listener;
	}

	/**
	* 输入字符
	* @param map
	*/
	private void commitText(HashMap<String, Object> map) {
		if(map == null) {
			return;
		}

		String sChar = (String) map.get("sChar");
		sText += sChar;
	}

	/**
	* 键盘事件
	* @param map
	*/
	private void sendKeyEvent(HashMap<String, Object> map) {
		if(map == null) {
		return;
		}

		KeyEvent event = (KeyEvent) map.get("keyEvent");

		if(event.getAction() == 1) {
			/* 删除字符事件 */
			if(event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
				int iLen = sText.length();
				if(iLen > 0) {
					sText = sText.substring(0, iLen - 1);
				}
			}
		}
	}
	/**
	* 设置监听事件
	*/
	private void initOnClickListener() {
		this.setOnClickListener(new IMsgOnClickListener() {
			@Override
			public void onClick() {
				/* 发布显示输入法的消息 */
				GlobalClient.messageDispatch.dispatchMessage(EnumMsgDispatchType.EN_SHOW_IUPUT, null);
			}
		});
	}

}

 

首先,作为一个控件,肯定少不了要有一个OnClick事件咯,至于实现的方法,大家用自己的方法就好,可以不用管我的方法。
然后,XEditText里还有两个方法,一个commitText(), 大家都知道了,用来获取字符的;还有一个sendKeyEvent(), 用来删除字符的~
OK,那,输入法输入字符和删除字符的动作,是怎么告诉我的输入框的呢?我用的方法是消息派发:
XInputConnection里调用这两个方法的时候,就发布消息,告诉所有想知道这个方法的对象。
至于消息派发是什么东西,也有大部分人都懂了~
如果有还没有接触到消息派发的朋友,我以后再另开帖子写一下我的见解哈~

反正现在大家只要知道一点,输入法有了输入字符和删除字符的动作,就会调用XInputConnection 的commitText()和sendKeyEvent()方法,
而这两个方法被我们重写了,我们可以在两个方法里利用消息派发,把输入的字符和删除字符动作告诉输入框。
OK了~嘿嘿

4. 还有一点要注意的,GameView要获取了焦点之后才能弹出输入法:

/**
* 显示输入法界面
*/
private void showInput() {
	/* 要让GameView获得焦点后才能弹出输入法 */
	this.requestFocus();

	input.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
}

我的唠叨到此结束喇~
最后当然要附上我的代码喇~
为了来这里炫耀,这个例子我写了一个多小时,然后这篇文字我又写了差不多一个小时,大家不要辜负我吖~嘿嘿

对了,补充一下,这个方法是可以实现输入框,但是有个问题:
部分手机在横屏的情况下,会无法显示输入法的候选栏,这个我还没有解决,如果有哪位朋友解决,请分享咯~嘿嘿

代码是为了尽量节省时间,所以有些地方是写死的:
1. 一些字符串常量没有抽离出来
2. 没有写专门管理控件的模块
3. 初始化函数堆了很多东西,有些乱

4. 坐标写死
5. 除了以上4条,如果有其他问题,一定要告诉我哈,记得咯~

 

 

源代码(CSDN上传的资源还没有验证通过,大家暂时到这个帖子里下载哈):http://www.android-fans.net/forum.php?mod=viewthread&tid=99#lastpost

 

 

 

 

 

CSDN的资源已经上传了,补上: http://download.csdn.net/detail/musicvs/4112154

12 评论

  1. 能简单介绍下如何自己实现一个button吗,不是靠继承系统控件那种,因为我想方便的用在surfaceview里面,但是不知道如何监听他,

发表评论

电子邮件地址不会被公开。 必填项已用*标注