当前位置:首页 > 安卓源码 > 技术博客 >

【Android视图效果】分组列表实现吸顶效果

时间:2019-06-28 09:01 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

效果图 分析 先来分析一下,可以看到这是一个按月份分组的2行图片列表,列表顶部一个悬浮栏,会随着列表滑动而刷新,点击顶部栏,弹出了一个筛选框。 思路 1.列表部分 可以用 RecyclerView + GridLayoutManager ,月份的标题栏可以使用多布局 首先是主体item

效果图

【Android视图效果】分组列表实现吸顶效果

分析

先来分析一下,可以看到这是一个按月份分组的2行图片列表,列表顶部一个悬浮栏,会随着列表滑动而刷新,点击顶部栏,弹出了一个筛选框。

思路

1.列表部分

可以用RecyclerView+GridLayoutManager,月份的标题栏可以使用多布局

首先是主体item的布局

<?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>

然后是月份标题的布局

<?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方法。

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方法这里就不贴了

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

SpaceDecoration

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 &amp;&amp; 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 &amp;&amp; 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 &amp;&amp; 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 &amp;&amp; position < dataEndPosition - 1//数据项除了最后一项
        || (position == dataEndPosition - 1 &amp;&amp; mDrawLastItem)//数据项最后一项
        || (!(position >= dataStartPosition &amp;&amp; position < dataEndPosition) &amp;&amp; mDrawHeaderFooter)//header&amp;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 &amp;&amp; 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 /*&amp;&amp; mFirstCompletelyVisiblePosition > mStickyHeadPosition*/ &amp;&amp; mFirstVisiblePosition >= mStickyHeadPosition &amp;&amp; mStickyHeadPosition != -1) {
View belowView = parent.findChildViewUnder(c.getWidth() / 2, mStickyHeadContainer.getChildHeight() + 0.01f);
mStickyHeadContainer.onDataChange(mStickyHeadPosition);
int offset;
if (isStickyHead(parent, belowView) &amp;&amp; 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 &amp;&amp; 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即可。

完整源码
https://github.com/18702953620/PicRvDemo

【Android视图效果】分组列表实现吸顶效果 转载https://www.codesocang.com/appboke/40563.html

技术博客阅读排行

最新文章