在另一篇文章“从零开始学习RecyclerView(二)”中,已经介绍如何为RecyclerView的item添加点击和长按事件的监听器,但是监听这些事件肯定得做某些操作,不然监听了也没有什么意义,这篇文章就记录一下如何对RecyclerView的数据进行操作,大家可以结合点击事件来实现点击修改数据或者长按删除数据等,还有记录一下如何为RecyclerView添加分割线,同样,这篇文章也是建立在之前的代码基础之上的。
RecyclerView具有高度自由性,其中一个原因也是它可以进行局部刷新,所以想往RecyclerView的Adapter中增、删、改单个数据是很方便的,官方API为我们提供了以下方法:
//该方法用于当增加一个数据的时候,position表示新增数据显示的位置
final void notifyItemInserted(int position)
//该方法用于删除一个数据的时候,position表示数据删除的位置
final void notifyItemRemoved(int position)
//该方法表示所在position对应的item位置不会改变,但是该item内容发生变化
final void notifyItemChanged(int position)
//该方法一般用于:适配器之前装载的数据大部分已经过时了,需要重新更新数据
//调用该方法的时候,recyclerView会重新计算子item及所有子item重新布局
//出于效率考虑,官方建议用更加精确的方法(比如上面三个方法)来取代这个方法
final void notifyDataSetChanged()
这几个方法还是很容易理解的,然后我们就可以在TestAdapter增加几个方法:
//移除数据
public void removeData(int position) {
stringList.remove(position);
notifyItemRemoved(position);
}
//新增数据
public void addData(int position) {
stringList.add(position, "Add One");
notifyItemInserted(position);
}
//更改某个位置的数据
public void changeData(int position) {
stringList.set(position, "Item " + position + " has changed");
notifyItemChanged(position);
}
在原来的MainActivity.xml布局上添加两个按钮:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_add"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="add" />
<Button
android:id="@+id/btn_change"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="change" />
</LinearLayout>
然后在MainActivity中处理一下这两个Button的点击事件,调用TestAdapter中的addData()和changeData()方法:
findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mTestAdapter.addData(1);
}
});
findViewById(R.id.btn_change).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mTestAdapter.changeData(1);
}
});
然后将之前MainActivity中RecyclerView的监听器修改一下,监听到长按时,删除该item。
rvTest.addOnItemTouchListener(new RecyclerViewClickListener(this, rvTest,
new RecyclerViewClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "Click " + getList().get(position), Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
mTestAdapter.removeData(position);
Toast.makeText(MainActivity.this, "Remove " + getList().get(position), Toast.LENGTH_SHORT).show();
}
}));
重新运行程序,应该可以看到下面结果:
可以看到,数据被正确的增、删、改了。而且这增删改的过程中,不是很突兀的直接变化,而是有一个小动画效果,使得看起来很舒服,它就是使用了RecyclerView默认提供的动画效果。
//这行代码不是必须的,因为RecyclerView会默认使用
rvTest.setItemAnimator(new DefaultItemAnimator());
在“从零开始学习RecyclerView(一)”中提供一个抽象类ItemAnimator,它就是为RecyclerView的item提供动画效果的,DefaultItemAnimator()就是它的一个实现类,也就是说,我们也可以自己定义其他的动画,不过我还没有很了解动画的实现,所以就先不记录这个了,听说Github上有个开源项目挺好的:RecyclerViewItemAnimators,大家可以学习一下。
public static abstract class ItemDecoration {
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect,((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),parent)
}
}
这三个方法中,onDraw()和onDrawOver()显然是用于绘制的,那么绘制分割线的逻辑可以放在这里面,它们二者的具体区别是:onDraw是在item view绘制之前调用,而onDrawOver是在item view绘制之后调用,因此我们一般选择重写其中一个方法即可。getItemOffsets,这个方法是告诉RecyclerView在绘制完一个item view的时候,应该留下多少空位,以便于绘制分割线。
接下来我们就可以写一个实现类了,该类参考自官方。
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
//使用系统自带的listDivider
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation){
//使用TypeArray加载该系统资源
final TypedArray ta = context.obtainStyledAttributes(ATTRS);
mDivider = ta.getDrawable(0);
//缓存
ta.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation){
if(orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if(mOrientation == VERTICAL_LIST){
drawVertical(c,parent);
}else{
drawHorizontal(c,parent);
}
}
public void drawVertical(Canvas c,RecyclerView parent){
//获取分割线的左边距,即RecyclerView的padding值
final int left = parent.getPaddingLeft();
//分割线右边距
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
//遍历所有item view,为它们的下方绘制分割线
for(int i=0;i<childCount;i++){
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left,top,right,bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if(mOrientation == VERTICAL_LIST){
//设置偏移的高度是mDivider.getIntrinsicHeight,该高度正是分割线的高度
outRect.set(0,0,0,mDivider.getIntrinsicHeight());
}else{
outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
}
}
}
接下来在MainAtivity中添加下列代码,还要注释掉MainActivity.xml中的margin值(之前加margin值就是为了区分开各个item)。
rvTest.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
重新运行程序,应该可以看到下面的结果:
可以看出,出现了浅灰色的分割线。这里面用的是系统自带的分割线样式,我们完全可以使用自定义样式来取代系统的分割线,关于绘制分割线的更多知识,比如说网格布局的分割线的绘制,可以参考洋神的博客:
http://blog.csdn.net/lmj623565791/article/details/45059587
关于RecyclerView的用法记录至此,可以看出,虽然比ListView要麻烦一点,但是其自由性是非常高的,因为有很多属性都需要我们自己去实现,潜台词就是我们自己想怎么做就可以怎么做,所以RecyclerView真的是一个很强大的东西。希望自己也能慢慢变得强大。