RecyclerView simple introduction

RecyclerView is a highly decoupled, unusually flexible View that achieves eye-popping effects by setting the different LayoutManager, ItemDecoration, ItemAnimator it provides.

The differences between RecyclerView and old-timers ListView mainly lie in the following features:

It is not necessary for the ListView to create a ViewHolder to improve performance. Because ListView does not have a strict ViewHolder design pattern. But when using RecyclerView, Adapter must implement at least one ViewHolder, must follow the ViewHolder design pattern.

Custom Item entry ListView can only implement vertical linear array list view, in contrast, RecyclerView RecyclerView. Can be set via the LayoutManager to customize the view of different style, such as horizontal scrolling list or irregular waterfalls flow list.

Tem animation does not provide any methods or interfaces in the ListView for developers to animate items. On the contrary, you can set RecyclerView RecyclerView. ItemAnimator to add animation to entry.

The setup data source encapsulates various types of Adapter for different data in LisView, such as an ArrayAdapter for handling arrays and a CursorAdapter for displaying Database results. In contrast, RecyclerView must be customized to implement recyclerView. Adapter and provide it with data collection.

Set the dividing line between two items in the ListView by setting the Android: Divider property. If you want to add the effect to RecyclerView, you must use RecyclerView. ItemDecoration, this implementation is not only more flexible, and the style is more rich.

Set the click event AdapterView is found in the ListView. OnItemClickListener interface, used for binding entries click event. But, unfortunately, in RecyclerView does not provide such interfaces, however, provides another interface RcyclerView. OnItemTouchListener, used to response entry touch events.

RecyclerView simple use

One is to add it directly to Gradle:

The compile 'com. Android. Support: recyclerview - v7:24.2.1'Copy the code

Version number need not find, copy the above appCompat directly use, change appCompat recyclerView can be changed

The compile 'com. Android. Support: appcompat - v7:24.2.1'Copy the code

The second option is to drag it directly from the Design palette to prompt you to add it

Introduction to core classes

RecyclerView’s task is limited to recycling and positioning the View on the screen. The View can display data without the support of two other classes: the Adapter and ViewHolder subclasses.

Recyclerview. Adapter processes data sets and binds views

The ViewHolder holds all views that are used to bind data or manipulate it

LayoutManager is responsible for placing views and other related operations

Item decoration is responsible for drawing the dividing line around the Item

The ItemAnimator animates general operations on items, such as adding and deleting items

ViewHolder

The ViewHolder does only one thing: hold the View

RecyclerView itself doesn’t create views, it creates ViewHolder, and ViewHolder references one by one

ItemView, as shown below:

You can obtain more valid information by printing viewholder.toString

@Override public String toString() { final StringBuilder sb = new StringBuilder("ViewHolder{" + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition); if (isScrap()) { sb.append(" scrap ") .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]"); } if (isInvalid()) sb.append(" invalid"); if (! isBound()) sb.append(" unbound"); if (needsUpdate()) sb.append(" update"); if (isRemoved()) sb.append(" removed"); if (shouldIgnore()) sb.append(" ignored"); if (isTmpDetached()) sb.append(" tmpDetached"); if (! isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")"); if (isAdapterPositionUnknown()) sb.append(" undefined adapter position"); if (itemView.getParent() == null) sb.append(" no parent"); sb.append("}"); return sb.toString(); }Copy the code

Adapter

Adapter is a controller object, from the model layer to obtain data, and then provide RecyclerView display, play a role of communication bridge, Adapter is responsible for: 1, create the necessary ViewHolder; 2. Bind ViewHolder to model layer data.

Public VH onCreateViewHolder(ViewGroup parent, int viewType) creates the Item view and returns the corresponding ViewHolder

Public void onBindViewHolder(VH holder, int Position) Binds data to the correct Item view.

Public int getItemCount() returns the number of itMes held by the Adapter

To create adapter, you need to subclass RecyclerView. When RecyclerView needs to display a view object, it will find its adapter, as shown in the following figure:

First, RecyclerView asks how many objects are in the arraylist by calling Adapter’s getItemCount() method. Then RecyclerView calls the createViewHolder(ViewGroup, int) method of the Adapter to create the ViewHolder and the view that the ViewHolder is to display. Finally, RecyclerView passes in the ViewHolder and its location, calling onBindViewHolder(ViewHolder, int). The Adapter finds the data for the destination location and binds it to the ViewHolder view. By binding, you populate the view with model data. After the whole process is completed, RecyclerView can display the crime list items on the screen. Note that the createViewHolder(ViewGroup, int) method is called infrequently compared to the onBindViewHolder(ViewHolder, int) method. Once a ViewHolder is created, RecyclerView stops calling createView Holder(…). Methods. You then save time and memory by recycling the old ViewHolder.

Begin to use

Start by creating a JavaBean responsible for simulating the data

public class JavaBean { String name; int age; public JavaBean(String name, int age) { this.name = name; this.age = age; }}Copy the code

Then add the dependency according to the above two ways, and then set the controller:

/** * @author HaoTianYi [email protected] * @version v1.0 * @des one function is to initialize UI, one function is to update data * @time 2016-11-10 18:54 */ public class MyHolder extends RecyclerView.ViewHolder { private TextView mTvAge; private TextView mTvName; public MyHolder(View itemView) { super(itemView); // Initialize View mTvAge = (TextView) ItemView.findViewById (R.i.age); mTvName = (TextView) itemView.findViewById(R.id.name); Public void bindData(JavaBean bean) {public void bindData(JavaBean bean) { mTvAge.setText(" "+bean.age); mTvName.setText(bean.name); }}Copy the code

Set content filler:

/** * @author HaoTianYi [email protected] * @version v1.0 * @des get data from the model layer and send it to RecyclerView * @time 2016-11-10  18:54 */ public class MyAdapter extends RecyclerView.Adapter { private List mBeen; public MyAdapter(List been) { mBeen = been; } @override public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) { ItemView = LayoutInflater. From (parent.getContext()).inflate(r.layout.item, null); return new MyHolder(itemView); } /** * Once you have created the item that fills the screen, When you swipe the item, you reuse the *, so instead of calling onCreateViewHolder, you call onBindViewHolder @param holder @param position / @Override public void OnBindViewHolder (MyHolder holder, int position) {holder.bindData(mbe.get (position)); } @Override public int getItemCount() { return mBeen.size(); }}Copy the code

The code to use the Activity:

public class MainActivity extends AppCompatActivity { private List mBeen = new ArrayList(); Private Final String NAME = ""; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() {for (int I = 0; i < 50; i++) { JavaBean javaBean = new JavaBean(NAME + i, i); mBeen.add(javaBean); } RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv); recyclerView.setAdapter(new MyAdapter(mBeen)); / / must set up the layout, otherwise will not display contents recyclerView. SetLayoutManager (new LinearLayoutManager (MainActivity. This)); }}Copy the code

Compared to ListView, RecyclerView each class is really responsibility super single ah!!

Finally put the code for item:


   
    
    


Copy the code

Layout management LayoutManager

Layout management of the base class is RecyclerView LayoutManager (abstract)

LinearLayoutManager Linear manager, supports horizontal and vertical. GridLayoutManager grid layout manager StaggeredGridLayoutManager waterfall layout manager

Change the code above to achieve horizontal scrolling in an instant:


 LinearLayoutManager manager = new LinearLayoutManager(MainActivity.this);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(manager);

Copy the code

Implement grid manager, 4 columns per row


recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this,4));
Copy the code

Waterfall flow manager, same as above when the second parameter is set to Vertical:


 recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));

Copy the code

When HORIZONTAL is set to 4 lines, it is HORIZONTAL sliding. The width of the item needs to be set carefully. After all, there is no constraint on the HORIZONTAL width, because the control can scroll horizontally. There is no serious distortion here because the font is set to size

Commonly used API

FindFirstVisibleItemPosition () returns the first visible Item of the position findFirstCompletelyVisibleItemPosition () returns the current first fully shows the position of the Item FindLastVisibleItemPosition last visible () returns the current position of the Item findLastCompletelyVisibleItemPosition last completely visible () returns the current position of the Item

Set up a waterfall layout

To change the height of each item, change the onCreateViewHolder method in the Adapter

@override public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) { ItemView = LayoutInflater. From (parent.getContext()).inflate(r.layout.item, null); // Set random height int height = (int) (math.random () * 100); LinearLayout. LayoutParams params = new LinearLayout. LayoutParams (954/4300 + height); Params. The setMargins,10,10,10 (10); itemView.setLayoutParams(params); return new MyHolder(itemView); }Copy the code

The reference code in the Activity remains unchanged:


  StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
Copy the code

This can be done, but each item will change after a while, and there is an animation effect

As you can see the layout changes as it slides, override the getItemViewType in the Adapter to fix each item


  @Override
public int getItemViewType(int position) {
    return position;
}

Copy the code

Now there are no layout changes

Splitter RecyclerView ItemDecoration

By setting the recyclerView. AddItemDecoration (new DividerDecoration (this)); To change offsets between items or to decorate items.

RecyclerView ItemDecoration is an abstract class, can rewrite the following three methods, to achieve the offset between the Item or decorative effect: Public void onDraw(Canvas C, RecyclerView parent) Public void onDrawOver(Canvas C, RecyclerView Parent) is called after the Item is drawn, Public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) LayoutManager calls this method during the measurement phase, calculating the correct size of each Item and setting the offset.

The specific use

Public class DividerItemDecoration extends RecyclerView. ItemDecoration {/ * * * set an attribute set, Can be set in its own attrs * / private static final int [] attrs = new int [] {android. State Richard armitage TTR event. ListDivider}; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; /** * divider Drawable */ private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation ! = HORIZONTAL_LIST && orientation ! = VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent) { System.out.println("recyclerview - itemdecoration onDraw()"); if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); Public void drawVertical(Canvas c, Canvas c, Canvas c, Canvas c, Canvas c, Canvas c) RecyclerView parent) { 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); android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); Final int top = child.getBottom() + params.bottommargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); }} public void drawHorizontal(Canvas c, Canvas c, Canvas c); RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); 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(); final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {if (mOrientation == VERTICAL_LIST) {// This is a rectangular outrect. set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); }}}Copy the code

Change the code in the Activity in the code above:


  recyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this,DividerItemDecoration.VERTICAL_LIST));

Copy the code

Custom dividers

Simply add an Android :listDivider attribute to attrs and the rest is automatically generated:

<! -- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:listDivider">@drawable/divider_item</item>Copy the code

The corresponding custom divider_item.xml


  

    
    


Copy the code

The simplest separator

If you just want to set a margin and a small amount of code in the Adapter, change the onCreateViewHolder method:

@override public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) { ItemView = LayoutInflater. From (parent.getContext()).inflate(r.layout.item, null); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(parent.getWidth(),150); Params. The setMargins,10,10,10 (10); itemView.setLayoutParams(params); return new MyHolder(itemView); }Copy the code

Separator instance in grid layout

Reference: CSDN

public class DividerGridItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; public DividerGridItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) {// int getSpanCount = 1; RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } public void drawHorizontal(Canvas c, RecyclerView parent) { 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(); final int left = child.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawVertical(Canvas c, RecyclerView parent) { 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(); final int top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); If (layoutManager instanceof GridLayoutManager) {if ((pos + 1) % spanCount == 0)// If (pos + 1) % spanCount == 0) } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); If (" orientation = = StaggeredGridLayoutManager. VERTICAL) {if ((pos + 1) % spanCount = = 0) / / if it's the last column, Do not draw the right {return true; } } else { childCount = childCount - childCount % spanCount; If (pos >= childCount)// If it is the last column, return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; If (pos >= childCount)// If it is the last line, return true is not required; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); / / StaggeredGridLayoutManager and longitudinal rolling the if (orientation = = StaggeredGridLayoutManager. VERTICAL) {childCount = childCount - childCount % spanCount; If (pos >= childCount) return true; } else / / StaggeredGridLayoutManager and horizontal scroll {/ / if it is the last line, do not need to draw the bottom of the if ((pos + 1) % spanCount = = 0) {return true; } } } return false; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); If (isLastRaw(parent, itemPosition, spanCount, childCount)) You don't need to draw the bottom {outRect. Set (0, 0, mDivider getIntrinsicWidth (), 0). } else if (parent (parent, itemPosition, spanCount, childCount)) Do not need to draw the right {outRect. Set (0, 0, 0, mDivider getIntrinsicHeight ()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); }}Copy the code

Animation RecyclerView ItemAnimator

A DefaultItemAnimator is implemented by default in Android

An ItemAnimator can animate an Item independently. The ItemAnimator triggers three events:

Insert a piece of data into the data set. Remove a piece of data from the data set. 3. Changes a piece of data in a data set

Event Listener

Public void setOnItemClickListener(@nullable OnItemClickListener) ItemClick listener public void setOnItemClickListener(@nullable OnItemClickListener SetOnItemLongClickListener (OnItemLongClickListener listener) Item long press event listeners

First set up a callback interface in the Adapter, add the OnItemClickListener interface, and change the onBindViewHolder method

private OnItemClickListener mOnItemClickListener; Interface OnItemClickListener {// Click event void onItemClick(View View, int position); Void onItemLongClick(View View, int position); } public void setOnItemClickLitener(OnItemClickListener mLitener) { mOnItemClickListener = mLitener; } /** * Once you have created the item that fills the screen, When you swipe the item, you reuse the *. Instead of calling onCreateViewHolder, you call onBindViewHolder * * @param holder * @param position */ @Override public void OnBindViewHolder (final MyHolder holder, final int position) {system.out.println (holder.toString()); holder.bindData(mBeen.get(position)); if (mOnItemClickListener ! = null) { holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.onItemClick(holder.itemView, position); }}); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { mOnItemClickListener.onItemLongClick(holder.itemView, position); return true; }}); }}Copy the code

Set the listener in the Activity:

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv); mAdapter = new MyAdapter(mBeen); mAdapter.setOnItemClickLitener(new MyAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { System.out.println("-----"+"onItemClick"+"-----"); } @Override public void onItemLongClick(View view, int position) { System.out.println("-----"+"onItemLongClick"+"-----"); }}); recyclerView.setAdapter(mAdapter);Copy the code

Trigger the event and see the result:

Reference RecyclerView