android实现评论列表_【Android视图效果】分组列表实现吸顶效果

3ad997a351710ff0b2583e033e5b800b.gif

效果图

613c4ea643c39ddc434b28ff2ba5303f.gif

效果图

分析

先来分析一下,可以看到这是一个按月份分组的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.ConstraintLayoutandroid: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>

PictureAdapter 这里使用了 BaseRecyclerViewAdapterHelper,需要继承BaseMultiItemQuickAdapter,关于adapter多布局的使用,篇幅所限,这里不再细述。

public class PictureAdapter extends BaseMultiItemQuickAdapter<PictureModel, BaseViewHolder> {
    public PictureAdapter(@Nullable List 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 && position 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         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         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 View child = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(child);

if (position >= dataStartPosition && position 1//数据项除了最后一项
        || (position == dataEndPosition - 1 && mDrawLastItem)//数据项最后一项
        || (!(position >= dataStartPosition && position //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布局

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


<android.support.v7.widget.RecyclerViewandroid: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.StickyHeadContainerandroid:id="@+id/shc_pictrues"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/cffffff">

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

      <TextViewandroid: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 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即可。

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

大家都在看

Android自定义频道选择器、频道定制

Flutter 学习及实战分享

你必须要掌握的Android冷启动优化

Android自定义View:快递时间轴实现

欢迎前往安卓巴士博客区投稿,技术成长于分享

期待巴友留言,共同探讨学习

cc598ad23246680a7b089a76f971a082.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/346133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用ajax写无限循环,ajax无限循环

// 猜你喜欢的无限加载(function(){var content document.getElementsByClassName("content")[0];var footer document.getElementsByTagName("footer")[0];var winh window.innerHeight - footer.offsetHeight;var ul document.getElementById("…

lr监控虚拟服务器,lr监控服务器 教程

lr监控服务器 教程 内容精选换一换本教程旨在演示使用GDS(General Data Service)工具将远端服务器上的数据导入GaussDB(DWS)中的办法&#xff0c;帮助您学习如何通过GDS进行数据导入的方法。在本教程中&#xff0c;您将&#xff1a;生成本教程需要使用的CSV格式的数据源文件。将…

vc mysql 图片_VC连接MySQL

一、MySQL的安装可以考虑安装mysql-5.0.41-win32(可到http://www.newhua.com/soft/3573.htm处下载)&#xff0c;当然你有更新的版本更好&#xff0c;注意选择“完全安装”(只有这样才会安装VC编译时需要的头文件等)。安装后期会进行服务器配置&#xff0c;你可以设置你的服务器…

魔兽巨龙追猎者服务器微信群,魔兽世界:难怪现在“龙脊”价格低,掉落率提升,群里一天出几个...

祥子哥最近几天一直在练猎人号&#xff0c;目前已经67级了&#xff0c;再过两三天差不多就能满级了。知道祥子哥为什么练猎人吗&#xff1f;不全是因为猎人伤害高玩着爽&#xff0c;还有另一个原因&#xff0c;那就是“龙脊奖章”现在价格很便宜&#xff0c;祥子哥使把劲应该能…

mysql group原理_MySQL Group By 实现原理分析

【IT168 专稿】由于 GROUP BY 实际上也同样会进行排序操作&#xff0c;而且与 ORDER BY 相比&#xff0c;GROUP BY 主要只是多了排序之后的分组操作。当然&#xff0c;如果在分组的时候还使用了其他的一些聚合函数&#xff0c;那么还需要一些聚合函数的计算。所以&#xff0c;在…

使用AWS Lambda的CloudWatch事件通知

CloudWatchEvents的主要用例是跟踪整个AWS基础架构中的更改。 当前&#xff0c;它支持在Auto Scaling组&#xff0c;EC2&#xff0c;EBS和其他各种事件中发出的事件。 为了对这些事件进行有意义的处理&#xff0c;我们需要一种消耗它们的方法。 AWS使用术语“ targets来指代任何…

r语言 中断r的输入_R语言_004数据输入

现实的情况是&#xff0c;我们大部分遇到的都是表格数据&#xff0c;在R语言里面叫数据框&#xff0c;数据来源一般不可能我们自己在程序开始前手动录入&#xff0c;正常的逻辑是从外面读取现成的数据&#xff0c;再预处理、建模什么的。根据经验&#xff0c;现在的数据来源主要…

我的机器人现在无处可去。 无家可归。 无服务器。

我通常会关注各种网站-有关最新出版物&#xff0c;热门新优惠&#xff0c;限时游戏和竞赛等。 其中大多数不提供“干净”的通知系统&#xff0c;例如RSS feed。 因此&#xff0c;我经常不得不刮擦他们HTML才能达到我所需要的。 这意味着我经常需要运行一些自定义的字符串操作…

dubbo 消费者也要暴露端口吗_一文详细解读 Dubbo 中的 http 协议

(给ImportNew加星标&#xff0c;提高Java技能)转自&#xff1a;Kirito的技术分享&#xff0c;作者&#xff1a;kiritomoe太阳红彤彤&#xff0c;花儿五颜六色&#xff0c;各位读者朋友好&#xff0c;又来到了分享 Dubbo 知识点的时候了。说到 Dubbo 框架支持的协议&#xff0c;…

非一致性访存系统_Hibernate事实:访存策略的重要性

非一致性访存系统在使用ORM工具时&#xff0c;每个人都承认数据库设计和实体到表映射的重要性。 这些方面引起了很多关注&#xff0c;而诸如获取策略之类的事情可能只是推迟了。 我认为&#xff0c;不应将实体获取策略与实体映射设计分开&#xff0c;因为除非经过适当设计&…

应用新的JDK 11字符串方法

在“ 使用JDK 11的Java字符串上的新方法 ”和“ String&#xff03;repeat即将加入Java&#xff1f; ”&#xff0c;我讨论了JDK 11引入Java String的六个新方法。 可用的早期访问JDK 11构建已经包含了这些新方法&#xff0c;在这篇文章中&#xff0c;我将使用其中的一种早期访…

为什么需要切换到在线签署文档和合同

嘿&#xff0c;怪胎&#xff0c; 今天&#xff0c;我们为您带来一些不同。 无论您是开发人员&#xff0c;经理还是设计师&#xff0c;这都会提高您的生产力和效率。 对于公司和个人而言&#xff0c;良好地管理文书工作是强大基础的最重要部分之一。 将工作流程从纸质转移到数…

github怎么自动更新被人更新过的项目_GitHub 的这 8 个实用技巧,95%的人不知道...

知道的越多&#xff0c;不知道的就越多&#xff0c;业余的像一棵小草&#xff01;编辑&#xff1a;业余草来源&#xff1a;https://www.xttblog.com/?p49881、一秒钟把Github项目变成前端网站GitHub Pages大家可能都知道&#xff0c;常用的做法&#xff0c;是建立一个gh-pages…

java 注解 属性 类型_收藏!你一定要知道的Java8中的注解

全文共3002字&#xff0c;预计学习时长6分钟海中有大量的注解!JavaSE 1.5中首次引入了注解。Java注解的目的是允许程序员编写关于其程序的元数据。在OracleDocs中&#xff0c;注解的定义是:“注解是元数据的一种形式&#xff0c;它提供的数据与程序本身无关。”注解可以在代码的…

camel 多个 to_具有多个查询参数的Camel CXF服务

camel 多个 to出色的Apache Camel团队忙于解决查询中多个参数的处理问题&#xff0c;这是一种解决方法。 希望本文将在下一版本的Camel中不再使用。 &#xff08;目前&#xff0c;我使用2.7.5&#xff09; 问题 大于1的查询参数作为null值传递给Camel-CXF服务。 假设网址中有四…

select * from where 三个条件_VBA学习笔记70: Select语句基础

学习资源:《Excel VBA从入门到进阶》第72集 by兰色幻想 这节课来详细讲解Select语句。 Select 字段 from 表 where 条件 例:从sheet1中筛选全部数据。 * 表示全部字符,无条件可以省略where。 Select * from [sheet1$] 如果是对表中特定单元格区域进行查找,可以在[sheet1$]的…

使用Servlet和Bootstrap上传Ajax文件

介绍 在本教程中&#xff0c;我们将检查Ajax文件上传如何与Servlet一起使用。 同样&#xff0c;我们将用Bootstrap装饰表单并通过jQuery Ajax上传ajaxify文件。 实作 基本的servlet实现是相同的。 因此&#xff0c;我们需要做的第一件事是更新我们的web.xml文件并为我们的应用…

linux文件系统dentry_Linux文件系统(四)---三大缓冲区之inode缓冲区 (内存inode映像 )...

在文件系统中&#xff0c;有三大缓冲为了提升效率&#xff1a;inode缓冲区、dentry缓冲区、块缓冲。(内核&#xff1a;2.4.37)一、inode缓冲区为了加快对索引节点的索引&#xff0c;引入inode缓冲区&#xff0c;下面我们看Linux/fs/inode.c代码。inode缓冲区代码1、一些数据结构…

使用这些先进的GC技术提高应用程序性能

应用程序性能是我们的首要考虑因素&#xff0c;垃圾收集优化是取得小而有意义的进步的好地方 自动化垃圾收集&#xff08;与JIT HotSpot编译器一起&#xff09;是JVM中最先进&#xff0c;最有价值的组件之一&#xff0c;但是许多开发人员和工程师对垃圾收集&#xff08;GC&a…

mysql中lead_SqlServer2012中LEAD函数简单分析_MySQL

LEAD函数简单点说&#xff0c;就是把下一行的某列数据提取到当前行来显示&#xff0c;看示例更能解释清楚&#xff0c;先看测试用脚本DECLARE TestData TABLE(ID INT IDENTITY(1,1),Department VARCHAR(20),LastName VARCHAR(20),Rate FLOAT)INSERT INTO TestData(Department,L…