上面兩個效果圖,圖一里面我爲了展現吸頂原理,我給吸頂加了背景,圖二是取消背景的效果圖。
我直接上相關代碼,然後再進行解釋:
這個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.SmartRefreshLayout
android:id="@+id/smart_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.scwang.smartrefresh.layout.header.ClassicsHeader
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/xd_rcv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<tyj.com.testlib.xd.StickyHeadContainer
android:id="@+id/stick_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--android:background="#0a0"-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ccc">
<!--android:background="#a0a"-->
<TextView
android: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"-->
<View
android: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() {
@Override
public void onClick(View v) {
// TODO: 2017/1/9 屏蔽點擊事件
}
});
}
@Override
protected 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));
}
@Override
protected 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);
}
// 生成默認的佈局參數
@Override
protected LayoutParams generateDefaultLayoutParams() {
return super.generateDefaultLayoutParams();
}
// 生成佈局參數,將佈局參數包裝成我們的
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
// 生成佈局參數,從屬性配置中生成我們的佈局參數
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
// 查當前佈局參數是否是我們定義的類型這在code聲明佈局參數時常常用到
@Override
protected 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<>();
@Override
public int getItemCount() {
return mDataList == null ? 0 : mDataList.size();
}
@Override
public 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);
}
@Override
public 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;
}
@Override
public void onBindViewHolder(SuperViewHolder holder, int position) {
if (null == mDataList || mDataList.isEmpty() || position > mDataList.size() - 1) {
return;
}
onBindItemHolder(holder, position, mDataList.get(position));
}
//這個是用來解決局部刷新數據的
@Override
public 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;
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
FullSpanUtil.onAttachedToRecyclerView(recyclerView, this, TYPE_TITLE);
}
@Override
public 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;
}
@Override
protected 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;
//如果要在多類型的基礎上再實現多類型,可以在這裏加case
default:
resId = R.layout.item_multi_type_1;
break;
}
return resId;
}
@Override
protected 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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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">
<TextView
android: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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
<TextView
android: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;
@Override
protected 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() {
@Override
public void titleClick(String title) {
ToastUtil.newInstance().showToast(title);
}
@Override
public void clickItem(String msg) {
ToastUtil.newInstance().showToast(msg);
}
});
stickHeader.setDataCallback(new StickyHeadContainer.DataCallback() {
@Override
public void onDataChange(int pos) {
XdTitleEntity item = (XdTitleEntity) adapter.getItemData(pos);
mXdTitleTv.setText(item.getTitleName());
}
});
sm.setOnRefreshListener(new OnRefreshListener() {
@Override
public 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。
@Override
public 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() {
@Override
public void onChanged() {
reset();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
reset();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
reset();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
reset();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
reset();
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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 :
1) 你自仔細看這個xml 就會感覺 箭頭 3標記的像是多餘的,但是其實是有原因的,因爲箭頭 2 標記的 TextView 無法android:layout_width = "match_parent" ,寫了就出現bug了。我是不斷的加背景顏色才試出來的。所以我用箭頭 3 來將箭頭 1 的佈局撐起來成match_parent ,然後再將textView 居中。