当前位置: 代码迷 >> 综合 >> RecyclerView 学习之吸顶
  详细解决方案

RecyclerView 学习之吸顶

热度:48   发布时间:2023-10-15 09:17:11.0

RecyclerView 学习之吸顶

RecyclerView 学习之吸顶

RecyclerView 学习之吸顶 

上面两个效果图,图一里面我为了展现吸顶原理,我给吸顶加了背景,图二是取消背景的效果图。

我直接上相关代码,然后再进行解释:

这个activity 的 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"><com.scwang.smartrefresh.layout.SmartRefreshLayoutandroid:id="@+id/smart_layout"android:layout_width="match_parent"android:layout_height="match_parent"><com.scwang.smartrefresh.layout.header.ClassicsHeaderandroid:layout_width="match_parent"android:layout_height="wrap_content" /><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/xd_rcv"android:layout_width="match_parent"android:layout_height="match_parent" /><tyj.com.testlib.xd.StickyHeadContainerandroid:id="@+id/stick_layout"android:layout_width="match_parent"android:layout_height="wrap_content"><!--android:background="#0a0"--><FrameLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:background="#ccc"><!--android:background="#a0a"--><TextViewandroid:id="@+id/xd_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="test"android:textColor="#fff"android:textSize="16sp" /><!--android:background="#00a"--><Viewandroid:layout_width="match_parent"android:layout_height="1dp" /></FrameLayout></tyj.com.testlib.xd.StickyHeadContainer></FrameLayout></com.scwang.smartrefresh.layout.SmartRefreshLayout></RelativeLayout>

xml 里面的StickHeadContainer 的代码:

import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;import tyj.com.yedashenlib.log.CyLogger;/*** @author ChenYe created by on 2019/3/20 0020. 16:27**/public class StickyHeadContainer extends ViewGroup {private static final String TAG = "StickyHeadContainer";private int mOffset,mLeft,mRight,mTop,mBottom;private int mLastOffset = Integer.MIN_VALUE;private int mLastStickyHeadPosition = Integer.MIN_VALUE;private DataCallback mDataCallback;public StickyHeadContainer(Context context) {this(context, null);}public StickyHeadContainer(Context context, AttributeSet attrs) {this(context, attrs, 0);}public StickyHeadContainer(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO: 2017/1/9 屏蔽点击事件}});}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int desireHeight;int desireWidth;int count = getChildCount();if (count != 1) {throw new IllegalArgumentException("只允许容器添加1个子View!");}final View child = getChildAt(0);// 测量子元素并考虑外边距measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);// 获取子元素的布局参数final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();// 计算子元素宽度,取子控件最大宽度desireWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;// 计算子元素高度desireHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;CyLogger.e(TAG,"desireWidth1:"+desireWidth);// 考虑父容器内边距desireWidth += (getPaddingLeft() + getPaddingRight());desireHeight += (getPaddingTop() + getPaddingBottom());CyLogger.e(TAG,"desireWidth2:"+desireWidth);// 尝试比较建议最小值和期望值的大小并取大值desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());// 设置最终测量值CyLogger.e(TAG,"desireWidth3:"+desireWidth);setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec), resolveSize(desireHeight, heightMeasureSpec));}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {final View child = getChildAt(0);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int paddingLeft = getPaddingLeft();final int paddingTop = getPaddingTop();mLeft = paddingLeft + lp.leftMargin;mRight = child.getMeasuredWidth() + mLeft;mTop = paddingTop + lp.topMargin + mOffset;mBottom = child.getMeasuredHeight() + mTop;child.layout(mLeft, mTop, mRight, mBottom);}// 生成默认的布局参数@Overrideprotected LayoutParams generateDefaultLayoutParams() {return super.generateDefaultLayoutParams();}// 生成布局参数,将布局参数包装成我们的@Overrideprotected LayoutParams generateLayoutParams(LayoutParams p) {return new MarginLayoutParams(p);}// 生成布局参数,从属性配置中生成我们的布局参数@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);}// 查当前布局参数是否是我们定义的类型这在code声明布局参数时常常用到@Overrideprotected boolean checkLayoutParams(LayoutParams p) {return p instanceof MarginLayoutParams;}public void scrollChild(int offset) {if (mLastOffset != offset) {mOffset = offset;ViewCompat.offsetTopAndBottom(getChildAt(0), mOffset - mLastOffset);}mLastOffset = mOffset;}public int getChildHeight() {return getChildAt(0).getHeight();}public void onDataChange(int stickyHeadPosition) {if (mDataCallback != null && mLastStickyHeadPosition != stickyHeadPosition) {mDataCallback.onDataChange(stickyHeadPosition);}mLastStickyHeadPosition = stickyHeadPosition;}public void reset() {mLastStickyHeadPosition = Integer.MIN_VALUE;}public interface DataCallback {void onDataChange(int pos);}public void setDataCallback(DataCallback dataCallback) {mDataCallback = dataCallback;}
}

然后几个adapter , XdAdapter -> BaseStickAdapter ->BaseDataAdapter :这三个adapter 写到一起

 

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;import java.util.ArrayList;
import java.util.List;import tyj.com.yedashenlib.log.CyLogger;
import tyj.com.yedashenlib.toast.ToastUtil;
import tyj.com.yedashenlib.widget.recylerview.holder.SuperViewHolder;
import tyj.com.yedashenlib.widget.recylerview.multitype.MultiTypeEntity;/*** @author ChenYe*         created by on 2017/11/17 0017. 09:52*         <p>*         这个是单列的数据baseAdapter:如果你用这个自定义的recyclerView的话,你的使用场景是跟listview*         差不多的效果,就是单列的话,就可以写一个adapter来继承这个BaseAdapter,在你写的adapter只*         需要对控件设置数据就可以了。这个BaseAdapter的使用范例的adapter是DataAdapter。**/public abstract class BaseDataAdapter<T> extends RecyclerView.Adapter<SuperViewHolder> {private static final String TAG = "BaseDataAdapter";protected List<T> mDataList = new ArrayList<>();@Overridepublic int getItemCount() {return mDataList == null ? 0 : mDataList.size();}@Overridepublic int getItemViewType(int position) {if (null != mDataList && !mDataList.isEmpty()) {T t = mDataList.get(position);if (t instanceof MultiTypeEntity) {return ((MultiTypeEntity) t).getItemType();}}return super.getItemViewType(position);}@Overridepublic SuperViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if (setLayout(viewType) instanceof Integer) {View view = View.inflate(parent.getContext(), ((Integer) setLayout(viewType)), null);return new SuperViewHolder(view);} else if (setLayout(viewType) instanceof View) {return new SuperViewHolder(((View) setLayout(viewType)));}return null;}@Overridepublic void onBindViewHolder(SuperViewHolder holder, int position) {if (null == mDataList || mDataList.isEmpty() || position > mDataList.size() - 1) {return;}onBindItemHolder(holder, position, mDataList.get(position));}//这个是用来解决局部刷新数据的@Overridepublic void onBindViewHolder(SuperViewHolder holder, int position, List<Object> payloads) {if (payloads.isEmpty()) {onBindViewHolder(holder, position);} else {//主要是这里onBindItemHolder(holder, position, payloads);}}/*** 这个是继承这个BaseAdapter需要传进来的item布局,可以传资源id,可以传view** @param type 多类型,如果你是单一的类型,这里就传0就好了* @return*/protected abstract Object setLayout(int type);/*** 这个是让继承这个BaseAdapter去实现的,然后在这个方法里面去把数据设置到item的* 控件上去的** @param holder   holder* @param position position* @param itemData item数据*/protected abstract void onBindItemHolder(SuperViewHolder holder, int position, T itemData);public void onBindItemHolder(SuperViewHolder holder, int position, List<Object> payloads) {}/*** 向外部提供的传递数据到adapter里面刷新数据用的** @param data 传一个集合进来,那么代表是想刷新整个adapter的数据*/public void setDataList(List<T> data) {mDataList.clear();mDataList.addAll(data);notifyDataSetChanged();}/*** 向外部提供插入一个集合数据到adapter使用的** @param data*/public void addDataList(List<T> data) {mDataList.addAll(data);notifyDataSetChanged();}/*** 在最后面添加一条数据,然后局部刷新一下** @param data*/public void insertLast(T data) {mDataList.add(data);notifyItemInserted(getItemCount());}/*** 返回点击的实体的数据,这个数据是从adapter里面的集合拿到这个数据,position也是adapter里面的,* 这样拿出来的数据才是最安全的。** @param position* @return*/public T getItemData(int position) {if (position < mDataList.size()) {return mDataList.get(position);}return null;}/*** 删除指定的那一条数据** @param position*/public void remove(int position) {this.mDataList.remove(position);notifyItemRemoved(position);if (position != getDataList().size()) {notifyItemRangeChanged(position, this.mDataList.size() - position);}}/*** 删除指定区间的*/public void removeBegin(int beginPosition, int endPosition) {try {if (mDataList.size() > beginPosition && mDataList.size() > endPosition) {for (int i = beginPosition; i < endPosition; i++) {mDataList.remove(beginPosition);}}notifyItemMoved(beginPosition, endPosition);notifyItemRangeChanged(beginPosition, this.mDataList.size() - beginPosition);} catch (Exception e) {ToastUtil.newInstance().showToast("出错了!");CyLogger.e(TAG, e.getMessage());}}/****/public List<T> getDataList() {return mDataList;}}
import android.support.v7.widget.RecyclerView;import tyj.com.yedashenlib.widget.recylerview.adapter.BaseDataAdapter;
import tyj.com.yedashenlib.widget.recylerview.holder.SuperViewHolder;/*** @author ChenYe created by on 2019/3/21 0021. 08:38**/public abstract class BaseStickyAdapter<T> extends BaseDataAdapter<T> {/*** title 、 normal,为了抽取,下面的两个类型定义我还是写在了BaseStickyAdapter里面,如果你后续还要增加类型,可以* 自己写在继承BaseStickyAdapter的类里面*/public static final int TYPE_TITLE = 2, TYPE_NORMAL = 3;@Overridepublic void onAttachedToRecyclerView(RecyclerView recyclerView) {super.onAttachedToRecyclerView(recyclerView);FullSpanUtil.onAttachedToRecyclerView(recyclerView, this, TYPE_TITLE);}@Overridepublic void onViewAttachedToWindow(SuperViewHolder holder) {super.onViewAttachedToWindow(holder);FullSpanUtil.onViewAttachedToWindow(holder, this, TYPE_TITLE);}
}
import android.view.View;import tyj.com.testlib.R;
import tyj.com.testlib.entity.XdNormalEntity;
import tyj.com.testlib.entity.XdTitleEntity;
import tyj.com.testlib.xd.BaseStickyAdapter;
import tyj.com.yedashenlib.widget.recylerview.holder.SuperViewHolder;
import tyj.com.yedashenlib.widget.recylerview.multitype.MultiTypeEntity;/*** @author ChenYe created by on 2019/3/21 0021. 08:33*         TODO 对title 的点击事件,我这里暂时只写了一个,如果你的吸顶布局里面想设置更多的点击事件,你可以自行设置**/public class XdAdapter extends BaseStickyAdapter<MultiTypeEntity> {private OnXdTitleClickListener mXdClickListener = null;/*** 这里有很多点击事件需要设置,要根据实际情况,目前我这里有两个点击事件,所以这个item点击事件有两个,我就* 直接写点击事件1和点击事件2了** @param listener*/public void setOnItemClickListener(OnXdTitleClickListener listener) {mXdClickListener = listener;}@Overrideprotected Object setLayout(int type) {int resId;switch (type) {case BaseStickyAdapter.TYPE_TITLE:resId = R.layout.item_multi_type_1;break;case BaseStickyAdapter.TYPE_NORMAL:resId = R.layout.item_multi_type_2;break;//如果要在多类型的基础上再实现多类型,可以在这里加casedefault:resId = R.layout.item_multi_type_1;break;}return resId;}@Overrideprotected void onBindItemHolder(SuperViewHolder holder, int position, MultiTypeEntity itemData) {if (holder.getItemViewType() == BaseStickyAdapter.TYPE_TITLE) {final String titleName = ((XdTitleEntity) itemData).getTitleName();holder.setText(R.id.type_1_tv, titleName);if (null != mXdClickListener) {holder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mXdClickListener.titleClick("点击了吸顶的顶部title:" + titleName);}});}} else {final String desc = ((XdNormalEntity) itemData).getDesc();holder.setText(R.id.type_2_tv, desc);if (null != mXdClickListener) {holder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mXdClickListener.clickItem("点击了普通item:" + desc);}});}}}public interface OnXdTitleClickListener {/*** 可以在这里自行添加并列的事件** @param title msg*/void titleClick(String title);/*** 普通item点击事件** @param msg*/void clickItem(String msg);}
}

然后BaseStickAdapter 里面用到了一个Util :

import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.ViewGroup;/*** @author ChenYe created by on 2019/3/21 0021. 08:41**/
public class FullSpanUtil {public static void onAttachedToRecyclerView(RecyclerView recyclerView, final RecyclerView.Adapter adapter, final int pinnedHeaderType) {// 如果是网格布局,这里处理标签的布局占满一行final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();if (layoutManager instanceof GridLayoutManager) {final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;final GridLayoutManager.SpanSizeLookup oldSizeLookup = gridLayoutManager.getSpanSizeLookup();gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {if (adapter.getItemViewType(position) == pinnedHeaderType) {return gridLayoutManager.getSpanCount();}if (oldSizeLookup != null) {return oldSizeLookup.getSpanSize(position);}return 1;}});}}public static void onViewAttachedToWindow(RecyclerView.ViewHolder holder, RecyclerView.Adapter adapter, int pinnedHeaderType) {// 如果是瀑布流布局,这里处理标签的布局占满一行final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {final StaggeredGridLayoutManager.LayoutParams slp = (StaggeredGridLayoutManager.LayoutParams) lp;slp.setFullSpan(adapter.getItemViewType(holder.getLayoutPosition()) == pinnedHeaderType);}}}

adapter的 两个xml ,按照先后顺序是type_1的 ,第二个是type_2的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="wrap_content"><TextViewandroid:id="@+id/type_1_tv"android:layout_width="match_parent"android:layout_height="50dp"android:background="#ccc"android:gravity="center"android:text="test"android:textColor="#fff"android:textSize="16sp" /></RelativeLayout>
<?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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_vertical"android:orientation="horizontal"><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@mipmap/ic_launcher" /><TextViewandroid:id="@+id/type_2_tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="40dp"android:gravity="center"android:text="test"android:textSize="16sp" /></LinearLayout>
</RelativeLayout>

activity代码 :

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.TextView;import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;import java.util.ArrayList;
import java.util.List;import io.reactivex.annotations.NonNull;
import tyj.com.testlib.adapter.XdAdapter;
import tyj.com.testlib.entity.XdNormalEntity;
import tyj.com.testlib.entity.XdTitleEntity;
import tyj.com.testlib.xd.BaseStickyAdapter;
import tyj.com.testlib.xd.StickyHeadContainer;
import tyj.com.testlib.xd.StickyItemDecoration;
import tyj.com.yedashenlib.toast.ToastUtil;
import tyj.com.yedashenlib.widget.recylerview.multitype.MultiTypeEntity;/*** @author ChenYe created by on 2019/3/20 0020. 14:46*         需要注意,你的StickyHeadContainer 布局需要跟吸顶布局一直,不然会有显示**/public class XdActivity extends Activity {private static final String TAG = "XdActivity";private TextView mXdTitleTv;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_xd);final SmartRefreshLayout sm = (SmartRefreshLayout) findViewById(R.id.smart_layout);mXdTitleTv = (TextView) findViewById(R.id.xd_tv);RecyclerView rcv = (RecyclerView) findViewById(R.id.xd_rcv);rcv.setLayoutManager(new LinearLayoutManager(this));final StickyHeadContainer stickHeader = (StickyHeadContainer) findViewById(R.id.stick_layout);rcv.addItemDecoration(new StickyItemDecoration(stickHeader, BaseStickyAdapter.TYPE_TITLE));final XdAdapter adapter = new XdAdapter();rcv.setAdapter(adapter);adapter.setOnItemClickListener(new XdAdapter.OnXdTitleClickListener() {@Overridepublic void titleClick(String title) {ToastUtil.newInstance().showToast(title);}@Overridepublic void clickItem(String msg) {ToastUtil.newInstance().showToast(msg);}});stickHeader.setDataCallback(new StickyHeadContainer.DataCallback() {@Overridepublic void onDataChange(int pos) {XdTitleEntity item = (XdTitleEntity) adapter.getItemData(pos);mXdTitleTv.setText(item.getTitleName());}});sm.setOnRefreshListener(new OnRefreshListener() {@Overridepublic void onRefresh(@NonNull RefreshLayout refreshLayout) {adapter.setDataList(createList());sm.finishRefresh();}});sm.autoRefresh(200, 500, 1, false);adapter.setDataList(createList());}private List<MultiTypeEntity> createList() {List<MultiTypeEntity> list = new ArrayList<>();list.add(new XdTitleEntity("测试title 1"));for (int i = 0; i < 4; i++) {list.add(new XdNormalEntity("测试文本1_" + i));}list.add(new XdTitleEntity("测试title 2"));for (int i = 0; i < 8; i++) {list.add(new XdNormalEntity("测试文本2_" + i));}list.add(new XdTitleEntity("测试title 3"));for (int i = 0; i < 15; i++) {list.add(new XdNormalEntity("测试文本3_" + i));}return list;}
}

activity 里面用到的 ItemDecoration 、 两个实体 、实体里面继承的interface :

import android.graphics.Canvas;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;/*** @author ChenYe created by on 2019/3/21 0021. 09:30**/
public class StickyItemDecoration extends RecyclerView.ItemDecoration {private static final String TAG = "StickyItemDecoration";private int mStickyHeadType;private int mFirstVisiblePosition;private int mStickyHeadPosition;private int[] mInto;private RecyclerView.Adapter mAdapter;private StickyHeadContainer mStickyHeadContainer;private boolean mEnableStickyHead = true;public StickyItemDecoration(StickyHeadContainer stickyHeadContainer, int stickyHeadType) {mStickyHeadContainer = stickyHeadContainer;mStickyHeadType = stickyHeadType;}// 当我们调用mRecyclerView.addItemDecoration()方法添加decoration的时候,RecyclerView在绘制的时候,去会绘制decorator,即调用该类的onDraw和onDrawOver方法,// 1.onDraw方法先于drawChildren// 2.onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。// 3.getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {super.onDraw(c, parent, state);checkCache(parent);if (mAdapter == null) {// checkCache的话RecyclerView未设置之前mAdapter为空return;}calculateStickyHeadPosition(parent);if (mEnableStickyHead && mFirstVisiblePosition >= mStickyHeadPosition && mStickyHeadPosition != -1) {View belowView = parent.findChildViewUnder(c.getWidth() / 2, mStickyHeadContainer.getChildHeight() + 0.01f);mStickyHeadContainer.onDataChange(mStickyHeadPosition);int offset;if (isStickyHead(parent, belowView) && belowView.getTop() > 0) {offset = belowView.getTop() - mStickyHeadContainer.getChildHeight();} else {offset = 0;}mStickyHeadContainer.scrollChild(offset);mStickyHeadContainer.setVisibility(View.VISIBLE);
//            CyLogger.e(TAG, "吸顶可见,高度:" + mStickyHeadContainer.getHeight() + ",宽度:" + mStickyHeadContainer.getWidth());} else {mStickyHeadContainer.reset();mStickyHeadContainer.setVisibility(View.INVISIBLE);
//            CyLogger.e(TAG, "吸顶不可见");}}public void enableStickyHead(boolean enableStickyHead) {mEnableStickyHead = enableStickyHead;if (!mEnableStickyHead) {mStickyHeadContainer.setVisibility(View.INVISIBLE);}}private void calculateStickyHeadPosition(RecyclerView parent) {final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();//        mFirstCompletelyVisiblePosition = findFirstCompletelyVisiblePosition(layoutManager);// 获取第一个可见的item位置mFirstVisiblePosition = findFirstVisiblePosition(layoutManager);// 获取标签的位置,int stickyHeadPosition = findStickyHeadPosition(mFirstVisiblePosition);if (stickyHeadPosition >= 0 && mStickyHeadPosition != stickyHeadPosition) {// 标签位置有效并且和缓存的位置不同mStickyHeadPosition = stickyHeadPosition;}}/*** 从传入位置递减找出标签的位置** @param formPosition* @return*/private int findStickyHeadPosition(int formPosition) {for (int position = formPosition; position >= 0; position--) {// 位置递减,只要查到位置是标签,立即返回此位置final int type = mAdapter.getItemViewType(position);if (isStickyHeadType(type)) {return position;}}return -1;}/*** 通过适配器告知类型是否为标签** @param type* @return*/private boolean isStickyHeadType(int type) {return mStickyHeadType == type;}/*** 找出第一个可见的Item的位置** @param layoutManager* @return*/private int findFirstVisiblePosition(RecyclerView.LayoutManager layoutManager) {int firstVisiblePosition = 0;if (layoutManager instanceof GridLayoutManager) {firstVisiblePosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();} else if (layoutManager instanceof LinearLayoutManager) {firstVisiblePosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();} else if (layoutManager instanceof StaggeredGridLayoutManager) {mInto = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(mInto);firstVisiblePosition = Integer.MAX_VALUE;for (int pos : mInto) {firstVisiblePosition = Math.min(pos, firstVisiblePosition);}}return firstVisiblePosition;}/*** 找出第一个完全可见的Item的位置** @param layoutManager* @return*/private int findFirstCompletelyVisiblePosition(RecyclerView.LayoutManager layoutManager) {int firstVisiblePosition = 0;if (layoutManager instanceof GridLayoutManager) {firstVisiblePosition = ((GridLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();} else if (layoutManager instanceof LinearLayoutManager) {firstVisiblePosition = ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition();} else if (layoutManager instanceof StaggeredGridLayoutManager) {mInto = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];((StaggeredGridLayoutManager) layoutManager).findFirstCompletelyVisibleItemPositions(mInto);firstVisiblePosition = Integer.MAX_VALUE;for (int pos : mInto) {firstVisiblePosition = Math.min(pos, firstVisiblePosition);}}return firstVisiblePosition;}/*** 检查缓存** @param parent*/private void checkCache(final RecyclerView parent) {final RecyclerView.Adapter adapter = parent.getAdapter();if (mAdapter != adapter) {mAdapter = adapter;// 适配器为null或者不同,清空缓存mStickyHeadPosition = -1;mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {@Overridepublic void onChanged() {reset();}@Overridepublic void onItemRangeChanged(int positionStart, int itemCount) {reset();}@Overridepublic void onItemRangeChanged(int positionStart, int itemCount, Object payload) {reset();}@Overridepublic void onItemRangeInserted(int positionStart, int itemCount) {reset();}@Overridepublic void onItemRangeRemoved(int positionStart, int itemCount) {reset();}@Overridepublic void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {reset();}});}}private void reset() {mStickyHeadContainer.reset();}/*** 查找到view对应的位置从而判断出是否标签类型** @param parent* @param view* @return*/private boolean isStickyHead(RecyclerView parent, View view) {final int position = parent.getChildAdapterPosition(view);if (position == RecyclerView.NO_POSITION) {return false;}final int type = mAdapter.getItemViewType(position);return isStickyHeadType(type);}}
import tyj.com.testlib.xd.BaseStickyAdapter;
import tyj.com.yedashenlib.widget.recylerview.multitype.MultiTypeEntity;/*** @author ChenYe created by on 2019/3/21 0021. 08:32**/public class XdTitleEntity implements MultiTypeEntity {private String titleName;public XdTitleEntity(String titleName) {this.titleName = titleName;}public String getTitleName() {return titleName;}public void setTitleName(String titleName) {this.titleName = titleName;}@Overridepublic int getItemType() {return BaseStickyAdapter.TYPE_TITLE;}
}
import tyj.com.testlib.xd.BaseStickyAdapter;
import tyj.com.yedashenlib.widget.recylerview.multitype.MultiTypeEntity;/*** @author ChenYe created by on 2019/3/21 0021. 08:32**/public class XdNormalEntity implements MultiTypeEntity {private String desc;public XdNormalEntity(String desc) {this.desc = desc;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic int getItemType() {return BaseStickyAdapter.TYPE_NORMAL;}
}
/*** @author ChenYe created by on 2019/3/20 0020. 13:50**/public interface MultiTypeEntity {/*** 返回数据的type** @return*/int getItemType();
}

你把这些类拿起来然后自己写成一个小demo应该是可以跑起来的,我是借鉴了好几个大神的demo,然后再综合一下弄出来。

需要注意的地方是activity 的xml :

RecyclerView 学习之吸顶

1) 你自仔细看这个xml 就会感觉  箭头 3标记的像是多余的,但是其实是有原因的,因为箭头 2 标记的 TextView 无法android:layout_width = "match_parent" ,写了就出现bug了。我是不断的加背景颜色才试出来的。所以我用箭头 3 来将箭头 1 的布局撑起来成match_parent ,然后再将textView 居中。

  相关解决方案