最近在做毕业设计,其中一个小功能就是要实现一个聊天窗口,聊天窗口里面要输入内容,输入法弹出来以后,信息气泡要抬起来,输入法收回去以后还要放下来。
在网上查了一圈,基本认定,是没有输入法弹出收回这个监听器的,只能从基础布局的大小入手,监听基础布局的大小改变,来判断输入法的弹出和收回。
View中已经提供了方法
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* @param w Current width of this view.
* @param h Current height of this view.
* @param oldw Old width of this view.
* @param oldh Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
}
但View和我正在使用的RelativeLayout并不提供setOnSizeChangedListener()方法,因此,我们需要在RelativeLayout中添加一个setOnSizeChangedListener()方法,因此,需要把RelativeLayout重写,目的是重写里面的onSizeChange()方法。
public class ResizableRelativeLayout extends RelativeLayout {
<span style="white-space:pre"> </span>//构造方法直接调用父类方法,此处已省略
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
}
那么问题来了,我的目的是让Listview里面的内容往上推,但是,我重写的是外面嵌套的布局,我怎么告诉用这个布局的Activity尺寸已经改变,然后让Activity做一些事呢?
想一下JavaScript,方法是可以在方法的参数里传递的,那么在Java里面,有没有方法能把方法传递过来呢?答案是有。
我们需要在重写的ResizableRelativeLayout里面添加一个接口,在这个接口里面有一个方法,这个方法在尺寸改变时调用,那么如果要监听尺寸改变,就要实现这个接口,那么这个接口的方法自然就从实现接口的地方传递进来,这样就算是传递了方法。
再来个大白话版:接口在里面摆着,就是让这个类里的方法们一个方向,让他们去调这个接口里的方法,至于方法具体是什么,无所谓,反正知道要去调这个方法,工作就结束了。
那么,我们把接口添加进去。
public class ResizableRelativeLayout extends RelativeLayout {
private OnSizeChangeListener onSizeChangeListener;
public interface OnSizeChangeListener{
void onSizeChange(int width, int height, int oldWidth, int oldHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
onSizeChangeListener.onSizeChange(w,h,oldw,oldh);//在尺寸改变时,调用接口里面的那个方法,这个方法,是自己实现的,也就实现了方法传递
}
public void setOnSizeChangeListener(OnSizeChangeListener onSizeChangeListener){
this.onSizeChangeListener = onSizeChangeListener;
}
}
在Activity中,使用
resizableRelativeLayout.setOnSizeChangeListener(new ResizableRelativeLayout.OnSizeChangeListener() {
@Override
public void onSizeChange(int width, int height, int oldWidth, int oldHeight) {
<span style="white-space:pre"> </span>//需要在尺寸改变时做的事
}
});
这样,就实现了对尺寸改变的监听。
但问题还没解决,我们要做的是监听输入法弹出收回,所以要在这里做一个判断,当高度减小时,判断结果是输入法弹出,反之输入法收回。
resizableRelativeLayout.setOnSizeChangeListener(new ResizableRelativeLayout.OnSizeChangeListener() {
@Override
public void onSizeChange(int width, int height, int oldWidth, int oldHeight) {
if(oldHeight>height)
listView.setSelection(listView.getAdapter().getCount()-1);
}
});
运行后发现,确实可以监听输入法弹出和收回,但是ListView并没有跟着动,大概想一下原因就是,输入法弹出是有延迟的,具体原因还没有想,解决方法是让ListView延迟抬起。
resizableRelativeLayout.setOnSizeChangeListener(new ResizableRelativeLayout.OnSizeChangeListener() {
@Override
public void onSizeChange(int width, int height, int oldWidth, int oldHeight) {
if(oldHeight>height)
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
listView.setSelection(listView.getAdapter().getCount()-1);
}
},500);
}
});
这样,就实现了监听输入法弹出和收回。
我们来总结一下思路:
1.系统没有提供输入法弹出和收回的监听,我们需要自己实现这个监听,实现这个监听的一个方法,就是监听界面的尺寸改变,因为输入法弹出后会改变界面尺寸;
2.系统依然没有提供尺寸改变的监听,但在尺寸改变时有一个回调方法onSizeChanged(),我们需要用这个回调方法自己实现一个监听;
3.实现监听的目的是方法传递,我们需要建立一个接口,在外面实现接口里面的方法,就实现了方法传递。
至此已经实现了监听尺寸改变以及输入法弹出收回
4.在实际环境中,输入法弹出有延迟,需要让需要做的事延迟启动。
然后是另一个问题,为什么使用接口。
然后是另一个问题,为什么使用接口。
上边已经说到,接口是为了传递方法,而传递方法的原理,就是类里面的方法知道接口里有那么一个叫xxx的方法,所以类里的方法就知道怎么调用这个xxx方法,具体的实现,可以放在使用这个类的时候再去实现。