【Android進階】RecyclerView之分組列表加吸頂效果(四)

前言

前面說了RecyclerViewItemDecoration,這裏咱們用它來實現個吸頂效果php

效果圖

20190212_142252.gif

分析

先來分析一下,能夠看到這是一個按月份分組的2行圖片列表,列表頂部一個懸浮欄,會隨着列表滑動而刷新,點擊頂部欄,彈出了一個篩選框。java

思路

1.列表部分

能夠用RecyclerView+GridLayoutManager,月份的標題欄可使用多佈局android

首先是主體item的佈局git

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/cffffff" app:cardCornerRadius="4dp">


    <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content">

        <ImageView android:id="@+id/iv_pictrue" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintDimensionRatio="1.78:1" />

        <TextView android:id="@+id/tv_pictrue_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:textColor="@color/c151619" android:textSize="16sp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/iv_pictrue" tools:text="長沙會議圖集(210)" />

        <TextView android:id="@+id/tv_pictrue_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:layout_marginBottom="10dp" tools:text="2018-11-10" android:textColor="@color/c969696" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_pictrue_title" />


    </android.support.constraint.ConstraintLayout>

</android.support.v7.widget.CardView>
複製代碼

而後是月份標題的佈局github

<?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="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" android:paddingTop="15dp" android:paddingBottom="15dp">

    <TextView android:layout_width="0dp" android:layout_height="1dp" android:layout_marginLeft="15dp" android:layout_marginRight="10dp" android:layout_weight="1" android:background="@color/cbbbfc2" />

    <TextView android:id="@+id/tv_picture_month" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="2018年10月" android:textColor="@color/c969696" android:textSize="16sp" />


    <TextView android:layout_width="0dp" android:layout_height="1dp" android:layout_marginLeft="10dp" android:layout_marginRight="15dp" android:layout_weight="1" android:background="@color/cbbbfc2" />


</LinearLayout>
複製代碼

PictureAdapter 這裏使用了 BaseRecyclerViewAdapterHelper,須要繼承BaseMultiItemQuickAdapter,關於adapter多佈局的使用,篇幅所限,這裏再也不細述。緩存

public class PictureAdapter extends BaseMultiItemQuickAdapter<PictureModel, BaseViewHolder> {
    public PictureAdapter(@Nullable List<PictureModel> data) {
        super(data);
        addItemType(PictureModel.PICTURE_CONTENT, R.layout.item_pictures);
        addItemType(PictureModel.PICTURE_TITLE, R.layout.item_picture_month);
    }


    @Override
    protected void convert(BaseViewHolder helper, PictureModel item) {

        if (helper.getItemViewType() == PictureModel.PICTURE_CONTENT) {
            //標題/數量
            helper.setText(R.id.tv_pictrue_title, item.getTitle() + "(" + item.getPicture_count() + ")");
            //時間
            helper.setText(R.id.tv_pictrue_time, item.getDate());
            //封面圖
            GlideUtils.loadImg(mContext, item.getCover_image(), (ImageView) helper.getView(R.id.iv_pictrue));

        } else if (helper.getItemViewType() == PictureModel.PICTURE_TITLE) {
            helper.setText(R.id.tv_picture_month, item.getDate());

        }

    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        FullSpanUtil.onAttachedToRecyclerView(recyclerView, this, PictureModel.PICTURE_TITLE);


    }

    @Override
    public void onViewDetachedFromWindow(@NonNull BaseViewHolder holder) {
        super.onViewDetachedFromWindow(holder);
        FullSpanUtil.onViewAttachedToWindow(holder, this, PictureModel.PICTURE_TITLE);
    }

}
複製代碼

其中,因爲月份的標題須要佔滿一行,須要重寫onAttachedToRecyclerViewonViewDetachedFromWindow方法。app

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


}
複製代碼

PictureModel須要繼承MultiItemEntity,而後重寫getItemType方法,adapter便可經過modeltype來判斷該使用哪一個佈局。 注:get和set方法這裏就不貼了ide

public class PictureModel implements MultiItemEntity {

    public static final int PICTURE_TITLE = 1;
    public static final int PICTURE_CONTENT = 0;
 

    private int type;
    private String id;
    private String title;
    private String date_time;
    private String create_time;
    private String picture_count;
    private String status;
    private String cover_image;
    private String date;

     public PictureModel(int type) {
        this.type = type;
    }
    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    @Override
    public int getItemType() {
        return type;
    }
  
}
複製代碼

最後,是在Activity使用佈局

pictureAdapter = new PictureAdapter(null);
 rvPictrues.setLayoutManager(new GridLayoutManager(context, 2));
 SpaceDecoration spaceDecoration = new SpaceDecoration(dp2px(context, 10));
 spaceDecoration.setPaddingStart(false);
 rvPictrues.addItemDecoration(spaceDecoration);
 rvPictrues.setAdapter(pictureAdapter);
 pictureAdapter.bindToRecyclerView(rvPictrues);
複製代碼
/** * dp轉px * * @param context * @param dpVal * @return */
    public static int dp2px(Context context, float dpVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal,
                context.getResources().getDisplayMetrics());
    }
複製代碼

SpaceDecorationpost

public class SpaceDecoration extends RecyclerView.ItemDecoration {

    private int space;
    private int headerCount = -1;
    private int footerCount = Integer.MAX_VALUE;
    private boolean mPaddingEdgeSide = true;
    private boolean mPaddingStart = true;
    private boolean mPaddingHeaderFooter = false;
    private ColorDrawable mColorDrawable;

    private boolean mDrawLastItem = true;
    private boolean mDrawHeaderFooter = false;


    public SpaceDecoration(int space) {
        this.mColorDrawable = new ColorDrawable(Color.parseColor("#e7e7e7"));
        this.space = space;
    }

    public SpaceDecoration(int space, int color) {
        this.mColorDrawable = new ColorDrawable(color);
        this.space = space;
    }

    public void setPaddingEdgeSide(boolean mPaddingEdgeSide) {
        this.mPaddingEdgeSide = mPaddingEdgeSide;
    }

    public void setPaddingStart(boolean mPaddingStart) {
        this.mPaddingStart = mPaddingStart;
    }

    public void setPaddingHeaderFooter(boolean mPaddingHeaderFooter) {
        this.mPaddingHeaderFooter = mPaddingHeaderFooter;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        int spanCount = 0;
        int orientation = 0;
        int spanIndex = 0;
        int headerCount = 0, footerCount = 0;
        if (parent.getAdapter() instanceof BaseQuickAdapter) {
            headerCount = ((BaseQuickAdapter) parent.getAdapter()).getHeaderLayoutCount();
            footerCount = ((BaseQuickAdapter) parent.getAdapter()).getFooterLayoutCount();
        }

        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof StaggeredGridLayoutManager) {
            orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
            spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
            spanIndex = ((StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
        } else if (layoutManager instanceof GridLayoutManager) {
            orientation = ((GridLayoutManager) layoutManager).getOrientation();
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
            spanIndex = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
        } else if (layoutManager instanceof LinearLayoutManager) {
            orientation = ((LinearLayoutManager) layoutManager).getOrientation();
            spanCount = 1;
            spanIndex = 0;
        }

        /** * 普通Item的尺寸 */
        if ((position >= headerCount && position < parent.getAdapter().getItemCount() - footerCount)) {

            if (orientation == VERTICAL) {
                float expectedWidth = (float) (parent.getWidth() - space * (spanCount + (mPaddingEdgeSide ? 1 : -1))) / spanCount;
                float originWidth = (float) parent.getWidth() / spanCount;
                float expectedX = (mPaddingEdgeSide ? space : 0) + (expectedWidth + space) * spanIndex;
                float originX = originWidth * spanIndex;
                outRect.left = (int) (expectedX - originX);
                outRect.right = (int) (originWidth - outRect.left - expectedWidth);
                if (position - headerCount < spanCount && mPaddingStart) {
                    outRect.top = space;
                }
                outRect.bottom = space;
                return;
            } else {
                float expectedHeight = (float) (parent.getHeight() - space * (spanCount + (mPaddingEdgeSide ? 1 : -1))) / spanCount;
                float originHeight = (float) parent.getHeight() / spanCount;
                float expectedY = (mPaddingEdgeSide ? space : 0) + (expectedHeight + space) * spanIndex;
                float originY = originHeight * spanIndex;
                outRect.bottom = (int) (expectedY - originY);
                outRect.top = (int) (originHeight - outRect.bottom - expectedHeight);
                if (position - headerCount < spanCount && mPaddingStart) {
                    outRect.left = space;
                }
                outRect.right = space;
                return;
            }
        } else if (mPaddingHeaderFooter) {
            if (orientation == VERTICAL) {
                outRect.right = outRect.left = mPaddingEdgeSide ? space : 0;
                outRect.top = mPaddingStart ? space : 0;
            } else {
                outRect.top = outRect.bottom = mPaddingEdgeSide ? space : 0;
                outRect.left = mPaddingStart ? space : 0;
            }
        }

    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

        if (parent.getAdapter() == null) {
            return;
        }

        int orientation = 0;
        int headerCount = 0, footerCount = 0, dataCount;

        if (parent.getAdapter() instanceof BaseQuickAdapter) {
            headerCount = ((BaseQuickAdapter) parent.getAdapter()).getHeaderLayoutCount();
            footerCount = ((BaseQuickAdapter) parent.getAdapter()).getFooterLayoutCount();
            dataCount = parent.getAdapter().getItemCount();
        } else {
            dataCount = parent.getAdapter().getItemCount();
        }
        int dataStartPosition = headerCount;
        int dataEndPosition = headerCount + dataCount;


        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof StaggeredGridLayoutManager) {
            orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
        } else if (layoutManager instanceof GridLayoutManager) {
            orientation = ((GridLayoutManager) layoutManager).getOrientation();
        } else if (layoutManager instanceof LinearLayoutManager) {
            orientation = ((LinearLayoutManager) layoutManager).getOrientation();
        }
        int start, end;
        if (orientation == OrientationHelper.VERTICAL) {
            start = parent.getPaddingLeft();
            end = parent.getWidth() - parent.getPaddingRight();
        } else {
            start = parent.getPaddingTop();
            end = parent.getHeight() - parent.getPaddingBottom();
        }

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            int position = parent.getChildAdapterPosition(child);

            if (position >= dataStartPosition && position < dataEndPosition - 1//數據項除了最後一項
                    || (position == dataEndPosition - 1 && mDrawLastItem)//數據項最後一項
                    || (!(position >= dataStartPosition && position < dataEndPosition) && mDrawHeaderFooter)//header&footer且可繪製
                    ) {

                if (orientation == OrientationHelper.VERTICAL) {
                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    int top = child.getBottom() + params.bottomMargin;
                    int bottom = top;
                    mColorDrawable.setBounds(start, top, end, bottom);
                    mColorDrawable.draw(c);
                } else {
                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                    int left = child.getRight() + params.rightMargin;
                    int right = left;
                    mColorDrawable.setBounds(left, start, right, end);
                    mColorDrawable.draw(c);
                }
            }
        }
    }
}
複製代碼

2.頂部欄部分

這裏可使用ItemDecoration,難點在於如何設置點擊點擊事件。感謝做者,提供了一種新的思路 StickyItemDecoration,這裏只說如何使用,原理閱讀做者源碼便可。 按照這個思路,咱們能夠將頭部佈局單獨出來,這樣的話,處理點擊事件就很簡單。 activity佈局

<RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent">


            <android.support.v7.widget.RecyclerView android:id="@+id/rv_pictrues" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/cf7f8fa" android:nestedScrollingEnabled="false" />

            <com.leda.yunke.widget.sticky.StickyHeadContainer android:id="@+id/shc_pictrues" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/cffffff">

                <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="vertical">

                    <TextView android:id="@+id/tv_picture_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:drawableRight="@mipmap/unfold" android:drawablePadding="10dp" android:gravity="center" android:text="2018年11月" android:textColor="@color/c969696" android:textSize="16sp" />

                    <TextView style="@style/line_f3f3f3" />
                </LinearLayout>


            </com.leda.yunke.widget.sticky.StickyHeadContainer>
        </RelativeLayout>
複製代碼

StickyHeadContainer

public class StickyHeadContainer extends ViewGroup {

    private int mOffset;
    private int mLastOffset = Integer.MIN_VALUE;
    private int mLastStickyHeadPosition = Integer.MIN_VALUE;

    private int mLeft;
    private int mRight;
    private int mTop;
    private int mBottom;

    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;

        // 考慮父容器內邊距
        desireWidth += getPaddingLeft() + getPaddingRight();
        desireHeight += getPaddingTop() + getPaddingBottom();
        // 嘗試比較建議最小值和指望值的大小並取大值
        desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
        desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());
        // 設置最終測量值
        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;
    }

    protected int getChildHeight() {
        return getChildAt(0).getHeight();
    }

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

複製代碼

activity中完整使用

StickyItemDecoration stickyItemDecoration = new StickyItemDecoration(shcPictrues, PictureModel.PICTURE_TITLE);
        stickyItemDecoration.setOnStickyChangeListener(new OnStickyChangeListener() {
            @Override
            public void onScrollable(int offset) {
                //可見時
                shcPictrues.scrollChild(offset);
                shcPictrues.setVisibility(View.VISIBLE);
            }

            @Override
            public void onInVisible() {
              //不可見時
                shcPictrues.reset();
                shcPictrues.setVisibility(View.INVISIBLE);
            }
        });

        shcPictrues.setDataCallback(new StickyHeadContainer.DataCallback() {
            @Override
            public void onDataChange(int pos) {
              //數據更新
                List<PictureModel> listModels = pictureAdapter.getData();
                if (listModels.size() > pos) {
                    tvPictureTime.setText(listModels.get(pos).getDate());
                }
            }
        });
        //添加至rv
        rvPictrues.addItemDecoration(stickyItemDecoration);

        pictureAdapter = new PictureAdapter(null);
        rvPictrues.setLayoutManager(new GridLayoutManager(context, 2));
        rvPictrues.addItemDecoration(stickyItemDecoration);
        SpaceDecoration spaceDecoration = new SpaceDecoration(DensityUtils.dp2px(context, 10));
        spaceDecoration.setPaddingStart(false);
        rvPictrues.addItemDecoration(spaceDecoration);
        rvPictrues.setAdapter(pictureAdapter);
        pictureAdapter.bindToRecyclerView(rvPictrues);
複製代碼

StickyItemDecoration

public class StickyItemDecoration extends RecyclerView.ItemDecoration {

    private int mStickyHeadType;
    private int mFirstVisiblePosition;
    // private int mFirstCompletelyVisiblePosition;
    private int mStickyHeadPosition;
    private int[] mInto;

    private RecyclerView.Adapter mAdapter;

    private StickyHeadContainer mStickyHeadContainer;
    private boolean mEnableStickyHead = true;


    private OnStickyChangeListener mOnStickyChangeListener;
    public void setOnStickyChangeListener(OnStickyChangeListener onStickyChangeListener){
        this.mOnStickyChangeListener = onStickyChangeListener;
    }

    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 /*&& mFirstCompletelyVisiblePosition > mStickyHeadPosition*/ && 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;
            }
            if (mOnStickyChangeListener!=null){
                mOnStickyChangeListener.onScrollable(offset);
            }
        } else {
            if (mOnStickyChangeListener!=null){
                mOnStickyChangeListener.onInVisible();
            }
        }

    }

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

}

複製代碼

3.點擊頂部欄彈窗

這裏就偷個懶,不貼代碼了。

最後,就是組裝數據而後設置給pictureAdapter便可。

完整源碼 PicRvDemo

你的承認,是我堅持更新博客的動力,若是以爲有用,就請點個贊,謝謝

相關文章
相關標籤/搜索