Let’s review the RecyclerView of that year from the beginning

Let’s review the RecyclerView of that year from the beginning

1. What is RecyclerView?

According to the official description given by Google: A flexible view for providing a limited window into a large data set. A flexible view that can display a large data set in a limited window.

So we can understand that an appropriate use scenario for RecyclerView is: due to size limitations, the user's device cannot display all items at once, and the user needs to scroll up and down to see more items. Items that scroll out of the visible area will be recycled and reused when the next item becomes visible.

Caching entries is a very useful method for reducing memory overhead and CPU calculations, because it means we don’t have to create new entries every time, thereby reducing memory overhead and CPU calculations, and it can also effectively reduce screen freezes and ensure smooth sliding.

RecyclerView doesn't care about visuals

But what is the difference with ListView? We have been using ListView for a long time, and it can do the same. From its class name, RecyclerView means that I only manage Recycler View, that is, RecyclerView only manages recycling and reuse of View, and other developers can set it up by themselves. You want another layout? Insert another LayoutManager. Do you want different animations? Insert an ItemAnimator, and so on. It can be seen that its high degree of decoupling gives you full customization freedom (so you can easily implement ListView, GridView, waterfall flow and other effects through this control).

2. Introducing RecyclerView

RecyclerView is part of the Support Library. So just add the following dependency in app/build.gradle and you can use it immediately:

  1. dependencies { compile 'com.android.support:recyclerview-v7:25.3.1' }

Add to the layout file:

  1. <android.support.v7.widget.RecyclerView
  2.  
  3. android:id= "@+id/recycler_view"  
  4.  
  5. android:layout_width= "match_parent"  
  6.  
  7. android:layout_height= "match_parent" />

Then introduce RecyclerView into the page:

  1. RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

OK, from now on, let's take it step by step and start to understand it.

3. Use RecyclerView

The following table shows the most important classes used to display data using RecyclerView. These classes are internal classes of RecyclerView. If you want to use RecyclerView, you need to do the following:

Class Function
Adapter Handle data set merging and bind views
ViewHolder Holds all Views used to bind data or require operations
LayoutManager Responsible for placing views and other related operations
ItemDecoration Responsible for drawing the dividing line near the Item
ItemAnimator Add animation effects for general operations of Item, such as adding and deleting items, etc.

We can understand the basic structure of RecyclerView more intuitively from the following figure:

Next, I'll describe what each class or interface contains and how to use it.

3.1 RecyclerView.ViewHolder

The basic usage of ViewHolder is to store View objects. The Android team recommended the use of the "ViewHolder design pattern" a long time ago, but did not require developers to use the ViewHolder pattern in Adapter. Now for this new RecyclerView.Adapter, we must implement and use this pattern.

It's a bit strange that Google waited so long to force the use of the ViewHolder pattern, but it's better late than never. If you don't know about the ViewHolder pattern, check out Android training Hold View Objects in a View Holder. There are also a lot of articles on ListView optimization on the Internet. Interview focus.

One thing is specific to RecyclerView. A ViewHolder subclass can access the root view of the ViewHolder by accessing the public member itemView. So no need to store in the ViewHolder subclass.

The following is the code of the example ViewHolder, which is an inner class of the example Adapter:

  1. public   static class MyViewHolder extends RecyclerView.ViewHolder{
  2. TextView tv;
  3. public MyViewHolder( View itemView) {
  4. super(itemView);
  5. tv = (TextView) itemView.findViewById(R.id.tv);
  6. }
  7. }

3.2 Adapter

Adapter plays two roles. First, it creates corresponding Item-Layout according to different ViewTypes, and second, it accesses data sets and binds the data to the correct View. This requires us to rewrite the following three methods:

  • 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 items held by the Adapter

The sample code is as follows:

  1. @Override
  2. public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  3. MyViewHolder holder = new MyViewHolder(LayoutInflater. from (context)
  4. .inflate(R.layout.item_recycler_view,parent, false ));
  5. return holder;
  6. }
  7. @Override
  8. public void onBindViewHolder(MyViewHolder holder, int position) {
  9. holder.tv.setText(mDatas.get(position));
  10. }
  11. @Override
  12. public   int getItemCount() {
  13. return mDatas.size () ;
  14. }

So, a basic RecyclerView.Adapter looks like this:

  1. public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
  2. private Context context;
  3. private List<String> mDatas;
  4.  
  5. public RecyclerAdapter(Context context, List<String> mDatas) {
  6. this.context = context;
  7. this.mDatas = mDatas;
  8. }
  9. @Override
  10. public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  11. MyViewHolder holder = new MyViewHolder(LayoutInflater. from (context)
  12. .inflate(R.layout.item_recycler_view, parent, false ));
  13. return holder;
  14. }
  15. @Override
  16. public void onBindViewHolder(MyViewHolder holder, int position) {
  17. holder.tv.setText(mDatas.get(position));
  18. }
  19. @Override
  20. public   int getItemCount() {
  21. return mDatas == null ? 0 : mDatas. size ();
  22. }
  23.  
  24. public   static class MyViewHolder extends RecyclerView.ViewHolder {
  25. TextView tv;
  26.  
  27. public MyViewHolder( View itemView) {
  28. super(itemView);
  29. tv = (TextView) itemView.findViewById(R.id.tv);
  30. }
  31. }
  32. }

3.3 RecyclerView.LayoutManager

The LayoutManager's responsibility is to place the Items and decide when to recycle and reuse the Items. It has a default implementation: LinearLayoutManager, which can be used for vertical and horizontal lists.

RecyclerView.LayoutManager is an abstract class. RecyclerView provides us with 3 implementation classes:

  1. LinearLayoutManager is the current manager, supporting horizontal and vertical layouts.
  2. GridLayoutManager Grid layout manager
  3. StaggeredGridLayoutManager waterfall layout manager

3.3.1 LinearlayoutManager

LinearlayoutManager is the default implementation of LayoutManager. You can use this class to create vertical or horizontal lists.

  1. // Set the RecyclerView layout mode to vertical layout
  2. LinearLayoutManager layoutManager = new LinearLayoutManager(context);
  3. layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
  4. recyclerView.setLayoutManager(layoutManager);
  1. // Set the RecyclerView layout to horizontal layout
  2. LinearLayoutManager layoutManager= new LinearLayoutManager(this);
  3. layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
  4. recyclerView.setLayoutManager(layoutManager);

Running the program, we see the following results:

We found that there are some differences from ListView. The lack of a dividing line makes the list look unsightly. We will explain how to set a dividing line for RecyclerView in the next section.

Of course, there are some very practical APIs in LinearlayoutManager:

  • findFirstVisibleItemPosition() returns the position of the first visible item
  • findFirstCompletelyVisibleItemPosition() Returns the position of the first completely visible item
  • findLastVisibleItemPosition() returns the position of the last visible item
  • findLastCompletelyVisibleItemPosition() Returns the position of the last completely visible item

3.3.2 GridLayoutManager

There are two construction methods:

  1. /**
  2. * Creates a vertical GridLayoutManager
  3. *
  4. * @param context Current context, will be used to access resources.
  5. * @param spanCount sets the number of rows, because the default is vertical
  6. */ public GridLayoutManager(Context context, int spanCount) {
  7. super(context);
  8. setSpanCount(spanCount);
  9. }/**
  10. * @param context Current context, will be used to access resources.
  11. * @param spanCount sets the number of rows or columns, depending on the direction
  12. * @param orientation layout direction HORIZONTAL or VERTICAL.
  13. * @param reverseLayout Whether to display in reverse When   set   to   true , layouts from   end   to start.
  14. */ public GridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
  15. super(context, orientation, reverseLayout);
  16. setSpanCount(spanCount);
  17. }

We set up the code as follows:

  1. // Set up a 4-column table layout GridLayoutManager layoutManager = new GridLayoutManager(this,4,GridLayoutManager.VERTICAL, false );
  2. recyclerView.setLayoutManager(layoutManager);
  3. // Set up a horizontal 3-row table layout GridLayoutManager layoutManager = new GridLayoutManager(this,3,GridLayoutManager.HORIZONTAL, false );
  4. recyclerView.setLayoutManager(layoutManager);

3.3.3 StaggeredGridLayoutManager

Let's look at the construction method of StaggeredGridLayoutManager. There is only one method

  1. /**
  2. * Creates a StaggeredGridLayoutManager with given parameters.
  3. *
  4. * @param spanCount sets the number of rows or columns, depending on the direction, vertical is the number of columns, horizontal is the number of rows
  5. * @param orientation direction
  6. */
  7. public StaggeredGridLayoutManager( int spanCount, int orientation) {}

So we set it up like this:

  1. // Set the vertical waterfall layout
  2. StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL);
  3. recyclerView.setLayoutManager(layoutManager);

We can see that the effect is no different from GridLayoutManager, so let's make a small modification and you can see its power.

We add margin to the layout of each item:

  1. <?xml version= "1.0" encoding= "utf-8" ?><LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"  
  2. android:orientation= "vertical" android:layout_width= "match_parent"  
  3. android:layout_height= "wrap_content"  
  4. android:layout_margin= "5dp"  
  5. android:background= "@color/colorAccent" >
  6.  
  7. <TextView
  8. android:id= "@+id/tv"  
  9. android:layout_width= "100dp"  
  10. android:layout_height= "100dp"  
  11. android:layout_gravity= "center"  
  12. android:gravity= "center"  
  13. android:textColor= "#fff"  
  14. /></LinearLayout>

Then we set a random height for our item in the onBindViewHolder method of the adapter:

  1. //Paste part of the code, please see the attachment for the rest
  2. private List< Integer > mHeights;
  3. public RecyclerAdapter(Context context, List<String> mDatas) {
  4. this.context = context;
  5. this.mDatas = mDatas;
  6. mHeights = new ArrayList <Integer> ();
  7. for ( int i = 0; i < mDatas. size (); i++) {
  8. mHeights.add (( int ) (100 + Math.random() * 300));
  9. }
  10. }
  11.  
  12. @Override
  13. public void onBindViewHolder(MyViewHolder holder,
  14. final int position) {
  15. ViewGroup.LayoutParams lp = holder.tv.getLayoutParams();
  16. lp.height = mHeights.get(position);
  17. holder.tv.setLayoutParams(lp);
  18. }

Run it and we can see the effect. Isn't it amazing?

3.4 RecyclerView.ItemDecoration

By setting

  1. recyclerView.addItemDecoration(new DividerDecoration(Context context, int orientation));

To change the offset between items or decorate the items.

For example, if you add the following code after setting LinearlayoutManager above, the effect of the list will change:

  1. // Set a dividing line for the vertical display RecyclerView
  2. recyclerView.addItemDecoration(new DividerItemDecoration(activity,DividerItemDecoration.VERTICAL));
  3. // Set a dividing line for the horizontal display RecyclerView
  4. recyclerView.addItemDecoration(new DividerItemDecoration(activity,DividerItemDecoration.HORIZONTAL));

Of course, you can also set multiple ItemDecorations for RecyclerView. When displaying the list, it will traverse all ItemDecorations and call the drawing methods in them to decorate the Item.

RecyclerView.ItemDecoration is an abstract class that can be used to implement offsets or decoration effects between items by overriding the following three methods:

  • public void onDraw(Canvas c, RecyclerView parent) The decoration drawing is called before the Item item is drawn, so it may be obscured by the content of the Item
  • public void onDrawOver(Canvas c, RecyclerView parent) The decoration is drawn after the Item item is drawn, so the decoration will float above the Item
  • public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) Similar to padding or margin, LayoutManager calls this method during the measurement phase to calculate the correct size of each Item and set the offset.

Of course, if we use GridLayoutManager, the previous DividerItemDecoration is not applicable to the dividing line, mainly because when it is drawn, such as the horizontal line, the value for each child is:

  1. final int   left = parent.getPaddingLeft();
  2. final int   right = parent.getWidth() - parent.getPaddingRight();

Because each Item has one row, this is fine. However, when using GridLayoutManager, a row has multiple childItems, so they are drawn multiple times. In addition, when using GridLayoutManager, if the Item is in the last column (there is no separator line on the right) or the last row (there is no separator line at the bottom), the item will be drawn multiple times.

According to the above method, we can customize a DividerGridItemDecoration. Because the code is too long, we provide it in the attachment. We set DividerGridItemDecoration to GridLayoutManager, and the running effect is as follows:

3.5 RecyclerView.ItemAnimator

ItemAnimator can help Item realize independent animation.

ItemAnimator is triggered by the following three events:

  • A piece of data is inserted into the data collection
  • Remove a piece of data from a collection
  • Change a data item in a data set

Fortunately, a DefaultItemAnimator is implemented by default in Android. We can add animation effects to Item through the following code:

  1. recyclerView.setItemAnimator(new DefaultItemAnimator());

In previous versions, when the data set changed, we refreshed the list by calling .notifyDataSetChanged(). Because this would trigger a redraw of the list, there would be no animation effect. Therefore, we needed to call some special methods prefixed with notifyItem*(), such as:

  • public final void notifyItemInserted(int position) Inserts an item to the specified position
  • public final void notifyItemRemoved(int position) Remove the item at the specified position
  • public final void notifyItemChanged(int position) Updates the item at the specified position

Next, we use DefaultItemAnimator to show the animation effect. Modify the code as follows:

Add two buttons in Activity to dynamically insert and remove items.

  1. btnAdd.setOnClickListener(new View .OnClickListener() {
  2. @Override
  3. public void onClick( View v) {
  4. adapter.addData(1);
  5. }
  6. });
  7.  
  8. btnRemove.setOnClickListener(new View .OnClickListener() {
  9. @Override
  10. public void onClick( View v) {
  11. adapter.removeData(2);
  12. }
  13. });

Add two methods to the Adapter:

  1. public void addData( int position) {
  2. mDatas.add (position, "Insert" );
  3. mHeights.add ( ( int ) (100 + Math.random() * 300));
  4. notifyItemInserted(position);
  5. } public void removeData( int position) {
  6. mDatas.remove(position);
  7. notifyItemRemoved(position);
  8. }

The results are as follows:

3.6 Listeners

Unfortunately, RecyclerView does not provide the following two Item click listener events like ListView does:

  • public void setOnItemClickListener(@Nullable OnItemClickListener listener) Item click event listener
  • public void setOnItemLongClickListener(OnItemLongClickListener listener) Item long click event listener

But this can't stop us, I can define two such methods.

  1. public interface OnItemClickLitener {
  2. void onItemClick( View   view , int position);
  3. void onItemLongClick( View   view , int position);
  4. }
  5. private OnItemClickLitener litener;
  6. public void setLitener(OnItemClickLitener litener) {
  7. this.literer = litener;
  8. }

In the onBindViewHolder method, set a listener for the control that needs to respond to the click event:

  1. // If a callback is set, respond to the click event holder.itemView.setOnClickListener(new View .OnClickListener() {
  2. @Override
  3. public void onClick( View v) {
  4. if (literer != null ) {
  5. litener.onItemClick(v, position);
  6. }
  7. }
  8. });
  9.  
  10. holder.itemView.setOnLongClickListener(new View .OnLongClickListener() {
  11. @Override
  12. public boolean onLongClick( View v) {
  13. if (literer != null ) {
  14. litener.onItemLongClick(v, position);
  15. } return   false ;
  16. }
  17. });

Set this method in Activity:

  1. adapter.setLitener(new RecyclerAdapter.OnItemClickLitener() {
  2. @Override
  3. public void onItemClick( View   view , int position) {
  4. Toast.makeText(activity, "The" +position+ "item was clicked" , Toast.LENGTH_SHORT).show();
  5. }
  6.  
  7. @Override
  8. public void onItemLongClick( View   view , int position) {
  9. Toast.makeText(activity, "The" +position+ "item was long pressed" , Toast.LENGTH_SHORT).show();
  10. }
  11. });

The effect is as shown below:

<<:  How much do you know about performance optimization?

>>:  Building a reinforcement learning API based on TensorFlow: How was TensorForce created?

Recommend

Zhu Yonghai's Slow Bull Lecture on Quantitative Science Opens - Issue 21

Course Catalog Lao Zhu's 21st main course The...

Do cats really care about you? Now we have a conclusion

Hello, this is Science Popularization China. We o...

Alpha Egg AI Dictionary Pen X10: A personal tutor in a pencil case

In today's internationalized era, English pro...

The underlying logic behind Tik Tok’s full-case marketing!

In recent years, new brands have risen rapidly, n...

Stock Market Shilin April Practical Training Camp

Resources of Guhai Shilin April Practical Trainin...

Why did the chick I bought as a child die so quickly? I finally know the answer!

We often see some merchants or small farmers carr...