An overview of

This article is the Android navigation group list series, due to the reasons of time and space, finally up and down, the full version of the effect is as follows:



The upper remnant will look like this:Two ItemDecoration, one to implement the hover header grouping list function, one to implement the splitter line (official demo)

There are a lot of methods on the Internet about the implementation of a hover group head list, like I have seen the chairman of the custom ExpandListView implementation, also saw someone with an additional parent layout inside the RecyclerView/ListView+ a head View (position fixed above the parent layout) implementation. For the above solution, there are the following personal feel bad: 1. RecyclerView is the mainstream now 2. In RecyclerView coat a parent layout is always to increase the layout level, easy to overdraw, is not elegant enough. 3. There are two ways to implement the item layout with such a classification header. One is to treat the classification header as an itemViewtype (trouble), and the other is that each item layout contains the layout of the classification header. Item efficiency decreases). Besides, Google provides us with Item decoration, which itself is used to modify items in RecyclerView. Its getItemOffsets() onDraw() method is used to leave space and draw for Item classification head (solve defect 3). Its onDrawOver() method is used to draw the hovering header View (fix Defect 2). What’s more, ItemDecoration has been out for a long time, but you still don’t use it? This article uses ItemDecoration to create a group list, and is equipped with the hover header function.

Preview: Add multiple ItemDecorations, their execution order, ItemDecoration method execution order, ItemDecoration and RecyclerView drawing order

2 use ItemDecoration

Usage: Add one or more ItemDecorations to RecyclerViewPool

        
        mRv.addItemDecoration(mDecoration = new TitleItemDecoration(this, mDatas));
        mRv.addItemDecoration(new TitleItemDecoration2(this,mDatas));
        mRv.addItemDecoration(new    DividerItemDecoration(MainActivity.this,DividerItemDecoration.VERTICAL_LIST));Copy the code

AddItemDecoration () to RecyclerView. AddItemDecoration () to RecyclerView. Item decor (ItemDecoration) Add ItemDecoration(ItemDecoration, int index) addItemDecoration(ItemDecoration, int index) Other articles explaining RecyclerView are generally a brief description of ItemDecoration, and the Demo used are generally the official DividerItemDecoration class, not to mention adding multiple item decorations. In fact, I was writing the Demo yesterday when I found this method, click to view the source code:

public void addItemDecoration(ItemDecoration decor) { addItemDecoration(decor, -1); } public void addItemDecoration(ItemDecoration decor, int index) { if (mLayout ! = null) { mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or" + " layout"); } if (mItemDecorations.isEmpty()) { setWillNotDraw(false); } if (index < 0) { mItemDecorations.add(decor); } else { mItemDecorations.add(index, decor); } markItemDecorInsetsDirty(); requestLayout(); }Copy the code

Old trick: Our most common single-argument method internally calls a two-argument method, passing index to -1. Item decorations we add are stored in the mItemDecorations variable of RecyclerView class, which is an ArrayList, as defined below

    private final ArrayList mItemDecorations = new ArrayList<>();Copy the code

ItemDecoration method introduction and preparation

Common (all) methods:

In the order in which they are called in RecyclerView: 1. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) 2. public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) 3. public void onDrawOver(Canvas c, RecyclerView parent, Recyclerview. State State) the three methods must be implemented to inherit an ItemDecoration. (There are only three methods except @deprecated in ItemDecoration.)

Method one writing

public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state):

Set (int left, int top, int right, int bottom); Set the padding value for the ItemView in each of the four directions.

The following picture I think is classic:Blog.piasy.com/2016/03/26/…Thank the author. If the author does not allow me to transfer the picture, please contact me to delete



The entity bean of this article is written as follows:

/** * Created by zhangxutong . * Date: 16/08/28 */ public class CityBean { private String tag; private String city; public CityBean(String tag, String city) { this.tag = tag; this.city = city; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getCity() { return city; } public void setCity(String city) { this.city = city; }}Copy the code

The getItemOffsets method is as follows: The information of postion can be obtained through parent, and the classification of each bean in the data can be obtained through postion. Because the data set is already in order, if it is different from the previous classification, it means it is a new classification. Set (0, mTitleHeight, 0, 0); Outrect. set(0, 0, 0, 0); .

@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(); if (position > -1) { if (position == 0) { outRect.set(0, mTitleHeight, 0, 0); } else { if (null ! = mDatas.get(position).getTag() && ! mDatas.get(position).getTag().equals(mDatas.get(position - 1).getTag())) { outRect.set(0, mTitleHeight, 0, 0); } else { outRect.set(0, 0, 0, 0); }}}}Copy the code

Public void onDraw(Canvas C, RecyclerView parent, RecyclerView.State State) : We need to use the parent and state variables to get auxiliary information, such as the top, bottom, left, and right sides of the drawing, childCount, childView, and so on. Finally, c calls the Canvas method to draw the UI we want. OnDraw draws the content below the ItemView. Although it can draw the Rect area that exceeds the getItemOffsets(), the area that exceeds the Rect area will not be displayed, but the area covered by the ItemView will be overdrawn. This article is written as follows: Obtain the left and right of drawing UI by parent and childCount, traverse childView, and decide whether to draw the classified Title area according to the postion of childView, just like the judgment method in method 1: The method of classifying the title is to customize the routine of the View, according to the determined range of the upper and lower left and right first drawRect to draw a background, and then drawText to drawText. (not a custom View may refer to god in the article: kwok blog.csdn.net/lmj62356579… Blog.csdn.net/guolin_blog…). .

@Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); int position = params.getViewLayoutPosition(); if (position > -1) { if (position == 0) { drawTitleArea(c, left, right, child, params, position); } else { if (null ! = mDatas.get(position).getTag() && ! mDatas.get(position).getTag().equals(mDatas.get(position - 1).getTag())) { drawTitleArea(c, left, right, child, params, position); } else {}}}}} /** * Method of drawing Title area background and text ** @param c * @param left * @param right * @param child * @param params * @param position */ private void drawTitleArea(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) { mPaint.setColor(COLOR_TITLE_BG); c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint); mPaint.setColor(COLOR_TITLE_FONT); mPaint.getTextBounds(mDatas.get(position).getTag(), 0, mDatas.get(position).getTag().length(), mBounds); c.drawText(mDatas.get(position).getTag(), child.getPaddingLeft(), child.getTop() - params.topMargin - (mTitleHeight / 2 - mBounds.height() / 2), mPaint); }Copy the code

After writing method 12, you have completed the classification list title drawing, method 3 to achieve the top hover title effect: GO

Public void onDrawOver(Canvas C, RecyclerView parent, recyclerView.state State) : Similar to the onDraw() method, we need to use the parent and state variables to get auxiliary information, such as drawing, position, childView, etc. Finally, c calls the Canvas method to draw the UI we want. The content drawn by onDrawOver is in the top layer of RecyclerView, which will block ItemView. So naturally comes with hover effect, which is the best way to draw hover View. This paper is prepared as follows: The parent linearLayOutManager is used to obtain the first visible itemView and postion, and its category title (tag). Then draw the background and text (tag) of the hover View, as described in method 2.

@Override public void onDrawOver(Canvas c, RecyclerView parent, Recyclerview.state State) {// The last call is drawn at the top int pos = ((LinearLayoutManager)(parent.getLayoutManager())).findFirstVisibleItemPosition() String tag = mDatas.get(pos).getTag() View child = parent.getChildAt(pos) mPaint.setColor(COLOR_TITLE_BG) c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint) mPaint.setColor(COLOR_TITLE_FONT) mPaint.getTextBounds(tag, 0, tag.length(), mBounds) c.drawText(tag, child.getPaddingLeft(), parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2), mPaint) }Copy the code

The ItemDecoration of the group list with a hovering header is now complete.

Title, ItemDecoration

** * Created by zhangxutong. * Date: 16/08/28 */ public class TitleItemDecoration extends RecyclerView.ItemDecoration { private List mDatas; private Paint mPaint; private Rect mBounds; private int mTitleHeight; private static int COLOR_TITLE_BG = Color.parseColor("#FFDFDFDF"); private static int COLOR_TITLE_FONT = Color.parseColor("#FF000000"); private static int mTitleFontSize; public TitleItemDecoration(Context context, List datas) { super(); mDatas = datas; mPaint = new Paint(); mBounds = new Rect(); mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, context.getResources().getDisplayMetrics()); mTitleFontSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, context.getResources().getDisplayMetrics()); mPaint.setTextSize(mTitleFontSize); mPaint.setAntiAlias(true); } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); int position = params.getViewLayoutPosition(); if (position > -1) { if (position == 0) { drawTitleArea(c, left, right, child, params, position); } else { if (null ! = mDatas.get(position).getTag() && ! mDatas.get(position).getTag().equals(mDatas.get(position - 1).getTag())) { drawTitleArea(c, left, right, child, params, position); } else {}}}}} /** * Method of drawing Title area background and text ** @param c * @param left * @param right * @param child * @param params * @param position */ private void drawTitleArea(Canvas c, int left, int right, View child, RecyclerView.LayoutParams params, int position) { mPaint.setColor(COLOR_TITLE_BG); c.drawRect(left, child.getTop() - params.topMargin - mTitleHeight, right, child.getTop() - params.topMargin, mPaint); mPaint.setColor(COLOR_TITLE_FONT); mPaint.getTextBounds(mDatas.get(position).getTag(), 0, mDatas.get(position).getTag().length(), mBounds); c.drawText(mDatas.get(position).getTag(), child.getPaddingLeft(), child.getTop() - params.topMargin - (mTitleHeight / 2 - mBounds.height() / 2), mPaint); } @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { int pos = ((LinearLayoutManager)(parent.getLayoutManager())).findFirstVisibleItemPosition();  String tag = mDatas.get(pos).getTag(); View child = parent.getChildAt(pos); mPaint.setColor(COLOR_TITLE_BG); c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent.getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint); mPaint.setColor(COLOR_TITLE_FONT); mPaint.getTextBounds(tag, 0, tag.length(), mBounds); c.drawText(tag, child.getPaddingLeft(), parent.getPaddingTop() + mTitleHeight - (mTitleHeight / 2 - mBounds.height() / 2), mPaint); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition(); if (position > -1) { if (position == 0) { outRect.set(0, mTitleHeight, 0, 0); } else { if (null ! = mDatas.get(position).getTag() && ! mDatas.get(position).getTag().equals(mDatas.get(position - 1).getTag())) { outRect.set(0, mTitleHeight, 0, 0); } else { outRect.set(0, 0, 0, 0); } } } } }Copy the code

Some items decoration:

Multiple item decorations and their drawing sequence. You can add multiple itemdecorations to a RecyclerView, and create a RecyclerView with multiple itemdecorations. As mentioned in section 2, a number of item decorations are finally stored in the variable mItemDecorations (ArrayList) in RecyclerView, so we can search the source code of RecyclerView. See where the mItemDecorations are used. Find that in the draw() and onDraw() methods: according to the order of postion in mItemDecorations, the onDrawOver and onDraw methods of each ItemDecoration are called in turn. Therefore, if the later ItemDecoration overlaps with the drawing area of the previous ItemDecoration, it will cover the previous ItemDecoration (OverDraw).

@Override public void draw(Canvas c) { super.draw(c); final int count = mItemDecorations.size(); for (int i = 0; i < count; i++) { mItemDecorations.get(i).onDrawOver(c, this, mState); } @Override public void onDraw(Canvas c) { super.onDraw(c); final int count = mItemDecorations.size(); for (int i = 0; i < count; i++) { mItemDecorations.get(i).onDraw(c, this, mState); }}Copy the code

2. Drawing sequence of Item decoration and RecyclerView. When introducing the three methods of ItemDecoration, we have mentioned the conclusion: The onDraw of ItemDecoration is called first and drawn on the bottom layer, and then the middle layer of ItemView is drawn on top. MItemDecorations. Get (I). OnDrawOver (c, this, mState); The super.draw(c) method calls the View’s public void draw(Canvas Canvas) method as follows: RecyclerView onDraw() method, in RecyclerView onDraw() method, will call mitemu.get (I). OnDraw (c, this, mState); So onDraw is called first, draws at the bottom and calls the View (ViewGroup) dispatchDraw(Canvas) method; In the ViewGroup dispatchDraw(Canvas) method, drawChild(Canvas, View Child, Long drawingTime) is executed to draw each itemView. Get (I). OnDrawOver (c, this, mState); So onDrawOver, ItemDecoration, on top. The View’s draw() method looks like this:

/** * This method is called by ViewGroup.drawChild() to have each child view draw itself. * * This is where the View specializes rendering behavior based on layer type, * and hardware acceleration. */ boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { ............ Omit the if (! dirtyOpaque) onDraw(canvas); dispatchDraw(canvas);Copy the code

Vi Full Code address:

Code: download.csdn.net/detail/zxt0…

Welcome to my github download up and down collection demo: like conveniently click a star ha ~ github.com/mcxtzhang/D… Master branch is the upper part of the remnant, sideBar branch is the top and bottom of the complete.

Seven:

This is my first blog post using MarkDown, and it feels great. RecyclerView related categories, each is a treasure, every exploration feel like a treasure, feel that you can do a lot of things with ItemDecoration, Unfortunately, ItemDecoration seems to be unable to accept user clicking events. Otherwise, I would like to use ItemDecoration in the navigation bar on the right. This is a good design idea. Multiple itemdecorations play their respective roles. For example, in this paper, the official ItemDecoration is used as the dividing line. Create a hover title for ItemDecoration. Choose any number of item decorations to enrich your RecyclerView according to your needs. Maybe my general idea of low is that it’s still a XXX class, and if you use it to extend the functionality, you need the extends and code~, but then the different functionality is too coupled to reuse. After all, composition is greater than inheritance. Alexander, I have a lot of work to do this week, so I’m going to write the next post tomorrow, but it may have to be moved to the weekend. Impatient friends can go to my Github sideBar branch to see, is on the basis of this article, a combination of a sideBar custom View, and then use TinyPinyin (github.com/promeG/Tiny…) Set to Adapter, set to sidebar, listen to the sidebar Item switch. In the callback method, Call RecyclerView scrollToPositionWithOffset (int position, int offset) method, sliding RecyclerView to designated location.