• Android上使用RecyclerView实现顶部悬浮标题效果的Sticky Title View


    目前很多的项目都在使用顶部悬浮标题的效果,很明显,这的确是一个比较人性化,用户体验效果比较好的UI交互效果,对于这个效果,有很多种实现方式,如果说要用RecyclerView来实现一个分类信息展示,并且要用到顶部标题栏悬浮显示当前分类的效果,应该怎么实现呢?

    先来看个效果。

     

    大概就这么一个效果,建议大家看效果时候,忽略左边的list列表,因为没有做关联,左边就是一个摆设。

    实现这么一个效果,需要怎么去分析和实习那呢,其实从这样一个效果来看,我个人应该会先想到这样几个问题:

    1. 这种不规则的GridView的是如何实现的?

    2.使用RecyclerView实现不规则GridView,每个item之间的分割距离又是如何控制实现的?

    3. 这种顶部悬浮效果又是如何实现的?

    这是两个主要的问题。
    OK,我还是主要围绕这两个问题,一步一步来实现。

    一、首先这个不规则的GridView是如何实现的呢?

    我们知道首先RecyclerView在使用的时候,可以设置一个LayoutManager,LayoutManager常用的有LinearLayoutManager和GridLayoutManager,

    从名字上来说,很明显,前者是更偏向实现一个横纵向的ListView,而后者更倾向实现横纵向的GridView。

    那么无疑,今天主角肯定是GridLayoutManager了。

    说到这里,必须要贴出今天涉及到布局文件了。

    主界面的MainActivity.java的布局文件,很简单第一个RecyclerView就是左边的List,这个可以忽略,其实就是一个摆设。

    我们今天的实现都在这个FrameLayout中,这个会在代码中填充成一个Fragment。

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#f0f0f0"
        android:orientation="horizontal"
        android:weightSum="4">
     
        <android.support.v7.widget.RecyclerView
            android:id="@+id/left_class_recyvler"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />
     
     
        <FrameLayout
            android:id="@+id/fragment"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="3" />
     
    </LinearLayout>

    那么接下来就来贴一下Fragment的xml布局文件吧。

    detail_fragment_layout.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/detail_fragment_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
     
        <android.support.v7.widget.RecyclerView
            android:id="@+id/detail_recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </android.support.v7.widget.RecyclerView>
     
    </LinearLayout>

    也是一样极其简单,就一个RecyclerView,今天实现这个功能就主要跟这个RecycerView打交道了。

    OK,布局文件准备好了,那我们肯定要准备Grid中item 的布局文件了,可是大家通过上面的图片可以看出,里面其实是有两中不同风格的布局文件的,一个绿色的标题,

    一个是黄色的item,那么这样个情况下,毫无疑问我们是要准备两个item布局文件的,一个是title的布局文件,一个普通的item布局文件。

    普通item的layout布局文件:

    common_rv_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:background="@android:color/holo_orange_dark"
        android:orientation="vertical">
     
        <TextView
            android:id="@+id/textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center" />
     
     
    </LinearLayout>

    title类型的laytout布局文件:

    detail_title.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="vertical">
     
        <TextView
            android:layout_gravity="center"
            android:id="@+id/title_textview"
            android:textSize="20sp"
            android:gravity="left|center_vertical"
            android:background="#09a33d"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
     
    </LinearLayout>

    当然还有描述grid item的java bean对象。

    package com.zhg.views.stickytitlerecyclerview;
     
    /**
     * Created by User on 2017/7/21.
     */
     
    public class DetailBean {
        private boolean isTitle;
        private String name;
        private String tag=null;
     
        public boolean isTitle() {
            return isTitle;
        }
     
        public void setTitle(boolean title) {
            isTitle = title;
        }
     
        public String getName() {
            return name;
        }
     
        public void setName(String name) {
            this.name = name;
        }
     
        public String getTag() {
            return tag;
        }
     
        public void setTag(String tag) {
            this.tag = tag;
        }
    }

    用一个变量isTitle来标记,item是普通item还是title,tag是用来标记item是属于那个分类的(如上图的,分类1,分类2......分类n)。

    OK,现在基本铺路已经完成了,各个布局文件都已经出来了,剩下的就是脑补逻辑代码了。

    当然这里,肯定就是先实现一个Adapter了咯,那么这个adapter该怎么写呢?

    package com.zhg.views.stickytitlerecyclerview;
     
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
     
    import java.util.ArrayList;
    import java.util.List;
    import java.util.zip.Inflater;
     
    /**
     * Created by User on 2017/7/21.
     */
     
    public abstract  class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
        protected List<T> mList = new ArrayList<>();
        protected OnItemClickListener mListener;
        protected Context mContext;
        public final static int ITEM_VIEW_TYPE_TITLE = 1;
        public final static int ITEM_VIEW_TYPE_NORMAL = 0;
        protected LayoutInflater mInflater;
     
     
        public void setOnItemClickListener(OnItemClickListener listener){
            this.mListener = listener;
        }
     
        public BaseRecyclerAdapter(Context context,List<T> list){
            mInflater = LayoutInflater.from(context);
            this.mContext = context;
            if(list!=null)
            this.mList = list;
        }
     
        public abstract BaseViewHolder getHolder(View itemView,int itemType,OnItemClickListener listener);
     
        public abstract int getLayoutId(int viewType);
     
        @Override
        public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = mInflater.inflate(getLayoutId(viewType),parent,false);
            return getHolder(itemView,viewType,mListener);
        }
     
        @Override
        public void onBindViewHolder(BaseViewHolder holder, int position) {
            holder.bindViewHolder(mList.get(position));
        }
     
        @Override
        public int getItemViewType(int position) {
            return 0;
        }
     
        @Override
        public int getItemCount() {
            return mList == null ? 0 : mList.size();
        }
     
    }

    可以看到,我们首先写可一个抽象的BaseRecyclerAdapter.java的,很明显,我们真正的Adapter是要去继承这个抽象的Adapter
    抽象类很简单咯,无非定义了两个抽象方法,让子类去做具体实现了,一个是getLayoutId()方法,这理所当然是让子类提供具体的layout布局id索引了,另一个是getHolder(View itemView,int itemType,OnItemClickListener listener),用来创建ViewHodler的接口,这个也是需要子类去实现,很正常,毕竟每个RecyclerView 的Adapter的的ViewHolder都可能不一样,所以让子类去做具体实现了。

    package com.zhg.views.stickytitlerecyclerview;
     
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.View;
    import android.view.ViewGroup;
     
    import java.util.ArrayList;
    import java.util.List;
     
    /**
     * Created by User on 2017/7/21.
     */
     
    public class DetailRecyclerViewAdapter extends BaseRecyclerAdapter<DetailBean> {
        public DetailRecyclerViewAdapter(Context context ,List<DetailBean> list){
            super(context,list);
        }
     
     
     
        @Override
        public int getItemViewType(int position) {
            if(position >=0 && position < mList.size()){
                    return  mList.get(position).isTitle() ? ITEM_VIEW_TYPE_TITLE : ITEM_VIEW_TYPE_NORMAL;
                }
                return ITEM_VIEW_TYPE_NORMAL;
        }
     
     
     
     
     
        @Override
        public int getItemCount() {
            return mList == null ? 0 : mList.size();
        }
     
        @Override
        public BaseViewHolder getHolder(View itemView,int viewType, OnItemClickListener listener) {
            return new DetailViewHolder(itemView,viewType,listener);
        }
     
        @Override
        public int getLayoutId(int viewType) {
            return viewType == ITEM_VIEW_TYPE_TITLE  ? R.layout.detail_title : R.layout.common_rv_item;
        }
     
        public class DetailViewHolder extends BaseViewHolder<DetailBean>{
     
            public DetailViewHolder(View itemView,int viewType,OnItemClickListener listener) {
                super(itemView,viewType, listener);
            }
     
            @Override
            public void bindViewHolder(DetailBean detailBean) {
                    mTextView.setText(detailBean.getName());
            }
        }
    }

    可以看到,构造方法中需要传递一个泛型的List集合,List集合数据元素类型必须为DetailBean类型的
    这里主要看getItemViewType这个方法,在这个方法中,其实这个方法很好理解,这个方法根据position,从list集合中获取DetailBean,判断这个对象是否为title类型,如果是的话就返回title类型的int类型标记,,如果不是就返回普通类型的int类型标记。
    然后就是实现覆盖上面所述的两个抽象方法,也是根据itemViewType标记来返回相应的layout布局文件。

    其他的无非就是创建ViewHolder了,其他的没有什么可讲的。

    接下来我们在fragment中,创建adapter,初始化ReceyclerView对象,初始化数据,给RecyclerView设置adapter,看看运行之后是什么效果呢。

    package com.zhg.views.stickytitlerecyclerview;
     
    import android.app.Activity;
    import android.content.Context;
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v4.app.Fragment;
    import android.support.v7.widget.DividerItemDecoration;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
     
    import java.util.ArrayList;
    import java.util.List;
     
    /**
     * Created by User on 2017/7/21.
     */
     
    public class DetailFragment extends Fragment {
        private  RecyclerView mRecyclerView;
        private DetailRecyclerViewAdapter mAdapter;
        private List<DetailBean> mList;
        private GridLayoutManager mLayoutManager;
        public DetailFragment(){
           initData();
        }
     
        private void initData() {
            mList = new ArrayList<>();
            for(int i = 0 ; i < 15 ; i++){
                DetailBean bean = new DetailBean();
                bean.setTitle(true);
                bean.setTag(i+"");
                bean.setName("分类"+i);
                mList.add(bean);
                for(int j = 0 ; j < 10 ; j++){
                    bean = new DetailBean();
                    bean.setTitle(false);
                    bean.setTag(i+"");
                    bean.setName(j+"");
                    mList.add(bean);
                }
            }
        }
     
     
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = initView(inflater,container);
            mRecyclerView = (RecyclerView) view.findViewById(R.id.detail_recycler);
            mAdapter = new DetailRecyclerViewAdapter(getActivity(),mList);
            mLayoutManager = new GridLayoutManager(getActivity(),3);
     
            mRecyclerView.setLayoutManager(mLayoutManager);
            mRecyclerView.setAdapter(mAdapter);
            return view;
        }
     
        private View initView(LayoutInflater inflater, @Nullable ViewGroup container) {
            View view  = inflater.inflate(R.layout.detail_fragment_layout,container,false);
            return view;
        }
    }

    我们可以看到,initData中初始化了数据,初始化了一个list集合,装的都是DetailBean对象,也就是每一个item,包括title类型的,然后就是findViewById找到RecyclerView,初始化adapter,给RecyvlerView设置adapter,设置了一个GridLayoutManager类型的布局管理器,在创建布局管理的时候,第二个参数,传递了一个3,这个就是说,这个Grid是个3列的grid,其他的没啥好解释的,直接运行看看什么效果把。

    ok,效果出来了,但是有一个很明显的问题,似乎除了有个绿色的,字体大点,item之外,其他的都是一样,该怎么实现我们最终效果之中的那种不规则的那种效果呢?

    这里面就有一个小技巧了,我之前转载过一篇博客,里面也重点介绍了这个小技巧,对这个还不清楚的,点击如下博客链接进行补充,如下:

    http://blog.csdn.net/q178266871/article/details/50716502
    那就是GridLayoutManager.setSpanSizeLookup方法,这个方法需要传入一个SpanSizeLookup对象,这个类会有一个必须实现的方法,getSpanSize(int position),有了这个position,可以对指定position的item的span大小进行控制,我们上面的代码,可以看出,我们设置了3个列,其实也就是3个span的空间,那么我们要实现这个效果,似乎也很简单了,我们利用这个接口,根据position拿到DetailBean对象,判断对象的isTitle是否为标题,如果是标题,那就让这个position对应的item占3个span就行了,其余的占一个span就行了,我们把这个加上试试效果吧。

    @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = initView(inflater,container);
            mRecyclerView = (RecyclerView) view.findViewById(R.id.detail_recycler);
            mAdapter = new DetailRecyclerViewAdapter(getActivity(),mList);
            mLayoutManager = new GridLayoutManager(getActivity(),3);
            mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    return mList.get(position).isTitle()?3:1;
                }
            });
     
            mRecyclerView.setLayoutManager(mLayoutManager);
            mRecyclerView.setAdapter(mAdapter);
            return view;
        }

    OK,这个效果看起来是没有问题了。

    二、使用RecyclerView实现不规则GridView,每个item之间的分割距离又是如何控制实现的?

    那么上面的效果我们看到了,大致的那种不规则的Grid已经实现了,但是现在这种item之间的分割是怎么实现呢?

    我们知道RecyclerView,是可以设置一个RecyclerView.ItemDecoration来设置分割效果,所以毫无疑问,我们自己实现一个分割Decoration来设置给RecyclerView。

    package com.zhg.views.stickytitlerecyclerview;
     
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Rect;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
     
    import java.util.List;
     
    /**
     * Created by User on 2017/7/25.
     */
     
    public class GridDividerItemDecoration extends RecyclerView.ItemDecoration {
        private Context mContext;
        private List<DetailBean> mList;
     
        public GridDividerItemDecoration(Context context, List<DetailBean> list) {
            super();
            mContext = context;
            mList = list;
     
        }
     
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
        }
     
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            if (parent.getLayoutManager() instanceof GridLayoutManager) {
                int position = parent.getChildAdapterPosition(view);
                DetailBean bean = mList.get(position);
                if (bean != null && bean.isTitle()) {
                    outRect.set(0, 0, 0, 0);
                } else if ("0".equals(bean.getName()) || "1".equals(bean.getName())) {
                    outRect.set(0, 5, 5, 0);
                } else if ("2".equals(bean.getName()) || "5".equals(bean.getName()) || "8".equals(bean.getName())) {
                    outRect.set(0, 5, 0, 0);
                } else if ("9".equals(bean.getName())) {
                    outRect.set(0, 5, 5, 5);
                } else {
                    outRect.set(0, 5, 5, 0);
                }
            } else if (parent.getLayoutManager() instanceof LinearLayoutManager) {
                outRect.set(0, 0, 0, 5);
            } else {
                super.getItemOffsets(outRect, view, parent, state);
            }
        }
     
        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
        }
    }

    看以看到,其他几个覆盖的方法,我们都没有做具体处理,我们主要在getItemOffsets方法中对每个item的上下左右偏移做了处理。

    这个我们也是利用的DetailBean这个对象来实现,用这个方式实现还是比较简单的。

    具体什么逻辑和方法,大家结合这个接口和前面的效果图看看,哈哈哈,对于getIemOffsets如何实现item之间的排列和分割,可以看看大神的博客对这个原理的说明,博客地址如下:http://blog.csdn.net/shangmingchao/article/details/51456319

    @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = initView(inflater,container);
            mRecyclerView = (RecyclerView) view.findViewById(R.id.detail_recycler);
            mAdapter = new DetailRecyclerViewAdapter(getActivity(),mList);
            mLayoutManager = new GridLayoutManager(getActivity(),3);
            mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    return mList.get(position).isTitle()?3:1;
                }
            });
            mRecyclerView.addItemDecoration(new GridDividerItemDecoration(getActivity(),mList));
            mRecyclerView.setLayoutManager(mLayoutManager);
            mRecyclerView.setAdapter(mAdapter);
            return view;
        }

    三. 这种顶部悬浮效果又是如何实现的?

    那么这种效果如何实现呢,其实RecyclerView是可以设置多个RecyclerView.ItemDecoration的,那么我们可以另外再自己写一个RecyclerView.ItemDecoration来实现绘制顶部的悬浮标题了。

    package com.zhg.views.stickytitlerecyclerview;
     
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Rect;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.text.TextUtils;
    import android.util.Log;
    import android.util.TypedValue;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
     
    import java.lang.reflect.Type;
    import java.util.List;
     
    /**
     * Created by User on 2017/7/24.
     */
     
    public class ItemHeaderDecoration extends RecyclerView.ItemDecoration {
        private Context mContext;
        private List<DetailBean> mList;
        public static String currentTag = "0";
        private LayoutInflater mLayoutInflater;
        private int mTitleHight = 50;
        private int mTitleTextSize = 20;
        public ItemHeaderDecoration(Context context , List<DetailBean> dataList) {
            super();
            this.mContext = context;
            this.mList = dataList;
            mTitleHight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50,context.getResources().getDisplayMetrics());
           // mTitleTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics());
            this.mLayoutInflater = LayoutInflater.from(mContext);
        }
     
        public static void setCurrentTag(String tag){
            ItemHeaderDecoration.currentTag = tag;
        }
     
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
        }
     
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
        }
     
        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            //获取到视图中第一个可见的item的position
            int position = ((GridLayoutManager)parent.getLayoutManager()).findFirstVisibleItemPosition();
            String tag = mList.get(position).getTag();
            View child = parent.findViewHolderForLayoutPosition(position).itemView;
            boolean flag = false;
            if((position+1) < mList.size()){
                String suspensionTag = mList.get(position+1).getTag();
                if(null!=tag && !tag.equals(suspensionTag)){
                    Log.d("ZHG-TEST","!!!!!!!!!!!!!child.Height() = "+child.getHeight()+" , child.top = "+child.getTop()+" , mTitleHight = "+mTitleHight);
                    if(child.getHeight()+child.getTop() < mTitleHight){
                        c.save();
                        flag = true;
                        c.translate(0,child.getHeight()+child.getTop()-mTitleHight);
                    }
                }
            }
            View topTitleView = mLayoutInflater.inflate(R.layout.detail_title,parent,false);
            TextView textView = (TextView) topTitleView.findViewById(R.id.title_textview);
            //textView.setTextSize(mTitleTextSize);
            textView.setText("分类"+tag);
            int toDrawWidthSpec;//用于测量的widthMeasureSpec
            int toDrawHeightSpec;//用于测量的heightMeasureSpec
            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) topTitleView.getLayoutParams();
            if (lp == null) {
                lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);//这里是根据复杂布局layout的width height,new一个Lp
                topTitleView.setLayoutParams(lp);
            }
            topTitleView.setLayoutParams(lp);
            if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                //如果是MATCH_PARENT,则用父控件能分配的最大宽度和EXACTLY构建MeasureSpec。
                toDrawWidthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.EXACTLY);
            } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
                //如果是WRAP_CONTENT,则用父控件能分配的最大宽度和AT_MOST构建MeasureSpec。
                toDrawWidthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(), View.MeasureSpec.AT_MOST);
            } else {
                //否则则是具体的宽度数值,则用这个宽度和EXACTLY构建MeasureSpec。
                toDrawWidthSpec = View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY);
            }
            //高度同理
            if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
                toDrawHeightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(), View.MeasureSpec.EXACTLY);
            } else if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                toDrawHeightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom(), View.MeasureSpec.AT_MOST);
            } else {
                toDrawHeightSpec = View.MeasureSpec.makeMeasureSpec(mTitleHight, View.MeasureSpec.EXACTLY);
            }
            //依次调用 measure,layout,draw方法,将复杂头部显示在屏幕上。
            topTitleView.measure(toDrawWidthSpec, toDrawHeightSpec);
            topTitleView.layout(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getPaddingLeft() + topTitleView.getMeasuredWidth(), parent.getPaddingTop() + topTitleView.getMeasuredHeight());
            topTitleView.draw(c);//Canvas默认在视图顶部,无需平移,直接绘制
            if (flag)
                c.restore();//恢复画布到之前保存的状态
            if (!TextUtils.equals(tag, currentTag)) {
                currentTag = tag;
            }
     
        }
    }

    在这里想说一下,绘制ItemDecoration了。onDraw()的绘制会先于ItemView的绘制,所以如果你在onDraw()方法中绘制的东西在ItemView边界内,就会被ItemView盖住。而onDrawOver()会在ItemView绘制之后再绘制,所以如果你在onDrawOver()方法中绘制的东西在ItemView边界内,就会盖住ItemView。简单点说,就是先执行ItemDecoration的onDraw()、再执行ItemView的onDraw()、再执行ItemDecoration的onDrawOver()。由于和RecyclerView使用的是同一个Canvas,所以你想在Canvas上画什么都可以,就像我们平时自定义View时写onDraw()方法一样。 
    所以我们要做悬浮title,那么首先肯定是在onDrawOver()中来实现了,这样的话,我们就能在Grid上面绘制一个浮起来的Title,但是不影响grid的itemview。
    ,最后把这个RecyclerView.ItemDecoration设置给RecyclerView,就能实现这个效果了。

    @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = initView(inflater,container);
            mRecyclerView = (RecyclerView) view.findViewById(R.id.detail_recycler);
            mAdapter = new DetailRecyclerViewAdapter(getActivity(),mList);
            mLayoutManager = new GridLayoutManager(getActivity(),3);
            mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    return mList.get(position).isTitle()?3:1;
                }
            });
            mRecyclerView.addItemDecoration(new GridDividerItemDecoration(getActivity(),mList));
            mRecyclerView.addItemDecoration(new ItemHeaderDecoration(getActivity(),mList));
            mRecyclerView.setLayoutManager(mLayoutManager);
            mRecyclerView.setAdapter(mAdapter);
            return view;
        }

    源码下载地址:

    点击打开链接

  • 相关阅读:
    prometheus,alertmanager 报警配置详解
    使用 kubeadm 搭建 kubernetes1.10 集群
    kibana-sentinl-监控报警
    ELK集群模式部署
    mongo 误操作恢复数据
    mongo 实时同步工具 mongosync
    移动端巨坑——iphone6Plus默认设置不使用sessionStorage
    iphone6 Plus seesionStorage失效
    移动端手势拖拽排序神器Sortable.js
    vue使用swiper(转)
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/9802552.html
Copyright © 2020-2023  润新知