Android Recycler View details and sliding deletion, etc.

Keywords: Android network xml encoding

This article Recycler ViewDemo


RecyclerView's three self-contained layouts

The picture interface of this article comes from the dry goods concentration camp http://gank.io/api

RecyclerView is a control in the support.v7 package, which can be said to be an enhanced version of ListView and GridView.

The official description of RecyclerView is (not translated because I am poor in English, really):

A flexible view for providing a limited window into a large data set.

RecyclerView has been around for a long time since it was released in 2014, and its use is quite common.

This paper mainly introduces the basic use of RecyclerView, automatic loading of more data, drag and drop of item and scratch deletion. See my demo for detailed effects and usage.

Basic usage of RecyclerView

RecyclerView is similar to ListView in usage. Let me take the grid layout as an example, first code and then elaborate.

First, create this control in the layout:

    <?xml version="1.0" encoding="utf-8"?>
 <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/grid_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="#ffffff"
tools:context="com.zcp.recyclerviewddmo.GridActivity">
<!--toolbar-->
<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        />

</android.support.design.widget.AppBarLayout>
<!--Dropdown refresh control-->
<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/grid_swipe_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >

    <!--Protagonist-->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/grid_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>

The code for Activity is as follows:

public class GridActivity extends AppCompatActivity {

private static RecyclerView recyclerview;
private CoordinatorLayout coordinatorLayout;
private GridAdapter mAdapter;
private List<Meizi> meizis;
private GridLayoutManager mLayoutManager;
private int lastVisibleItem ;
private int page=1;
private ItemTouchHelper itemTouchHelper;
private SwipeRefreshLayout swipeRefreshLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_grid);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    initView();//Initialize layout
    setListener();//Setting up listening events

    //Execute loading data
    new GetData().execute("http://gank.io/api/data/welfare/10/1 ";

}

private void initView(){
    coordinatorLayout=(CoordinatorLayout)findViewById(R.id.grid_coordinatorLayout);

    recyclerview=(RecyclerView)findViewById(R.id.grid_recycler);
    mLayoutManager=new GridLayoutManager(GridActivity.this,3,GridLayoutManager.VERTICAL,false);//Vertical grid layout set to a three-column
    recyclerview.setLayoutManager(mLayoutManager);

    swipeRefreshLayout=(SwipeRefreshLayout) findViewById(R.id.grid_swipe_refresh) ;
    //Adjust the location of Swipe Refresh Layout
    swipeRefreshLayout.setProgressViewOffset(false, 0,  (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
}

private void setListener(){
    //swipeRefreshLayout refresh listener
    swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            page=1;
            new GetData().execute("http://gank.io/api/data/welfare/10/1 ";
        }
    });
    }


private class GetData extends AsyncTask<String, Integer, String> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //Set swipeRefreshLayout to refresh status
        swipeRefreshLayout.setRefreshing(true);
    }

    @Override
    protected String doInBackground(String... params) {

        return MyOkhttp.get(params[0]);
    }

    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        if(!TextUtils.isEmpty(result)){

            JSONObject jsonObject;
            Gson gson=new Gson();
            String jsonData=null;

            try {
                jsonObject = new JSONObject(result);
                jsonData = jsonObject.getString("results");
            } catch (JSONException e) {
                e.printStackTrace();
            }
            if(meizis==null||meizis.size()==0){
                meizis= gson.fromJson(jsonData, new TypeToken<List<Meizi>>() {}.getType());

                Meizi pages=new Meizi();
                pages.setPage(page);
                meizis.add(pages);//Add an item to the data link list to display the number of pages
            }else{
                List<Meizi>  more= gson.fromJson(jsonData, new TypeToken<List<Meizi>>() {}.getType());
                meizis.addAll(more);

                Meizi pages=new Meizi();
                pages.setPage(page);
                meizis.add(pages);//Add an item to the data link list to display the number of pages
            }

            if(mAdapter==null){
                recyclerview.setAdapter(mAdapter = new GridAdapter(GridActivity.this,meizis));//recyclerview setup adapter

                //Implementing custom click listeners for adapters
                mAdapter.setOnItemClickListener(new GridAdapter.OnRecyclerViewItemClickListener() {
                    @Override
                    public void onItemClick(View view) {
                        int position=recyclerview.getChildAdapterPosition(view);
                        SnackbarUtil.ShortSnackbar(coordinatorLayout,"Click first"+position+"individual",SnackbarUtil.Info).show();
                       //For the color Snackbar tool class, see my previous article "No time to explain, use Snackbar! - Android Snackbar Fancy Guide" http://www.jianshu.com/p/cd1e80e64311
                    }
                    @Override
                    public void onItemLongClick(View view) {

                    }
                });       
            }else{
                //Let the adapter refresh data
                mAdapter.notifyDataSetChanged();
            }
        }
        //Stop swipeRefreshLayout loading animation
        swipeRefreshLayout.setRefreshing(false);
    }
}
}

Next, let's look at the RecyclerView adapter:

public  class GridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {

private Context mContext;
private List<Meizi> datas;//data

//Custom listening events
public static interface OnRecyclerViewItemClickListener {
    void onItemClick(View view);
    void onItemLongClick(View view);
}
private OnRecyclerViewItemClickListener mOnItemClickListener = null;
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
    mOnItemClickListener = listener;
}

//Adapter initialization
public GridAdapter(Context context,List<Meizi> datas) {
    mContext=context;
    this.datas=datas;
}

@Override
public int getItemViewType(int position) {
    //Determine whether item categories are graphs or display pages (pictures have URL s)
    if (!TextUtils.isEmpty(datas.get(position).getUrl())) {
        return 0;
    } else {
        return 1;
    }
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
    //Load different ViewHolder s according to item categories
    if(viewType==0){
        View view = LayoutInflater.from(mContext
                ).inflate(R.layout.grid_meizi_item, parent,
                false);//This layout is an image view for displaying pictures.
        MyViewHolder holder = new MyViewHolder(view);

       //Set up Click and Long Click Monitor for Layout
        view.setOnClickListener(this);
        view.setOnLongClickListener(this);

        return holder;
    }else{
        MyViewHolder2 holder2=new MyViewHolder2(LayoutInflater.from(
                mContext).inflate(R.layout.page_item, parent,
                false));//This layout is a textview to display the number of pages
        return holder2;
    }

}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
 //Bind data to item view, load network image if MyViewHolder, and display page number if MyViewHolder 2
    if(holder instanceof MyViewHolder){
        Picasso.with(mContext).load(datas.get(position).getUrl()).into(((MyViewHolder) holder).iv);//Loading Network Pictures
    }else if(holder instanceof MyViewHolder2){
        ((MyViewHolder2) holder).tv.setText(datas.get(position).getPage()+"page");
    }

}

@Override
public int getItemCount()
{
    return datas.size();//Number of data obtained
}

//Click Event Callback
@Override
public void onClick(View v) {
    if (mOnItemClickListener != null) {
        mOnItemClickListener.onItemClick(v);
    }
}
@Override
public boolean onLongClick(View v) {
    if (mOnItemClickListener!= null) {
      mOnItemClickListener.onItemLongClick(v);
   }
    return false;
}

//Custom ViewHolder for loading images
class MyViewHolder extends RecyclerView.ViewHolder
{
    private ImageButton iv;

    public MyViewHolder(View view)
    {
        super(view);
        iv = (ImageButton) view.findViewById(R.id.iv);
    }
}
//Customize ViewHolder to display the number of pages
class MyViewHolder2 extends RecyclerView.ViewHolder
{
    private TextView tv;

    public MyViewHolder2(View view)
    {
        super(view);
        tv = (TextView) view.findViewById(R.id.tv);
    }
}

    //Add an item
    public void addItem(Meizi meizi, int position) {
        meizis.add(position, meizi);
        notifyItemInserted(position);
        recyclerview.scrollToPosition(position);//Scroll the recyclerview to the new add item
    }

    //Delete an item
    public void removeItem(final int position) {
        meizis.remove(position);
        notifyItemRemoved(position);
    }
}

Data class:

 public class Meizi {

private String url;//Picture address
private int page;//The number of pages

public String getUrl() {
    return url;
}

public void setUrl(String url) {
    this.url = url;
}

public int getPage() {
    return page;
}

public void setPage(int page) {
    this.page = page;
}  
}

RecyclerView is similar to ListView.

RecyclerView needs to set the layout manager through setLayoutManager() method. RecyclerView has three default layout managers, LinearLayoutManager, GridLayoutManager and Staggered GridLayoutManager, which support horizontal and vertical alignment and reverse sliding. If you want to change RecyclerView to horizontal sliding, you can also call

mLayoutManager.setOrientation(GridLayoutManager.HORIZONTAL);

RecyclerView does not provide item click monitoring like ListView, so we can only implement it ourselves. RecyclerView's item click event listener can either set up a listener for item's view in this article or judge gestures in recyclerView.addOnItemTouchListener.

You can set the animation when item is loaded or removed by calling the following methods

recyclerView.setItemAnimator(new DefaultItemAnimator());

More animation effects can be seen by others. RecyclerViewItemAnimators  This project.

Autoload more

To automatically load the next page when you slide somewhere in the list (for example, when the last two items are left), you can set a sliding listener on RecyclerView to get the position of the last item currently displayed in the adapter. If the position of the item is less than or equal to 2 minus the total number of adapter items, load the next page of data.
The grid Layout Manager and Linear Layout Manager are implemented as follows:

  //recyclerview scroll listener
    recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            //0: The current screen stops rolling; 1:the screen is rolling and the user is still touching or finger is still on the screen; 2:the inertial sliding generated on the screen with the user's operation;
            // When the sliding state stops and there are fewer than two item s left, the next page is automatically loaded
            if (newState == RecyclerView.SCROLL_STATE_IDLE
                    && lastVisibleItem +2>=mLayoutManager.getItemCount()) {
                new GetData().execute("http://gank.io/api/data/welfare/10/"+(++) page);
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //Gets the location of the last visible view loaded in the adapter.
            lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
        }
    });

Staggered Grid Layout Manager Because the location of item is staggered, the findLastVisibleItemPosition() method returns an array, so we have to first determine the maximum value of this array, which is written in the onScrolled method above.

    int[] positions= mLayoutManager.findLastVisibleItemPositions(null);
    lastVisibleItem =Math.max(positions[0],positions[1]);//According to the number of columns set by Staggered Grid Layout Manager

Automatically load the next page.gif


XRecyclerView It's a library written by someone else, which implements the drop-down and bottom loading layout of RecyclerView. You can see what you need.

Item dragging and sliding deletion

ItemTouchHelper is an auxiliary class dealing with the sliding deletion and dragging of RecyclerView. It is used to drag and drop item s of RecyclerView.

ItemTouchHelper listens as follows

    itemTouchHelper=new ItemTouchHelper(new ItemTouchHelper.Callback() {

        //Used to set the direction of dragging and sliding
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            int dragFlags=0,swipeFlags=0;
            if(recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager||recyclerView.getLayoutManager() instanceof GridLayoutManager){
               //Grid layout has four directions
               dragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN|ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
            }else if(recyclerView.getLayoutManager() instanceof LinearLayoutManager){
               //Linear layout has two directions
               dragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN;

               swipeFlags = ItemTouchHelper.START|ItemTouchHelper.END; //Set the side slip direction to be both directions.
            }
            return makeMovementFlags(dragFlags,swipeFlags);//item does not slip if swipeFlags is 0
        }

        //Callback this method when dragging item long
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            int from=viewHolder.getAdapterPosition();
            int to=target.getAdapterPosition();

            Meizi moveItem=meizis.get(from);
            meizis.remove(from);
            meizis.add(to,moveItem);//Data Location in Exchange Data Link List

            mAdapter.notifyItemMoved(from,to);//Update the location of item in the adapter
            return true;
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //Here handle sliding deletion
        }

        @Override
        public boolean isLongPressDragEnabled() {
            return false;//Returning true sets drag and drop for all item s
        }
    });

itemTouchHelper needs to be bound to recyclerView to be effective, which is called when recyclerView is initialized

     itemTouchHelper.attachToRecyclerView(recyclerview);

Because I want the image item in the grid layout to be draggable and the page number item to be draggable, I return false by the isLongPress DragEnabled () method, and deal with it in the long click event monitoring of item.

       mAdapter.setOnItemClickListener(new GridAdapter.OnRecyclerViewItemClickListener() {
       @Override
       public void onItemClick(View view) {
       }

        @Override
        public void onItemLongClick(View view) {
              itemTouchHelper.startDrag(recyclerview.getChildViewHolder(view));//Setting drag item
           }
       });

If you want to set drag and slide response animation effects for item, you can use the following three methods of ItemTouchHelper. Using a linear layout example:

//Called when item drag starts
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
 if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
       viewHolder.itemView.setBackgroundColor(Color.LTGRAY);//Set the background color to grey when dragging
    }
}

//Called when item drag is complete
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
 viewHolder.itemView.setBackgroundColor(Color.WHITE);//Set the background color to white when dragging stops
}

 //Called when item view changes
 @Override
 public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
  //Modify item transparency according to the value of item slip offset. Screen width is the screen width I got in advance.
  viewHolder.itemView.setAlpha(1-Math.abs(dX)/screenwidth);
 }

Drag and slide delete.gif

This article Recycler ViewDemo

Posted by richierich on Thu, 23 May 2019 12:53:34 -0700