小甜点,RecyclerView 之 ItemDecoration 讲解及高级特性实践

RecyclerView中ItemDecoration的基础,粘性头部及点击事件 在工作中遇到ItemDecoration相关需求,发现自己以前学习的太过粗糙,特此重新学习记录相关知识。本文介绍了RecyclerView中ItemDecoration的相关方法,从基础的onDraw()和getItemOffsets()到进阶的onDrawOver(),最后还实现了ItemDecoration的点击事件的监听。 阅读详情

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

毫无疑问,RecyclerView 是现在 Android 世界中最重要的系统组件之一,它的出现就是为了高效代替 ListView 和 GridView。当时它的出现解决了我一个大的需求,这个需求就是在电视盒子界面上横向加载应用列表,由于 ListView 没有横向加载的功能,而网络上开源的那些 HorizontalListView 又不满足需求,所以我们只能自定义 ViewGroup 来实现需求,但是回收机制不是很完善,所以性能并不好,所以当 RecyclerView 横空出世时,我第一时间拥抱了它,并推荐 Android 开发小组成员们去了解它。

但后来,我发现 RecyclerView 除了比 ListView 好用外,某些地方它却更复杂了,它将更多的权力交给了开发者自己,比如布局,比如 ITEM 的分割线,比如点击监听等等。但总归它是好东西,所以我们得多花些时间来学习,平常开发我们一般按照 RecyclerView 的基本用法便可以实现绝大多数需求,但是某些场景下却远远不够,比如我们不想局限于 LinearLayoutManager 想自己定义 LayoutManager,我们需要定义时光轴的效果,我们想实现美妙的添加删除动画等等,这些情况下解决问题的话需要我们对 RecyclerView 本身有足够的了解。

今天,这篇文章不讲 RecyclerView 基本的知识和用法,讲它一个有趣的知识点 ItemDecoration。

ItemDecoration

Decoration 的英文意思是装饰物的意思,引申到这里来,肯定也是与 RecyclerView 的界面装饰有关。我们常见的就是分割线了。
我们在使用 ListView 的时候只要在 xml 文件中,使用 android:divider 就可以,但是很遗憾 RecyclerView 却没有相应的控制。

我们新建一个工程,然后在一个页面里面添加一个 RecyclerView。创建相关的 Adapter,加载布局文件,这里布局文件很简单,就是一个 TextView,再之后在 Activity 初始化它。

public class DividerActivity extends AppCompatActivity {
    RecyclerView mRecyclerView;
    List<String> data;
    TestAdapter mAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_divider);
        mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);
        initDatas();
        mAdapter = new TestAdapter(data);
        mRecyclerView.setAdapter(mAdapter);
        LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
        layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutmanager);
    }

    private void initDatas() {
        data = new ArrayList<>();
        for (int i = 0; i < 56;i++) {
            data.add(i+" test ");
        }
    }
}

 
  • 1

这里写图片描述

可以看到所有的选项都混在一起,为了美观应该需要 1 px 的分割线,之前我一般在 Item 的布局文件中设置它的 topMargin 或者是 bottomMargin,所以我们可以在相关的 Adapter 中这样修改。

public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item,parent,false);
        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
        layoutParams.topMargin = 1;
        view.setLayoutParams(layoutParams);
        TestHolder holder = new TestHolder(view);
        return holder;
}

 
  • 1

效果如下:

这里写图片描述

现在我们同样可以通过给 RecyclerView 添加 ItemDecoration 来实现它。

首先,我们需要自定义一个 ItemDecoration,按照目前的需求,我们只需要实现它的一个方法就可以了。

public class TestDividerItemDecoration extends RecyclerView.ItemDecoration {

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

//        //如果不是第一个,则设置top的值。
        if (parent.getChildAdapterPosition(view) != 0){
            //这里直接硬编码为1px
            outRect.top = 1;
        }
    }
}

 
  • 1

然后在 Activity 中添加它到 RecyclerView 就可以了。

mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);
initDatas();
mAdapter = new TestAdapter(data);
mRecyclerView.setAdapter(mAdapter);
LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(layoutmanager);
mRecyclerView.addItemDecoration(new TestDividerItemDecoration());
 
  • 1

效果如图:

这里写图片描述

getItemOffsets()

我们可以看到自定义的 TestDividerItemDeoration 只实现了一个方法 getItemOffsets()。方法里面有四个参数。

  • Rect outRect
  • View view
  • RecyclerView parent
  • RecyclerView.State state

这四个参数分别干什么的呢?我们不妨在 AndroidStudio 中按 Ctrl 键点击方法名,就可以到了它被调用的位置。

Rect getItemDecorInsetsForChild(View child) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    if (!lp.mInsetsDirty) {
        return lp.mDecorInsets;
    }

    if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
        // changed/invalid items should not be updated until they are rebound.
        return lp.mDecorInsets;
    }
    final Rect insets = lp.mDecorInsets;
    insets.set(0, 0, 0, 0);
    final int decorCount = mItemDecorations.size();
    for (int i = 0; i < decorCount; i++) {
        mTempRect.set(0, 0, 0, 0);
        //在这里被调用
        mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
        insets.left += mTempRect.left;
        insets.top += mTempRect.top;
        insets.right += mTempRect.right;
        insets.bottom += mTempRect.bottom;
    }
    lp.mInsetsDirty = false;
    return insets;
}

 
  • 1

我们注意这一行代码 java mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState); 很容易得知,outRect 是一个全为 0 的 Rect。view 指 RecyclerView 中的 Item。parent 就是 RecyclerView 本身,state 就是一个状态。

我们可以看下面的这张图。

这里写图片描述

绿色区域代表 RecyclerView 中的一个 ItemView,而外面橙色区域也就是相应的 outRect,也就是 ItemView 与其它组件的偏移区域,等同于 margin 属性,通过复写 getItemOffsets() 方法,然后指定 outRect 中的 top、left、right、bottom 就可以控制各个方向的间隔了。注意的是这些属性都是偏移量,是指偏移 ItemView 各个方向的数值。在上面的例子中我设置了 outRect.top = 1; 所以每个 ItemView 之间有 1 px 的空隙,而这 1 px 空隙透露了下面背景色,所以看起来就像是分隔线,这实现了简单的分隔线效果,但这种方法分隔线的效果只能取决于背景色,如果我要定制分割线的颜色呢?这个时候就要讲到一个新的方法名 onDraw()。

onDraw()

在 Android 中的每一个 View 中 onDraw() 是很重要的一个方法,用来绘制组件的UI效果,所以在 ItemDecocration 中它自然也是用来绘制外观的。我们来看它的方法声明。
java public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);

可以看到它传递了一个 Canvas 参数对象,所以它拥有了绘制的能力。但是怎么绘制呢?

其实它是配合了前面的 getItemOffsets 方法一起使用的,getItemOffsets 撑开了 ItemView 的上下左右间隔区域,而 onDraw 方法通过计算每个 ItemView 的坐标位置与它的 outRect 值来确定它要绘制内容的区间。

假设,我们要设计一个高度为 2 px 的红色分割线,那么我们就需要在每个 ItemView top位置上方画一个 2 px 高度的矩形,然后填充颜色为红色。
需要注意的一点是 getItemOffsets 是针对每一个 ItemView,而 onDraw 方法却是针对 RecyclerView 本身,所以在 onDraw 方法中需要遍历屏幕上可见的 ItemView,分别获取它们的位置信息,然后分别的绘制对应的分割线。

我们看下面的这张示意图
这里写图片描述

为了便于观察我将第一条分割线的颜色透明化了,我们可以看到每条分割线绘制的区域其实就是 outRect.top 至 ItemView.top 之间的区域,所以我们就需要在当初 getOffsets 方法进行位置偏移时就记录下每个 itemView 向上的间隔距离,之后的逻辑就是遍历屏幕上的 View,然后描绘分割线。

public class ColorDividerItemDecoration extends RecyclerView.ItemDecoration {

    private float mDividerHeight;

    private Paint mPaint;

    public ColorDividerItemDecoration() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

//        //第一个ItemView不需要在上面绘制分割线
        if (parent.getChildAdapterPosition(view) != 0){
            //这里直接硬编码为1px
            outRect.top = 1;
            mDividerHeight = 1;
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        int childCount = parent.getChildCount();

        for ( int i = 0; i < childCount; i++ ) {
            View view = parent.getChildAt(i);

            int index = parent.getChildAdapterPosition(view);
            //第一个ItemView不需要绘制
            if ( index == 0 ) {
                continue;
            }

            float dividerTop = view.getTop() - mDividerHeight;
            float dividerLeft = parent.getPaddingLeft();
            float dividerBottom = view.getTop();
            float dividerRight = parent.getWidth() - parent.getPaddingRight();

            c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);
        }
    }
}

 
  • 1

然后我们在 Activity 将 ColorDividerItemDecoration 添加到相应的 RecyclerView 中就可以了。

mRecyclerView.addItemDecoration(new ColorDividerItemDecoration());
 
  • 1

效果如下图:
这里写图片描述

至此,红色的分割线就搞定了。

但一定要注意的是,onDraw 方法可不只能绘制简单的线条,它可是拥有 Canvas 的,所以画圆、画矩形、画弧形、绘制图片都不在话下。为了提高本篇代码的技术含量,下面我们通过 ItemDecoration 来实现一个时光轴的效果。

通过 ItemDecoration 实现时光轴的效果

编码的开始先做设计,或者说先思考。思考我们要做什么,或者说要怎么做。
这里写图片描述

我们可以看到左边白色的图案就大概是我们时光轴要绘制的图形。我们通过 getItemOffsets 方法来对 ItemView 进行 left 和 top 的间距设置。然后确定好轴线的起始坐标,中间轴结点的图形或者是图案。我们可以通过 ItemView 将相应的时光轴片断分解,如下图。
这里写图片描述

主要是一些参数的确定,例如 DividerHeight,注意这个 DividerHeight 不是指 ItemView 向上的间隔值,而是相应的 ItemDecoration 的高度。中心坐标 (centerX,centerY),还有上下两段轴线的起始坐标。有了这些参数后,我们就能轻松地编码了。

public class TimelineItemDecoration extends RecyclerView.ItemDecoration {


    private Paint mPaint;
    //ItemView左边的间距
    private float mOffsetLeft;
    //ItemView右边的间距
    private float mOffsetTop;
    //时间轴结点的半径
    private float mNodeRadius;

    public TimelineItemDecoration(Context context) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);

        mOffsetLeft = context.getResources().getDimension(R.dimen.timeline_item_offset_left);
        mNodeRadius = context.getResources().getDimension(R.dimen.timeline_item_node_radius);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

//        //第一个ItemView不需要在上面绘制分割线
        if (parent.getChildAdapterPosition(view) != 0){
            //这里直接硬编码为1px
            outRect.top = 1;
            mOffsetTop = 1;
        }

        outRect.left = (int) mOffsetLeft;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        int childCount = parent.getChildCount();

        for ( int i = 0; i < childCount; i++ ) {
            View view = parent.getChildAt(i);

            int index = parent.getChildAdapterPosition(view);

            float dividerTop = view.getTop() - mOffsetTop;
            //第一个ItemView 没有向上方向的间隔
            if ( index == 0 ) {
                dividerTop = view.getTop();
            }

            float dividerLeft = parent.getPaddingLeft();
            float dividerBottom = view.getBottom();
            float dividerRight = parent.getWidth() - parent.getPaddingRight();

            float centerX = dividerLeft + mOffsetLeft / 2;
            float centerY = dividerTop + (dividerBottom - dividerTop) / 2;

            float upLineTopX = centerX;
            float upLineTopY = dividerTop;
            float upLineBottomX = centerX;
            float upLineBottomY = centerY - mNodeRadius;

            //绘制上半部轴线
            c.drawLine(upLineTopX,upLineTopY,upLineBottomX,upLineBottomY,mPaint);

            //绘制时间轴结点
            c.drawCircle(centerX,centerY,mNodeRadius,mPaint);

            float downLineTopX = centerX;
            float downLineTopY = centerY + mNodeRadius;
            float downLineBottomX = centerX;
            float downLineBottomY = dividerBottom;

            //绘制上半部轴线
            c.drawLine(downLineTopX,downLineTopY,downLineBottomX,downLineBottomY,mPaint);
        }
    }
}
 
  • 1

然后效果如下图:

这里写图片描述
感觉不怎么美观,我们尝试将结点的实心圆改成空心圆。

//绘制时间轴结点
mPaint.setStyle(Paint.Style.STROKE);
c.drawCircle(centerX,centerY,mNodeRadius,mPaint);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 
  • 1

效果如下:
这里写图片描述

感觉美观了许多。

同时,我们可以将轴结点用图标代替圆或者圆圈。
这里写图片描述

上面的图标感觉更好看了。

需要注意的是 onDraw 方法,ItemDecoration 是在 ItemView 的下方绘制的,也就是 ItemView 可能会覆盖 ItemDecoration 的内容。我们可以验证一下,在时间轴与 ItemView 的边界画一个完整的圆,观察它的效果。

     mPaint.setStyle(Paint.Style.STROKE);
    c.drawCircle(view.getLeft(),centerY,mNodeRadius,mPaint);

    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

 
  • 1

这里写图片描述
可以看到,在重合的地方,圆圈确实被 ItemView 内容覆盖了。

大家可能会想到,ItemDecoration 内容能不能覆盖在 ItemView 内容之上呢?

答案是肯定的,但不是在 onDraw() 方法实现,而是另外一个方法 onDrawOver()。

onDrawOver 和角标。

现实中的APP或者网站经常有一些排行榜比如下面:

这里写图片描述
或者这样。

这里写图片描述

这些角标都是绘制在 ItemView 之上的,现在有了 ItemDecoration 我们也可以轻松而优雅地实现它。

比如我们要实现一个图书销量排行榜。我们有大概的草图。

这里写图片描述

然后我们就可以编码了。
布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content"
    android:background="@android:color/white">

    <TextView
        android:id="@+id/tv_rank_oder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="24dp"
        android:gravity="center"
        android:textColor="#6c6c6c"/>

    <ImageView
        android:id="@+id/iv_cover"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_centerVertical="true"
        android:layout_marginLeft="60dp"/>

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_cover"
        android:layout_marginLeft="12dp"
        android:layout_marginTop="12dp"
        android:textColor="@android:color/black"/>
    <TextView
        android:id="@+id/tv_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_cover"
        android:layout_below="@id/tv_title"
        android:layout_marginLeft="12dp"
        android:layout_marginTop="12dp"
        android:textColor="@android:color/holo_red_dark"/>
</RelativeLayout>

 
  • 1

相应的 Adapter 代码:

public class BookRankAdapter extends RecyclerView.Adapter<BookRankAdapter.TestHolder> {

    List<String> data;
    int[] mIconResouces;
    public BookRankAdapter(List<String> data,int[] ids) {
        this.data = data;
        this.mIconResouces = ids;
    }

    public void setData(List<String> data,int[] ids) {
        this.data = data;
        mIconResouces = ids;
        notifyDataSetChanged();
    }

    @Override
    public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_ranklist_item,parent,false);
        TestHolder holder = new TestHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(TestHolder holder, int position) {
        if (data != null && data.size() > 0 ) {
            String text = data.get(position);

            String[] infos = text.split("-");
            holder.tvOrder.setText(position+"");
            holder.tvTitle.setText(infos[0]);
            holder.tvPrice.setText(infos[1]);

            holder.ivCover.setImageResource(mIconResouces[position]);
        }
    }


    @Override
    public int getItemCount() {
        return data == null ? 0 : data.size();
    }

    static class TestHolder extends  RecyclerView.ViewHolder{
        public TextView tvOrder;
        public TextView tvTitle;
        public TextView tvPrice;
        public ImageView ivCover;
        public TestHolder(View itemView) {
            super(itemView);

            tvOrder = (TextView) itemView.findViewById(R.id.tv_rank_oder);
            tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
            tvPrice = (TextView) itemView.findViewById(R.id.tv_price);
            ivCover = (ImageView) itemView.findViewById(R.id.iv_cover);

        }

    }
}


 
  • 1

自定义 FlagItemDecoration

public class FlagItemDecoration extends RecyclerView.ItemDecoration {
    private Paint mPaint;
    private Bitmap mIcon;
    private float mFlagLeft;

    public FlagItemDecoration(Context context) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);


        mIcon = BitmapFactory.decodeResource(context.getResources(),R.drawable.hotsale);
        mFlagLeft = context.getResources().getDimension(R.dimen.flag_left);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
      //  super.getItemOffsets(outRect, view, parent, state);

        //第一个ItemView不需要在上面绘制分割线
        if (parent.getChildAdapterPosition(view) == 0){
            outRect.top = 0;
        } else {
            outRect.top = 2;
        }

    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);

        int childCount = parent.getChildCount();

        for ( int i = 0; i < childCount; i++ ) {
            View view = parent.getChildAt(i);
            int index = parent.getChildAdapterPosition(view);
            float top = view.getTop();
            if ( index < 3 ) {
                c.drawBitmap(mIcon,mFlagLeft,top,mPaint);
            }

        }
    }
}

 
  • 1

然后在 Activity 进行相应的数据处理,这里的数据都是为了测试用的,所以比较随意。

public class BookRankActivity extends AppCompatActivity {
    RecyclerView mRecyclerView;
    List<String> data;
    BookRankAdapter mAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bookrank);
        mRecyclerView = (RecyclerView) findViewById(R.id.bookrank_recyclerview);


        initDatas();
        int resouces[] = new int[] {R.drawable.book_renmin,R.drawable.book_huochetou,
            R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming
            ,R.drawable.book_renmin,R.drawable.book_huochetou,
                R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming
            ,R.drawable.book_renmin,R.drawable.book_huochetou,
                R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming
        };
        mAdapter = new BookRankAdapter(data,resouces);
        mRecyclerView.setAdapter(mAdapter);
        LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
        layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutmanager);
        mRecyclerView.addItemDecoration(new FlagItemDecoration(this));
    }

    private void initDatas() {
        data = new ArrayList<>();
        data.add("人民的名义- ¥ 33.5");
        data.add("火车头 - ¥ 27.5");
        data.add("解忧杂货店- ¥ 19.9");
        data.add("TensorFlow - ¥ 102.5");
        data.add("王阳明心学 - ¥ 60");

        data.add("人民的名义1- ¥ 33.5");
        data.add("火车头1 - ¥ 27.5");
        data.add("解忧杂货店1- ¥ 19.9");
        data.add("TensorFlow1 - ¥ 102.5");
        data.add("王阳明心学1 - ¥ 60");

        data.add("人民的名义2 - ¥ 33.5");
        data.add("火车头2 - ¥ 27.5");
        data.add("解忧杂货店2- ¥ 19.9");
        data.add("TensorFlow2 - ¥ 102.5");
        data.add("王阳明心学2 - ¥ 60");
    }
}

 
  • 1

最终效果如下图:

这里写图片描述

有人在想,通过 ItemView 中的布局文件不就可以完成这样的操作吗?是的,确实是可以的,将 Flag 角标定义在每一个 ItemView 布局文件中,然后在 Adapter 的 onBindViewHolder 方法中根据 postion 的值来决定是否加载角标。

但是这里是为了说明 ItemDecoration 中的 onDrawOver 方法,为了说明它确实能让 ItemDecoration 图像绘制在 ItemView 内容之上。事实上,ItemDecoration 的妙处还有好多好多。

总结

自定义一个 ItemDecoration 通常要根据需要,复写它的 3 个方法。
* getItemOffsets 撑开 ItemView 上、下、左、右四个方向的空间
* onDraw 在 ItemView 内容之下绘制图形
* onDrawOver 在 ItemView 内容之上绘制图形。

提醒

由于文章篇幅,ItemDecoration 最让我兴奋的内容我需要另写一篇文章,那就是通过 ItemDecoration 自定义 RecyclerView 中的头部或者是粘性头部。相信大家对头部这个概念比较了解,现在通过 ItemDecoration 就可以优雅地实现它,记住优雅两个字,条条大路通罗马,但是有人就优雅、有人就显得手忙脚乱。所以,我将文章标题定了一个词,叫小甜点,吃了让人舒心,ItemDecoration 用了也让人舒心。

好了,文章就到这里结束。

完整源码地址

github地址

更有意思的内容请看这篇《RecyclerView探索之通过ItemDecoration实现StickyHeader效果》

RecyclerView 源码分析(九)ItemDecoration深入解析 转载Carson_Ho RecyclerView 源码分析(九)深入解析 RecyclerView.ItemDecoration类前言目录1. ItemDecoration类 简介1.1 定义1.2 作用2. 具体使用2.1 getItemOffsets()2.1.1 作用2.1.2 具体使用2.1.3 源码分析总结2.2 onDraw()2.2.1 作用2.2.2 具体使用2.2.3 特别注意2.2.4 应用场景2.2.5 实例讲解2.2.6 结果展示2.2.7 源码地址2.3 onDrawOver()2 阅读详情

相关推荐

RecyclerView高级使用详解(多套布局,加载更多,监听)

原文/番茄And鸡蛋(简书作者) 原文链接:http://www.jianshu.com/p/7385e2bbb365 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。效果图实现原理1.多套布局利用RecyclerView的特性==》必须实现的ViewHolder类,在定义RecyclerView.Adapter时,在内部实现多个ViewHolder的类,根据不同需求的布局,获取到

snake_son的博客 1079

ItemDecoration用法

1、ItemDecoration概念 在使用RecyclerView显示列表的时候需要分隔线隔开item,此时则可以使用Recycler的内部类ItemDecoration。ItemDecoration是android系统提供的基类,用于绘制RecyclerView的分隔线,我们可以通过继承ItemDecoration实现丰富的分隔线效果。(系统也提供了实现好的DividerItemDecoration使用。) 2、ItemDecoration的使用 使用ItemDecoration时,需继承此类并覆写其中

wh义华的专栏 4522

一种高超声速飞行器弹道的仿真方法

设计并实现了一个高超声速飞行器弹道仿真算法,并应用到基于OSGEarth的态势中。

eyepeak的博客 6542

RecyclerView 之 ItemDecoration 讲解及高级特性实践

RecyclerView 之 ItemDecoration

QG 2120

Vmware Vcenter7.0证书web续期发生错误

生成证书,选择第三个(PS,如果没有域名的,一定要写IP,不然很容易卡在85%,服务不能起来)这里大概率可能是证书里面的FQDN和主机不匹配,又或者是主机解析FQDN有问题。vCenter _MACHINE_CERT快到期了,通过web界面更新证书失败。vSphere Client 版本 7.0.2.00200。登陆ssh到vcenter,重新生成证书。先关掉HA,不然证书管理会报错。第一步先这样,重新续订一下证书。

m0_74823595的博客 3946

脑机接口(BCI)从原理到开发实战 10|脑控外设:用脑电波控制机械臂/游戏

摘要: 脑机接口(BCI)的“最后一公里”是将分类结果转化为实际动作的关键环节。本文介绍如何通过指令映射、硬件通信和执行反馈实现脑控外设,包括离散/连续控制策略、防抖处理及串口/蓝牙通信方法。通过两个实战案例(运动想象控制机械臂抓取、P300/SSVEP控制游戏)和完整项目(OpenBCI控制小车),演示了从分类标签到设备动作的完整流程,重点涵盖指令生成算法(多数投票)、Python与Arduino的串口通信集成,为BCI系统落地提供实用解决方案。(149字)

小明的博客 766

RecyclerView探索之通过ItemDecoration实现StickyHeader效果

我在上一篇《小甜点,RecyclerView 之 ItemDecoration 讲解及高级特性实践 》 讲解了 ItemDecoration 的基本用法及它的一些实践,抱着学习研究的态度,这一篇作为实践篇主要目的是尝试通过 ItemDecoration 来实现 RecyclerView 中的 StickyHeader 功能。 关于 StickyHeader 想必大家已经很清楚了,如果不有不清楚的,

frank 的专栏 3万+

手把手教你用.NET Reactor 7.3.0保护跨平台.NET应用(含macOS/Rider配置)

本文详细介绍了如何使用.NET Reactor 7.3.0为跨平台.NET应用提供代码保护。文章重点涵盖了在macOS环境下的GUI工具安装、与JetBrains Rider的深度集成配置,并针对Avalonia UI框架提供了专项保护策略和避坑指南,帮助开发者有效防止代码被反编译和破解。

coffee的博客 594

android_基础_recyclerview之ItemDecoration详解

转载自:https://www.jishudog.com/9055/html 一、简述 说到RecyclerView大家都很熟悉了,相比于ListView,它具有高度解耦、性能优化等优势,而且现在大多数安卓开发者都已经将RecyclerView用来完全替代ListView和GridView,因为它功能十分强大,但往往功能强大的东西,反而不太好控制,例如今天要说的这个ItemDecoration,ItemDecoration是条目装饰,下面来看看它的强大吧。 二、使用ItemDecoration绘制分割线.

写bug的蚊子的博客 2326

MATLAB实战:利用扫频数据重构系统伯德图的关键步骤

本文详细介绍了如何利用MATLAB处理扫频数据并重构系统伯德图的完整实战流程。内容涵盖数据导入与清洗、增益与相位的精确计算、频率响应数据对象(frd)的构建、数据平滑与插值处理,以及最终生成专业伯德图并进行关键参数(如相位裕度)标注的方法。文章重点解决了从实测“黑盒子”系统数据出发,获取直观频率响应特性曲线的核心问题,为控制系统分析与设计提供了实用指南。

weixin_29098117的博客 398

AI游戏制作07——波次计时器,敌人按波次生成

AI游戏制作07——波次计时器,敌人按波次生成

qq_40572023的博客 47

RecyclerView ItemDecoration 完全解析

RecyclerView ItemDecoration 解析 我们都知道,使用 RecyclerView 时 ,我们不能像 ListView 那样通过 setDivider() 的方式来设置分割线,好在 Android 为我们提供了定制性更强的 ItemDecoration 来为 RecyclerView 设置分割线。 什么是 ItemDecoration ? 顾名思义 ItemDecoratio...

Star的博客 6744

数字逻辑Fundamentals of Digital Logic with Verilog Design | 3rd Edition Solutins Chapter 3(step by step)

第三章3-1~3-27全部习题解析和代码模块

weixin_62651190的博客 1946

Android的RecyclerView.ItemDecoration使用

文章目录前言一、RecyclerView.ItemDecoration是什么?二、使用步骤1.引入库2.设置装饰器2.1 在Activity或者Fragment中设置装饰器2.2 实现MaintenanceItemDecoration,继承于RecyclerView.ItemDecoration()1、实现getItemOffsets方法,代码如下2、给底部留白部分加上节点,实现onDraw方法3、MaintenanceItemDecoration完整代码3.完成最终版带进度更新的装饰器3.1 设置装饰器3

Silicon_Valley 3559

Android AVB 分析(十二)嵌入式设备安全中的 dm-verity 简介

上一篇 《Android AVB 分析(十一)bootloader 是如何进行 verify boot 检查的?》 分析了 bootloader 中是如何调用 libavb 函数库验证 vbmeta 和 boot 等分区的。接下来就打算写一篇介绍 dm-verity 原理的文章,然后再写一篇徒手 dm-verity 实践的文章,结果偶然发现了本篇的英文原版。写得太好,我觉得自己完全没有必要再写原理了。由于原文是英文,有些英语不太好的朋友不一定能完全理解。我花了点时间,结合 AI 将英语原文翻译成中文。

洛奇看世界 3598

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

RecyclerView系列之二ItemDecoration

recyclerview itemDecoration

QG 1144

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【笔记】ItemDecoration——网格布局GridItemDecoration

RecyclerView可以说是ListView和GridView的升级综合版,在Android5.0推出,其特点如下: 1、支持不同方向、不同排版模式,实现多种数据展示形式(包含ListView、GridView、瀑布流); 2、装载了ViewHolder的回收机制,无需我们考虑View的复用; 3、取消了OnItemClick点击事件,需自己实现; 4、可控制Item增删动画; 5、...

林zero的专栏 6925

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

ItemDecoration解析(一) getItemOffsets

介绍 An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter's data set. This can be useful for drawing dividers between items, hig

Data的博客 5043

RecyclerView的分割线Decoration,五种实现方式,线性布局展示

上面三张图片中的item分割线就是效果图 一、首先定义LinearLayout布局xml文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:lay...

凌云志 1486

RecyclerView之ItemDecoration详解

转载地址:http://blog.youkuaiyun.com/shangmingchao【商明超的博客】 关于RecyclerView的ItemView装饰,之前一直用官方Demo的DividerItemDecoration,并没有认真地去理解ItemDecoration的用法,也没能体会到ItemDecoration的强大,直到要用到横向的RecyclerView,而且最左边的和最右边的Item

qq_15796477的博客 423

Android RecyclerView 使用完全解析 体验艺术般的控件

转载请标明出处: http://blog.youkuaiyun.com/lmj623565791/article/details/45059587; 本文出自:【张鸿洋的博客】 概述 RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。 据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实...

weixin_30390075的博客 2759

Android RecyclerView-使用Itemdecoration实现粘性头部功能,详细到具体步骤.

一 前言 该文详细的介绍了RecyclerView.ItemDecoration实现分组粘性头部的功能,让我们自己生产代码,告别代码搬运工的时代.另外文末附有完整Demo的连接.看下效果: 二 知识准备 RecyclerView.ItemDecoration对于我们最熟悉的功能就是给RecyclerView实现各种各样自定义的分割线了,实现分割线的功能其实和实现粘性头部的功能大同小异,那我们就来...

weixin_33744854的博客 266
上一篇: 如何优雅地从浏览器打开本地应用
下一篇: ClipDrawable让开发更简单
Ray534
博客等级 码龄12年 2粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值