从零开始学习RecyclerView(三)

在另一篇文章“从零开始学习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,大家可以学习一下。


如何为RecyclerView添加分割线呢,RecyclerView不像ListView 那样有divider和dividerHeight属性,估计也是为了照顾RecyclerView的高度自由性,不过我们还是可以自己去添加分割线,在“从零开始学习RecyclerView(一)”还提到了一个抽象类ItemDecoration,它是item的装饰类,

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真的是一个很强大的东西。希望自己也能慢慢变得强大。



  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值