之前做一个类似微信聊天的界面,要实现这种效果在自定义adapter的时候需要复写两个方法:
public int getItemViewType(int position)
public int getViewTypeCount()
然后在getView的时候根据type来指定converView。
当然这个是最基本的知识,今天要说的是另外的坑。
我们知道,listview渲染view是从上到下的
header |
item0 |
item1 |
item2 |
。。。 |
footer |
聊天界面是有下拉加载更多的,即到顶下拉会加载之前的记录,然后我们调用notifyDataSetChanged(),listview会刷新视图,这时我们会到新加载数据的顶部,通过查看源码,我发现listview在onChange的时候,如果当前位置不在顶部,它会记录下位置,更新的时候再复原,而在顶部的时候,就相当于重新开始渲染(其实也相当于是记住位置了,因为就是在顶部。)
但是聊天界面一般都是加载完新数据之后还是停留在之前的位置,那我们会调用listview.setSelectSection(int index),回到之前浏览的位置,这样界面会闪一下。为了解决这个问题,我使用recyclerView,recyclerview有两个方法可以反转列表,setStackFromEnd(true),setReverseLayout(true),设置完这两个属性时候,recyclerview展示视图变成了这样
footer |
。。。 |
item2 |
item1 |
item0 |
header |
但是使用recyclrview又带来了另一个问题。聊天记录的页面还有另一个需求,当用户点击输入的时候,界面需要滚到最底部,即最新的聊天内容处,这个需求使用listview实现很方便,listview有一个属性,alwaysScroll,在xml里定义listview的时候alwaysScroll设置为true,就能实现这个需求。但是recyclerview没有这个属性啊,那我们就只能监听键盘弹起事件了,但是android好像没有这种监听事件,所以我自定义了外层Layout,在manifest里定义activity的时候,我们设置聊天的activity的windowSoftInputMode为“adjustResize”,这样键盘弹起的时候布局会重绘,调整高度。
所以我自定义了外层Layout:
public class KeyBoardChangeLinearLayout extends LinearLayout {
private OnKeyBoardChangeListener mListner;
public KeyBoardChangeLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public KeyBoardChangeLinearLayout(Context context) {
super(context);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mListner != null) {
if (oldh > h) {
mListner.onShow();
} else {
mListner.onHidden();
}
}
}
public void setOnKeyBoardChangeListener(OnKeyBoardChangeListener listener) {
mListner = listener;
}
public interface OnKeyBoardChangeListener {
void onShow();
void onHidden();
}
}
复写onSizeChanged方法,当键盘弹起,触发layout重绘,oldh>h,当键盘收起,也触发layout重绘,oldh<h,这样我就能监听到键盘弹起还是收回了,当键盘弹起的时候,我让recyclerview滑动到第0项,也就是最底部就可以了,recyclerview.scrollToPosition(0),这个需求也算是实现了。
recyclerview之前没有使用过,经常使用的是listview,一个需求使用哪个空间合适并没有什么可以争执的,只要能更好的实现效果就可以了。