Learning Recycler View from scratch (3)

Keywords: Android xml github

In another article, "Learning RecyclerView (2) from scratch", we have introduced how to add click and long-click event listeners to RecyclerView's item, but there must be some operations to monitor these events. Otherwise, it is meaningless to listen. This article records how to listen to RecyclerView's data. To do this, you can combine click events to modify or delete data, and record how to add partition lines for RecyclerView. Similarly, this article is based on the previous code.

RecyclerView has a high degree of freedom, one of the reasons is that it can be partially refreshed, so it is very convenient to add, delete and change individual data in the Adadapter of RecyclerView. The official API provides us with the following methods:

//This method is used to indicate the location of the new data when adding one data.
final void notifyItemInserted(int position)

//When this method is used to delete a data, position represents the location of the deletion.
final void notifyItemRemoved(int position)

//This method indicates that the location of the item corresponding to the position will not change, but the content of the item will change.
final void notifyItemChanged(int position)

//This method is generally used: most of the data loaded before the adapter is out of date and needs to be updated.
//When this method is called, recyclerView recalculates the subitems and all subitems to reposition
//For efficiency reasons, officials suggest replacing this with more precise methods, such as the three above.
final void notifyDataSetChanged()


These methods are easy to understand, and then we can add several methods to the TestAdapter:

//Remove data
public void removeData(int position) {
stringList.remove(position);
notifyItemRemoved(position);
}

//Additional data
public void addData(int position) {
stringList.add(position, "Add One");
notifyItemInserted(position);
}

//Change data for a location
public void changeData(int position) {
stringList.set(position, "Item " + position + " has changed");
notifyItemChanged(position);
}

Add two buttons to the original MainActivity.xml layout:

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<Button
android:id="@+id/btn_add"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="add" />

<Button
android:id="@+id/btn_change"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="change" />
</LinearLayout>

Then, in MainActivity, handle the click events of these two Button s and call the addData () and changeData () methods in TestAdapter:

findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mTestAdapter.addData(1);
}
});
findViewById(R.id.btn_change).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mTestAdapter.changeData(1);
}
});


Then modify the RecyclerView listener in MainActivity to delete the item on time.

rvTest.addOnItemTouchListener(new RecyclerViewClickListener(this, rvTest,
                new RecyclerViewClickListener.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, "Click " + getList().get(position), Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onItemLongClick(View view, int position) {
                        mTestAdapter.removeData(position);
                        Toast.makeText(MainActivity.this, "Remove " + getList().get(position), Toast.LENGTH_SHORT).show();
                    }
                }));

Rerun the program and you should see the following results:






As you can see, the data has been added, deleted and changed correctly. And in the process of adding and deleting, it is not a sudden direct change, but a small animation effect, which makes it look very comfortable. It uses the animation effect provided by RecyclerView by default.

//This line of code is not required because RecyclerView will be used by default.
rvTest.setItemAnimator(new DefaultItemAnimator());

In "Learning RecyclerView from scratch (1)", we provide an abstract class ItemAnimator, which provides animation effect for RecyclerView item. DefaultItemAnimator () is one of its implementation classes. That is to say, we can define other animations ourselves, but I don't know much about animation implementation yet. So I don't record this first. I heard that there is an open source project on Github. RecyclerViewItemAnimators You can learn it.


How to add dividers to RecyclerView? RecyclerView does not have divider and dividerHeight attributes like ListView. It is estimated that it is also to take care of the high degree of freedom of RecyclerView. However, we can add dividers ourselves. In "Learning RecyclerView from scratch (1)", we also mention an abstract class ItemDe. Coration, which is the decorative class of item,

public static abstract class ItemDecoration {
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}

public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}

public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect,((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),parent)
}
}

Among the three methods, onDraw () and onDrawOver () are obviously used for drawing, so the logic of drawing partition lines can be put in it. The specific difference between them is that onDraw is an item. View is called before drawing, and onDrawOver is called after item view drawing, so we usually choose to override one of the methods. getItemOffsets, which tells RecyclerView to finish drawing an item When view ing, how many spaces should be left to facilitate the drawing of partition lines.

Next we can write an implementation class, which is referred to officially.

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    //Use the list Divider that comes with the system
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

    private Drawable mDivider;
    private int mOrientation;

    public DividerItemDecoration(Context context, int orientation){
        //Loading system resources using TypeArray
        final TypedArray ta = context.obtainStyledAttributes(ATTRS);
        mDivider = ta.getDrawable(0);
        //cache
        ta.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, RecyclerView.State state) {
        if(mOrientation == VERTICAL_LIST){
            drawVertical(c,parent);
        }else{
            drawHorizontal(c,parent);
        }
    }

    public void drawVertical(Canvas c,RecyclerView parent){
        //Get the left margin of the partition line, the padding value of RecyclerView
        final int left = parent.getPaddingLeft();
        //Right margin of partition line
        final int right = parent.getWidth() - parent.getPaddingRight();
        final int childCount = parent.getChildCount();
        //Traverse through all item view s and draw a partition line below them
        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.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left,top,right,bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(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, View view, RecyclerView parent, RecyclerView.State state) {
        if(mOrientation == VERTICAL_LIST){
            //Set the height of the offset to mDivider. get Intrinsic Height, which is exactly the height of the dividing line
            outRect.set(0,0,0,mDivider.getIntrinsicHeight());
        }else{
            outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
        }
    }
}

Next, add the following code to MainAtivity and comment out the margin value in MainActivity.xml (the margin value was added before to distinguish the item s).

rvTest.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));

Rerun the program and you should see the following results:


It can be seen that there is a light grey segmentation line. Here we use the system's own splitting line style, we can completely use custom style to replace the system's splitting line, more knowledge about drawing the splitting line, such as the grid layout of the splitting line drawing, you can refer to the Yangshen blog:

http://blog.csdn.net/lmj623565791/article/details/45059587

 

As far as the usage of RecyclerView is concerned, we can see that although it is a little more troublesome than ListView, its freedom is very high, because many attributes need to be realized by ourselves. The subtext is that we can do what we want, so RecyclerView is really a powerful thing. I hope I can become strong gradually.

Posted by cs-web on Sat, 06 Jul 2019 10:56:56 -0700