recycler view进阶

    技术2024-08-02  81

    但RecyclerView还是有很多其他的知识没有介绍。比如在入门篇我们看最终的实现效果里相邻的Item就没有分割线,这样看起来就不是很美观。一开始我以为RecyclerV没有默认的分割线,就写了一个,写完之后发现我的命名和自带默认的名字一样,这才发现原来是有默认分割线的[笑哭]。虽然RecyclerView提供默认的分割线,但是吧,那只是一条黑黑的线不好看,所以还是决定自己写一个可以变身的分割线了。

    另外一个是RecyclerView没有ListVies方便的地方,就是它没有默认的点击事件实现。 也就是我们的Item没有实现触摸反馈功能。比如单击事件和长按事件的实现。

    所以我们的进阶篇主要就学习上面的两个部分:

    (1)自定义分割线

    (2)Item的点击事件的实现

    一、自定义分割线 1.实现分割线最关键的点就是计算好在哪个位置去画一条线。更恰当的说是画一个矩形。

    (1)首先定义一个类先继承抽象类ItemDecoration,

    (2)然后重写里面几个重要的方法:onDraw()、getItemOffsets()

    2.其中onDraw()我们很熟悉了,通过 Canvas 对象绘制内容

    要注意的是:Itemdecoration的onDraw()绘制会先于ItemView的onDraw()绘制,所以如果在Itemdecoration的onDraw()中绘制的内容在ItemView边界内,就会被ItemView遮挡住。如下图:

    2.png 黄色部分区域就会被item遮挡住,这个现象被称为onDraw的overDraw现象。

    3.重写它的另外一个方法:getItemOffsets()

    (1)设置ItemView的内嵌偏移长度(inset)

    内嵌偏移长度是指:该矩形(outRect)与 ItemView的间隔

    内嵌偏移长度分为4个方向:上、下、左、右,并由outRect 中的 top、left、right、bottom参数控制。默认四个值都是0

    对照下图的左图,其中灰色区域就是内嵌偏移长度inset。当inset的四个方向都是默认值0的时候,就是右图的效果:矩形和item重叠了

    1.png (2)那么对于之前说的onerDraw现象就可以借助这个方法来解决了

    解决方案:通过getItemOffsets()设置与Item的间隔区域,从而获得与ItemView不重叠的绘制区域

    4.结合代码来看

    /**

    任意颜色的分割线 */ public class ColorDividerItemDecoration extends RecyclerView.ItemDecoration { private float mDividerHeight; private Paint mPaint;

    //item布局方式 private int mOrientation; //水平布局 public static final int HORIZONTAL_LIST= LinearLayoutManager.HORIZONTAL; //竖直布局 public static final int VERTICAL_LIST=LinearLayoutManager.VERTICAL;

    /** *默认是黑色、宽度为1的水平线 */ public ColorDividerItemDecoration(){ mPaint=new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.BLACK); mDividerHeight=1; mOrientation=HORIZONTAL_LIST; }

    /**

    @param color 分割线颜色@param mDividerHeight 分割线宽度@param mOrientation 布局方向 */ public ColorDividerItemDecoration(int color,float mDividerHeight,int mOrientation){ mPaint=new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(color); this.mDividerHeight=mDividerHeight; setOrientation(mOrientation); }

    @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); if (mOrientation==VERTICAL_LIST){ outRect.set(0,0,100,0); }else { outRect.set(0,0,0,100); } }

    @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); if(mOrientationHORIZONTAL_LIST) drawHorizontal(c,parent); else if (mOrientationVERTICAL_LIST) drawVertical(c, parent); }

    /**

    画水平的分割线

    @param c 画板

    @param parent recyclerview */ private void drawHorizontal(Canvas c, RecyclerView parent) { int childCount=parent.getChildCount(); //要给每一个item都画上线条,所以要遍历每个item的view for (int i=0;i<childCount;i++){ View view=parent.getChildAt(i); int index=parent.getChildAdapterPosition(view);

    if (index==0) continue; float dividerTop=view.getTop()-mDividerHeight; float dividerLeft=parent.getPaddingLeft(); float dividerRight=parent.getWidth()-parent.getPaddingRight(); float dividerBottom=view.getTop(); c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);

    } }

    /**

    画竖直的分割线

    @param c 画板

    @param parent recyclerview */ private void drawVertical(Canvas c, RecyclerView parent) { int childCount=parent.getChildCount(); //要给每一个item都画上线条,所以要遍历每个item的view for (int i=0;i<childCount;i++){ View view=parent.getChildAt(i); int index=parent.getChildAdapterPosition(view);

    if (index==1) continue; //RecyclerView的距离顶部padiing的距离 float dividerTop=parent.getPaddingTop(); //子view距离左边界距离减去分割线的宽度 float dividerLeft=view.getLeft()-mDividerHeight; //子view距离左边界距离 float dividerRight=view.getLeft(); //RecyclerView的高度减去距离底部的padding距离 float dividerBottom=parent.getHeight()-parent.getPaddingBottom(); c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);

    } }

    /**

    设定布局方向@param orientation */ public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException( “Invalid orientation. It should be either HORIZONTAL or VERTICAL”); } mOrientation = orientation; } } (1)在构造方法中准备数据:画笔、item的布局方式

    (2)然后就可以在onDraw方法中判断你的item布局方式从而选择画相应的分割线。

    (3)比如这里我的布局是垂直布局,那么就需要画垂直的分割线drawVertical()。

    2.jpg (4)简单分析一下drawVertical()过程:

    因为onDraw()针对RecyclerView本身,所以在使用onDraw()绘制时,需要先遍历RecyclerView的所有ItemView,分别获取它们的位置信息,然后再绘制内容。

    位置信息:

    //RecyclerView的距离顶部padiing的距离 float dividerTop=parent.getPaddingTop(); //子view距离左边界距离减去分割线的宽度 float dividerLeft=view.getLeft()-mDividerHeight; //子view距离左边界距离 float dividerRight=view.getLeft(); //RecyclerView的高度减去距离底部的padding距离 float dividerBottom=parent.getHeight()-parent.getPaddingBottom();

    位置确定完成后就可以画矩形框了,最终形成的就是我们的分割线

    c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint); 注意getItemOffsets() 针对是每一个ItemView的

    if (mOrientation==VERTICAL_LIST){ outRect.set(0,0,100,0); } 这里我设置距离左边是100单位,那么可以回到上面的效果图中看出黑色和红色分割线有一段较长的距离。

    5.自定义分割线分析完了,那么最后只需要在加入一句话就可以应用这个分割线了:

    mRecyclerView.addItemDecoration(new ColorDividerItemDecoration(Color.RED,5,LinearLayoutManager.VERTICAL)); 二、Item的点击事件 1.定义一个接口 写两个方法:单击事件和长按事件

    public interface OnItemClickListener{ void onClick(T item); void onItemLongClick(int item); } 2.写回调方法

    public void setOnClickListener(OnItemClickListener mOnClickListener){ this.mOnItemClickListener=mOnClickListener; } 3.在onBindViewHolder里面实现接口方法

    //点击事件 newsViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnItemClickListener!=null){ mOnItemClickListener.onClick(news); } } }); //长按点击事件 newsViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos=holder.getLayoutPosition(); mOnItemClickListener.onItemLongClick(pos); return false; } });

    4.在Activity里监听回调

    mNewsAdapter.setOnClickListener(new NewsAdapter.OnItemClickListener<News>() { @Override public void onClick(News item) { Toast.makeText(MainActivity.this,"点击了其中一条"+item.getNewsTitle(),Toast.LENGTH_SHORT).show(); } //监听长按事件 @Override public void onItemLongClick(final int item) { new AlertDialog.Builder(MainActivity.this) .setTitle("确认删除吗?") .setNegativeButton("取消",null) .setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mNewsAdapter.removeData(item); Toast.makeText(MainActivity.this,"已经删除了此条信息"+item,Toast.LENGTH_SHORT).show(); mNewsAdapter.notifyDataSetChanged(); } }) .show(); } });

    5.点击其中一个item效果如下

    3.jpg 到这里我们就结束了RecyclerView的小进阶,其实关于RecyclerView的其他内容还有很多,比如比较常用的时间轴效果,可能会在下一篇或下下一篇进行讲解。因为下一篇很有可能是源码解析篇哈。

    Processed: 0.014, SQL: 9