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;
    }

源碼下載地址:

點擊打開鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM