RecyclerView项目实战之:自动吸顶

效果展示

在这里插入图片描述
接下来,你将学到:
RecyclerView基本使用
ItemDecoration
③优秀的算法思想
等知识点!

一.前期准备工作

1.将MainActivity布局改成RelativeLayout,添加RecyclerView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </androidx.recyclerview.widget.RecyclerView>

</RelativeLayout>

2.新建rv_item_star布局文件,作为itemView布局

<?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="match_parent"
    android:background="@color/purple_500">
    <TextView
        android:id="@+id/tv_star"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="20sp"
        android:gravity="center"/>
</RelativeLayout>

3.创建实体类

public class Star {
    
    
    private String name;
    private String group;

    public Star(String name, String group) {
    
    
        this.name = name;
        this.group = group;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getGroup() {
    
    
        return group;
    }

    public void setGroup(String group) {
    
    
        this.group = group;
    }
}

4.创建适配器

public class StarAdapter extends RecyclerView.Adapter<StarAdapter.StarViewHolder> {
    
    
    private Context context;
    private List<Star> starList;

    public StarAdapter(Context context, List<Star> starList) {
    
    
        this.context = context;
        this.starList = starList;
    }

    @NonNull
    @Override
    public StarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
        View view = LayoutInflater.from(context).inflate(R.layout.rv_item_star,null);
        return new StarViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull StarViewHolder holder, int position) {
    
    
        holder.tv.setText(starList.get(position).getName());
    }

    @Override
    public int getItemCount() {
    
    
        //细节
        return starList == null ? 0 : starList.size();
    }

    public class StarViewHolder extends RecyclerView.ViewHolder{
    
    
        private TextView tv;
        public StarViewHolder(@NonNull View itemView) {
    
    
            super(itemView);
            tv = itemView.findViewById(R.id.tv_star);
        }
    }
}

5.创建继承自RecyclerView.ItemDecoration的类

public class StarDecoration extends RecyclerView.ItemDecoration {
    
    
    public StarDecoration(Context context) {
    
    
    }
}

此时的MainActivity

public class MainActivity extends AppCompatActivity {
    
    
    private List<Star> starList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        RecyclerView rv = findViewById(R.id.rv_list);
        //设置布局管理器和适配器
        rv.setLayoutManager(new LinearLayoutManager(this));
        rv.setAdapter(new StarAdapter(this,starList));
        //自定义分割线
        rv.addItemDecoration(new StarDecoration(this));
    }
    //初始化,为star集合添加数据
    private void init() {
    
    
        starList = new ArrayList<>();
        for(int i = 0;i < 4;i++){
    
    
            for(int j = 0;j < 20;j++){
    
    
                if(i % 2 == 0){
    
    
                    starList.add(new Star("何炅" + j,"快乐家族" + i));
                }else {
    
    
                    starList.add(new Star("汪涵" + j,"天天兄弟" + i));
                }
            }
        }
    }
}

运行一下,看效果
在这里插入图片描述

二.介绍下ItemDecoration

1.首先看一下系统提供的抽象类

public abstract static class ItemDecoration {
    
    
    	//绘制
        public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
    
    
            onDraw(c, parent);
        }
        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
                @NonNull State state) {
    
    
            onDrawOver(c, parent);
        }

		//设置Item偏移量
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
                @NonNull RecyclerView parent, @NonNull State state) {
    
    
            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
                    parent);
        }
    }

2.看其抽象类的实现类

官方目前只提供了一个实现类DividerItemDecoration。它是跟LinearLayoutManager配套的
在这里插入图片描述
我们如果想自定义参考线,就要看一下系统是如何实现某些方法的。所以我们看一下DividerItemDecoration的源码

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    
    
  	...
    private Drawable mDivider;
    private final Rect mBounds = new Rect();

    public DividerItemDecoration(Context context, int orientation) {
    
    
		//------------------------------------------
		//------------------------------------------
		//创建了一个Drawable,后面绘制分割线也是用Drawable绘制的
		//------------------------------------------
		//------------------------------------------
        mDivider = a.getDrawable(0);
		...
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    
    
        if (parent.getLayoutManager() == null || mDivider == null) {
    
    
            return;
        }
        //------------------------------------------
		//------------------------------------------
		//判断是VERTICAL还是HORIZONTAL,然后分别执行对应的方法
		//------------------------------------------
		//------------------------------------------
        if (mOrientation == VERTICAL) {
    
    
            drawVertical(c, parent);
        } else {
    
    
            drawHorizontal(c, parent);
        }
    }

    private void drawVertical(Canvas canvas, RecyclerView parent) {
    
    
        canvas.save();
        final int left;
        final int right;
        //------------------------------------------
		//------------------------------------------
		//做了一个判断
		//------------------------------------------
		//------------------------------------------
        if (parent.getClipToPadding()) {
    
    
        	//------------------------------------------
			//------------------------------------------
			//当为true的时候,就不能在padding中进行绘制
			//------------------------------------------
			//------------------------------------------
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            canvas.clipRect(left, parent.getPaddingTop(), right,
                    parent.getHeight() - parent.getPaddingBottom());
        } else {
    
    
            left = 0;
            right = parent.getWidth();
        }

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
    
    
            final View child = parent.getChildAt(i);
            parent.getDecoratedBoundsWithMargins(child, mBounds);
            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
            final int top = bottom - mDivider.getIntrinsicHeight();
            //------------------------------------------
			//------------------------------------------
			//设置边界,然后直接绘制
			//------------------------------------------
			//------------------------------------------
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(canvas);
        }
        canvas.restore();
    }
	...

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            RecyclerView.State state) {
    
    
        //------------------------------------------
		//------------------------------------------
		//左,上,右,下
		//outRect是什么,看下面的图
		//------------------------------------------
		//------------------------------------------
        if (mDivider == null) {
    
    
            outRect.set(0, 0, 0, 0);
            return;
        }
        if (mOrientation == VERTICAL) {
    
    
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
    
    
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}


(1)源码解析之 outRect

在这里插入图片描述
outRect相当于itemView加上四周预留的空间。这个空间可以用来绘制分割线。

(2)源码解析之onDrawonDrawOver

onDraw就是说可能会被ItemView覆盖。onDrawOver就是可能会覆盖ItemView一般情况下我们都选择onDraw
在这里插入图片描述
像上图所示黄色部分就是onDraw绘制的
下面的黄色部分就是onDrawOver绘制的
在这里插入图片描述

三.实战

项目层级
在这里插入图片描述

OK,下面让我们开始正儿八经撸代码
我们先不搞自动吸顶,先把顶部那个画出来。那么问题来了,怎么判断它是顶部呢?
有人说根据position的奇偶性来判断,如果是这样,那么如果增加了item个数,那么判断的那里又要跟着改变,这样耦合度太大。所以我们选择另外一种方案,就是通过组名的变化来判断是不是顶部。

1.所以我们在StarAdapter中写出如下算法,根据position判断是不是组名位置

private boolean isGroupHeader(int position){
    
    
        if(position == 0){
    
    
            return true;
        }else {
    
    
            String currentGroupName = getGroupName(position);
            String preGroupName = getGroupName(position - 1);
            if(currentGroupName.equals(preGroupName)){
    
    
                return false;
            }else {
    
    
                return true;
            }
        }
    }
    
private String getGroupName(int position){
    
    
   	return starList.get(position).getGroup();
}

2.然后再在StarDecoration中,做出对头部(组名位置)的特殊处理

所谓特殊处理,就是:
①如果是组名位置,那么就预留出更大的空间
②如果不是组名位置,那么就预留出分割线的空间即可

private int groupHeaderHeight;
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
    super.getItemOffsets(outRect, view, parent, state);
    if(parent.getAdapter() instanceof StarAdapter){
    
    
        StarAdapter adapter = (StarAdapter) parent.getAdapter();
        //得到position
        int position = parent.getChildLayoutPosition(view);
        //判断是否为头部
        boolean isGroupHeader = adapter.isGroupHeader(position);
        if(isGroupHeader){
    
    
            //如果是头部,则预留更大的空间
            outRect.set(0,groupHeaderHeight,0,0);
        }else {
    
    
            outRect.set(0,1,0,0);
        }
    }
}
public StarDecoration(Context context) {
    
    
        groupHeaderHeight = dp2px(context,100);
}

初步效果
在这里插入图片描述

3.然后我们绘制头部

onDrawOveronDraw都可以,但是我们一般还是用onDraw
所以我们在onDraw方法中进行头部绘制(这个绘制是在上面预留空间的基础上进行的)

@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
    super.onDraw(c, parent, state);
    if(parent.getAdapter() instanceof StarAdapter){
    
    
        StarAdapter adapter = (StarAdapter) parent.getAdapter();
        //首先得到当前屏幕中的itemView的个数
        int childCount = parent.getChildCount();
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        for (int i = 0; i < childCount; i++) {
    
    
            //得到某一count(不是position)对应的itemView
            View view = parent.getChildAt(i);
            //根据这个itemView得到其position
            int position = parent.getChildLayoutPosition(view);
            //Log.v("ljh","当前position为" + position);
            //判断这个position是否为头部
            boolean isGroupHeader = adapter.isGroupHeader(position);
            if(isGroupHeader){
    
    
                c.drawRect(left,view.getTop() - groupHeaderHeight,right,view.getTop(),headPaint);
            }
        }
    }
}

效果
在这里插入图片描述

4.绘制文字

好,接下来让我们在红色区域绘制文字(只留关键代码和代码结构,之前展示过的代码就忽略了)
注意如何把文字绘制到中央的算法

private Paint textPaint;
private Rect textRect;
public StarDecoration(Context context) {
    
    
        textPaint = new Paint();
        textPaint.setTextSize(50);
        textPaint.setColor(Color.WHITE);

        textRect = new Rect();
}
 @Override
 public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
     super.onDraw(c, parent, state);
     if(parent.getAdapter() instanceof StarAdapter){
    
    
         for (int i = 0; i < childCount; i++) {
    
    
             if(isGroupHeader){
    
    
                 c.drawRect(left,view.getTop() - groupHeaderHeight,right,view.getTop(),headPaint);
                 //得到组名
                 String groupName = adapter.getGroupName(position);
                 textPaint.getTextBounds(groupName,0,groupName.length(),textRect);
                 //注意如何把文字绘制到中央的算法
                 c.drawText(groupName,left,view.getTop() - groupHeaderHeight/2 + textRect.height()/2,
                         textPaint);
             }else{
    
    
                 //绘制分割线
                 c.drawRect(left,view.getTop() - 1,right,view.getTop(),headPaint);
             }
         }
     }
 }

在这里插入图片描述

5.核心算法:onDrawOver

因为要实现吸顶效果,必须要覆盖itemView。所以要在onDrawOver里面写(关键部分都写在注释中了)

@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
    super.onDrawOver(c, parent, state);
    if(parent.getAdapter() instanceof StarAdapter){
    
    
        StarAdapter adapter = (StarAdapter) parent.getAdapter();
        //返回可见区域内的第一个item的position
        int position = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
        //获取对应position的view
        View itemView = parent.findViewHolderForAdapterPosition(position).itemView;
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int top = parent.getPaddingTop();

        //这里有个细节
        //如果下面一个组没有顶着上面那个组,那么应该一直参数不变地去绘制
        //如果下面一个组在顶着上面那个组,那么上面那个组应该改变bottom
        //所以我们要进行一个判断,就是判断下面一个组到底有没有顶着上面的组
        //而判断的依据就是当前顶部的itemView的下一个itemView是不是要顶着它的下一个组
        //具体到这里就是判断当前顶部的itemView的下一个itemView是不是也是组名位置
        boolean isGroupHeader = adapter.isGroupHeader(position + 1);
        if(isGroupHeader){
    
    
            //随时改变底部
            int bottom = Math.min(groupHeaderHeight,itemView.getBottom());
            c.drawRect(left,top,right,top + bottom,headPaint);

            //绘制文字
            //得到组名
            String groupName = adapter.getGroupName(position);
            textPaint.getTextBounds(groupName,0,groupName.length(),textRect);
            c.drawText(groupName,left,top + bottom - groupHeaderHeight/2 + textRect.height()/2,
                    textPaint);
        }else {
    
    
            //如果没有被顶着,则一直绘制最上面的组名
            c.drawRect(left,top,right,top + groupHeaderHeight,headPaint);

            //绘制文字
            //得到组名
            String groupName = adapter.getGroupName(position);
            textPaint.getTextBounds(groupName,0,groupName.length(),textRect);
            c.drawText(groupName,left,top + groupHeaderHeight/2 + textRect.height()/2,
                    textPaint);
        }
    }
}

效果
在这里插入图片描述

6.问题一:是否绘制的判断

但是这样还是存在问题的,我们试着给Recycler加一个paddingTop

在这里插入图片描述
我们就会发现如下几个问题
在这里插入图片描述

①白色区域(padding部分)还有在绘制,不管是分割线还是GroupName

这里不应该进行绘制。因为它是跟着滑动而动的,所以它归onDraw管,所以我们在onDraw中进行一个更改

@Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.onDraw(c, parent, state);
        if(parent.getAdapter() instanceof StarAdapter){
    
    
            for (int i = 0; i < childCount; i++) {
    
    
            	//-------------------------------------------------------
            	//-------------------------------------------------------
            	//下面就是进行更改的地方,即增加了一个 且 的判断
            	//-------------------------------------------------------
            	//-------------------------------------------------------
                if(isGroupHeader && view.getTop() - groupHeaderHeight - parent.getPaddingTop() >= 0){
    
    
                }else if (view.getTop() - groupHeaderHeight - parent.getPaddingTop() >= 0){
    
                
                }
            }
        }
    }
②字没有慢慢上去,而是进行一个瞬间改变

应该在onDrawOver中更改,因为是固定不变的部分
问题应该是bottom搞的不对,因为加上paddingTop 情况和不加的情况,Math.min的两个参数是不一样的

@Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.onDrawOver(c, parent, state);
        if(parent.getAdapter() instanceof StarAdapter){
    
    
            if(isGroupHeader){
    
    
                //随时改变底部
                //这里把第二个参数进行了一个更改,让他减去parent.getPaddingTop()
                int bottom = 
                Math.min(groupHeaderHeight,itemView.getBottom() - parent.getPaddingTop());
                
            }else {
    
    
            }
        }
    }

效果
在这里插入图片描述

这下两个问题都解决了

7.问题二:文字移出去了

但是,还是有一个问题
我在给RecyclerView加上背景色之后,会发现字会被“挤”上去。如图
在这里插入图片描述

解决也很简单,就是让多出去的字不显示就行了,换句话说,就是截出应该显示的地方。而且这里是在不动的地方出现bug的,所以我们应该更改onDrawOver

@Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.onDrawOver(c, parent, state);
        if(parent.getAdapter() instanceof StarAdapter){
    
    
            if(isGroupHeader){
    
    
      
                
                //--------------------------------------------------
                //下面的即为更改的地方
                //--------------------------------------------------
               	// 绘制文字的高度不能超出区域
                c.clipRect(left, top, right, top + bottom);

                c.drawText(groupName,left,top + bottom - groupHeaderHeight/2 + textRect.height()/2,
                        textPaint);
            }else {
    
    
            }
        }
    }

效果
在这里插入图片描述

四.完整代码

MainActivity

public class MainActivity extends AppCompatActivity {
    
    
    private List<Star> starList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        RecyclerView rv = findViewById(R.id.rv_list);
        //设置布局管理器和适配器
        rv.setLayoutManager(new LinearLayoutManager(this));
        rv.setAdapter(new StarAdapter(this,starList));
        //自定义分割线
        rv.addItemDecoration(new StarDecoration(this));
    }
    //初始化,为star集合添加数据
    private void init() {
    
    
        starList = new ArrayList<>();

        for(int i = 0;i < 4;i++){
    
    
            for(int j = 0;j < 20;j++){
    
    
                if(i % 2 == 0){
    
    
                    starList.add(new Star("何炅" + j,"快乐家族" + i));
                }else {
    
    
                    starList.add(new Star("汪涵" + j,"天天兄弟" + i));
                }
            }
        }
    }
}

Star

public class Star {
    
    
    private String name;
    private String group;

    public Star(String name, String group) {
    
    
        this.name = name;
        this.group = group;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getGroup() {
    
    
        return group;
    }

    public void setGroup(String group) {
    
    
        this.group = group;
    }
}

StarAdapter

public class StarAdapter extends RecyclerView.Adapter<StarAdapter.StarViewHolder> {
    
    
    private Context context;
    private List<Star> starList;

    public StarAdapter(Context context, List<Star> starList) {
    
    
        this.context = context;
        this.starList = starList;
    }
    public boolean isGroupHeader(int position){
    
    
        if(position == 0){
    
    
            return true;
        }else {
    
    
            String currentGroupName = getGroupName(position);
            String preGroupName = getGroupName(position - 1);
            if(currentGroupName.equals(preGroupName)){
    
    
                return false;
            }else {
    
    
                return true;
            }
        }
    }

    public String getGroupName(int position){
    
    
        return starList.get(position).getGroup();
    }
    @NonNull
    @Override
    public StarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
        View view = LayoutInflater.from(context).inflate(R.layout.rv_item_star,null);
        return new StarViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull StarViewHolder holder, int position) {
    
    
        holder.tv.setText(starList.get(position).getName());
    }

    @Override
    public int getItemCount() {
    
    
        //细节
        return starList == null ? 0 : starList.size();
    }

    public class StarViewHolder extends RecyclerView.ViewHolder{
    
    
        private TextView tv;
        public StarViewHolder(@NonNull View itemView) {
    
    
            super(itemView);
            tv = itemView.findViewById(R.id.tv_star);
        }
    }
}

StarDecoration

public class StarDecoration extends RecyclerView.ItemDecoration {
    
    
    private int groupHeaderHeight;
    private Paint headPaint;
    private Paint textPaint;
    private Rect textRect;

    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.onDraw(c, parent, state);
        if(parent.getAdapter() instanceof StarAdapter){
    
    
            StarAdapter adapter = (StarAdapter) parent.getAdapter();
            //首先得到当前屏幕中的itemView的个数
            int childCount = parent.getChildCount();
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();
            for (int i = 0; i < childCount; i++) {
    
    
                //得到某一count(不是position)对应的itemView
                View view = parent.getChildAt(i);
                //根据这个itemView得到其position
                int position = parent.getChildLayoutPosition(view);
                //Log.v("ljh","当前position为" + position);
                //判断这个position是否为头部
                boolean isGroupHeader = adapter.isGroupHeader(position);
                if(isGroupHeader && view.getTop() - groupHeaderHeight - parent.getPaddingTop() >= 0){
    
    
                    c.drawRect(left,view.getTop() - groupHeaderHeight,right,view.getTop(),headPaint);
                    //得到组名
                    String groupName = adapter.getGroupName(position);
                    textPaint.getTextBounds(groupName,0,groupName.length(),textRect);
                    c.drawText(groupName,left,view.getTop() - groupHeaderHeight/2 + textRect.height()/2,
                            textPaint);
                }else if (view.getTop() - groupHeaderHeight - parent.getPaddingTop() >= 0){
    
    
                    //绘制分割线
                    c.drawRect(left,view.getTop() - 1,right,view.getTop(),headPaint);
                }
            }
        }
    }

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.onDrawOver(c, parent, state);
        if(parent.getAdapter() instanceof StarAdapter){
    
    
            StarAdapter adapter = (StarAdapter) parent.getAdapter();
            //返回可见区域内的第一个item的position
            int position = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
            //获取对应position的view
            View itemView = parent.findViewHolderForAdapterPosition(position).itemView;
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();
            int top = parent.getPaddingTop();

            //这里有个细节
            //如果下面一个组没有顶着上面那个组,那么应该一直参数不变地去绘制
            //如果下面一个组在顶着上面那个组,那么上面那个组应该改变bottom
            //所以我们要进行一个判断,就是判断下面一个组到底有没有顶着上面的组
            //而判断的依据就是当前顶部的itemView的下一个itemView是不是要顶着它的下一个组
            boolean isGroupHeader = adapter.isGroupHeader(position + 1);
            if(isGroupHeader){
    
    
                //随时改变底部
                int bottom = Math.min(groupHeaderHeight,itemView.getBottom() - parent.getPaddingTop());
                c.drawRect(left,top,right,top + bottom,headPaint);

                //绘制文字
                //得到组名
                String groupName = adapter.getGroupName(position);
                textPaint.getTextBounds(groupName,0,groupName.length(),textRect);

                // 绘制文字的高度不能超出区域
                c.clipRect(left, top, right, top + bottom);

                c.drawText(groupName,left,top + bottom - groupHeaderHeight/2 + textRect.height()/2,
                        textPaint);
            }else {
    
    
                //如果没有被顶着,则一直绘制最上面的组名
                c.drawRect(left,top,right,top + groupHeaderHeight,headPaint);

                //绘制文字
                //得到组名
                String groupName = adapter.getGroupName(position);
                textPaint.getTextBounds(groupName,0,groupName.length(),textRect);
                c.drawText(groupName,left,top + groupHeaderHeight/2 + textRect.height()/2,
                        textPaint);
            }
        }
    }

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        super.getItemOffsets(outRect, view, parent, state);
        if(parent.getAdapter() instanceof StarAdapter){
    
    
            StarAdapter adapter = (StarAdapter) parent.getAdapter();
            //得到position
            int position = parent.getChildLayoutPosition(view);
            //判断是否为头部
            boolean isGroupHeader = adapter.isGroupHeader(position);
            if(isGroupHeader){
    
    
                //如果是头部
                outRect.set(0,groupHeaderHeight,0,0);
            }else {
    
    
                //虽然上面绘制了分割线,但是这里还是要写
                //因为这里是留出空间,如果这里没有把空间留出来,那么上面根本没法绘制分割线
                outRect.set(0,1,0,0);
            }
        }
    }

    public StarDecoration(Context context) {
    
    
        groupHeaderHeight = dp2px(context,100);
        headPaint = new Paint();
        headPaint.setColor(Color.RED);

        textPaint = new Paint();
        textPaint.setTextSize(50);
        textPaint.setColor(Color.WHITE);

        textRect = new Rect();
    }

    private int dp2px(Context context,float dpValue){
    
    
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue*scale*0.5f);
    }
}

res:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:background="@color/black"
        android:paddingTop="150dp"
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </androidx.recyclerview.widget.RecyclerView>

</RelativeLayout>

res:rv_item_star.xml

<?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="match_parent"
    android:background="@color/purple_500">
    <TextView
        android:id="@+id/tv_star"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="20sp"
        android:gravity="center"/>
</RelativeLayout>

猜你喜欢

转载自blog.csdn.net/afdafvdaa/article/details/115303349

相关文章