Is there an addHeaderView equivalent for RecyclerView?

后端 未结 19 1617
独厮守ぢ
独厮守ぢ 2020-11-21 23:35

I\'m looking for an equivalent to addHeaderView for a recycler view. Basically I want to have an image with 2 buttons be added as a header to the listview. Is there a differ

19条回答
  •  温柔的废话
    2020-11-22 00:32

    Easy and reusable ItemDecoration

    Static headers can easily be added with an ItemDecoration and without any further changes.

    // add the decoration. done.
    HeaderDecoration headerDecoration = new HeaderDecoration(/* init */);
    recyclerView.addItemDecoration(headerDecoration);
    

    The decoration is also reusable since there is no need to modify the adapter or the RecyclerView at all.

    The sample code provided below will require a view to add to the top which can just be inflated like everything else. It can look like this:

    Why static?

    If you just have to display text and images this solution is for you—there is no possibility for user interaction like buttons or view pagers, since it will just be drawn to top of your list.

    Empty list handling

    If there is no view to decorate, the decoration will not be drawn. You will still have to handle an empty list yourself. (One possible workaround would be to add a dummy item to the adapter.)

    The code

    You can find the full source code here on GitHub including a Builder to help with the initialization of the decorator, or just use the code below and supply your own values to the constructor.

    Please be sure to set a correct layout_height for your view. e.g. match_parent might not work properly.

    public class HeaderDecoration extends RecyclerView.ItemDecoration {
    
        private final View mView;
        private final boolean mHorizontal;
        private final float mParallax;
        private final float mShadowSize;
        private final int mColumns;
        private final Paint mShadowPaint;
    
        public HeaderDecoration(View view, boolean scrollsHorizontally, float parallax, float shadowSize, int columns) {
            mView = view;
            mHorizontal = scrollsHorizontally;
            mParallax = parallax;
            mShadowSize = shadowSize;
            mColumns = columns;
    
            if (mShadowSize > 0) {
                mShadowPaint = new Paint();
                mShadowPaint.setShader(mHorizontal ?
                        new LinearGradient(mShadowSize, 0, 0, 0,
                                new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)},
                                new float[]{0f, .5f, 1f},
                                Shader.TileMode.CLAMP) :
                        new LinearGradient(0, mShadowSize, 0, 0,
                                new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)},
                                new float[]{0f, .5f, 1f},
                                Shader.TileMode.CLAMP));
            } else {
                mShadowPaint = null;
            }
        }
    
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
            // layout basically just gets drawn on the reserved space on top of the first view
            mView.layout(parent.getLeft(), 0, parent.getRight(), mView.getMeasuredHeight());
    
            for (int i = 0; i < parent.getChildCount(); i++) {
                View view = parent.getChildAt(i);
                if (parent.getChildAdapterPosition(view) == 0) {
                    c.save();
                    if (mHorizontal) {
                        c.clipRect(parent.getLeft(), parent.getTop(), view.getLeft(), parent.getBottom());
                        final int width = mView.getMeasuredWidth();
                        final float left = (view.getLeft() - width) * mParallax;
                        c.translate(left, 0);
                        mView.draw(c);
                        if (mShadowSize > 0) {
                            c.translate(view.getLeft() - left - mShadowSize, 0);
                            c.drawRect(parent.getLeft(), parent.getTop(), mShadowSize, parent.getBottom(), mShadowPaint);
                        }
                    } else {
                        c.clipRect(parent.getLeft(), parent.getTop(), parent.getRight(), view.getTop());
                        final int height = mView.getMeasuredHeight();
                        final float top = (view.getTop() - height) * mParallax;
                        c.translate(0, top);
                        mView.draw(c);
                        if (mShadowSize > 0) {
                            c.translate(0, view.getTop() - top - mShadowSize);
                            c.drawRect(parent.getLeft(), parent.getTop(), parent.getRight(), mShadowSize, mShadowPaint);
                        }
                    }
                    c.restore();
                    break;
                }
            }
        }
    
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            if (parent.getChildAdapterPosition(view) < mColumns) {
                if (mHorizontal) {
                    if (mView.getMeasuredWidth() <= 0) {
                        mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
                                View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
                    }
                    outRect.set(mView.getMeasuredWidth(), 0, 0, 0);
                } else {
                    if (mView.getMeasuredHeight() <= 0) {
                        mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
                                View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
                    }
                    outRect.set(0, mView.getMeasuredHeight(), 0, 0);
                }
            } else {
                outRect.setEmpty();
            }
        }
    }
    

    Please note: The GitHub project is my personal playground. It is not thorougly tested, which is why there is no library yet.

    What does it do?

    An ItemDecoration is additional drawing to an item of a list. In this case, a decoration is drawn to the top of the first item.

    The view gets measured and laid out, then it is drawn to the top of the first item. If a parallax effect is added it will also be clipped to the correct bounds.

提交回复
热议问题