How Yo Remove Skip Hop Baby View Table
In my recent article Gmail similar Inbox we have successfully cloned the gmail's inbox using a RecyclerView with some absurd animations. Just one thing missing in that is adding swipe to delete and undo functionalities equally gmail does.
In this commodity nosotros are going to add swipe to delete and undo options in a recycler view. Instead of continuing that article, we are going to start a fresh projection to brand information technology simpler to empathise.
1. How RecyclerView Swipe works? ItemTouchHelper
With the assist of ItemTouchHelper grade yous tin can add swipe to dismiss, drag & driblet support to RecyclerView. Swiping the row will remove the row from the RecyclerView, but it won't refresh the data. You can run across an empty row displayed on swiping the row. Yous have to take care of refreshing the list past removing the detail from the adapter dataset.
ItemTouchHelper.SimpleCallback provides sure callback methods like onMove(), onChildDraw(), onSwiped() when the row is swiped. Showing the groundwork view, removing the particular from adapter can be done using these callback methods.
Below is the code snipped of ItemTouchHelper and attaching it to recycler view. This code has to be bit modified in society to make the swipe and undo works. Nosotros'll see how to practise that presently.
RecyclerView recyclerView = findViewById(R.id.recycler_view); ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) { @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { // Row is swiped from recycler view // remove it from adapter } @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, bladder dY, int actionState, boolean isCurrentlyActive) { // view the background view } }; // attaching the touch on helper to recycler view new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView); 1.1 Defining the Swipe Directions
The swipe directions tin exist decided while creating the SimpleCallback(). In this article nosotros just used Left -> Correct direction. If yous want other directions, you tin combine with | operator. Nosotros can define LEFT, RIGHT, Upwardly and Downward directions.
new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.Right)
1.2 Designing The Layout
Planning the layout is very important while adding background view of the row. Lot of people are providing the background by drawing the view on a Canvas in onChildDraw() method. This will be a tiresome process cartoon it on a canvas if the background view complex. Pattern the same in an xml layout is much easier. Then I accept placed both the foreground and background views in a simple layout using FrameLayout keeping the foreground layout on top.
The foreground view will be always visible in the recycler view, and when swipe is performed the background will be visible staying in a static position.
Below prototype volition requite you lot an idea how the foreground and background views are placed in a frame.
2. Example JSON – Restaurant Carte du jour
I have created an example JSON that contains eating house carte du jour with proper names and images. We are going to consume this json to display the nutrient items in our RecyclerView list.
https://api.androidhive.info/json/menu.json
iii. Creating New Projection
At present that nosotros have the required knowledge of ItemTouchHelper, let's first a new project and run across how to become the desired output we are looking for.
Annotation: This project is developed using Android Studio three.0 Beta. In lodge to get this commodity working, go the latest the version of Android Studio here.
one. Create a new project in Android Studio from File ⇒ New Projection and select Basic Action from templates.
2. Open build.gradle located under app folder and add RecyclerView, Glide and Volley dependencies.
dependencies { implementation 'com.android.support:recyclerview-v7:26.1.0' // glide image library implementation 'com.github.bumptech.glide:glide:3.7.0' // volley http library implementation 'com.android.volley:volley:1.0.0' implementation 'com.google.code.gson:gson:2.half-dozen.2' } iii. Add the below resources to your strings.xml, dimens.xml and colors.xml respectively.
<resources> <string name="app_name">Recycler Swipe</string> <string name="action_settings">Settings</string> <string name="my_cart">My Cart</cord> <string name="delete">DELETE</string> </resources>
<resources> <dimen name="activity_padding_horizontal">16dp</dimen> <dimen name="padd_10">10dp</dimen> <dimen name="ic_delete">30dp</dimen> <dimen name="thumbnail">90dp</dimen> </resources>
<?xml version="i.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#111</color> <colour name="colorPrimaryDark">#FFF</color> <color name="colorAccent">#ea3732</color> <color name="bg_row_background">#fa315b</color> <color name="item_name">#535353</colour> <color name="description">#a9a9a9</color> </resources>
4. Open up styles.xml add the below styles to employ Lighter theme to your app. This make the text to darker equally we take white background to toolbar.
<resources> <!-- Base awarding theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light"> <!-- Customize your theme hither. --> <item proper noun="colorPrimary">@color/colorPrimary</particular> <item proper name="colorPrimaryDark">@color/colorPrimaryDark</particular> <item proper noun="colorAccent">@colour/colorAccent</item> </manner> <style name="AppTheme.NoActionBar"> <item name="windowActionBar">faux</item> <item name="windowNoTitle">true</detail> </style> <way proper noun="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Light" /> <mode proper noun="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> </resources>
5. Create a form named MyApplication.coffee and extend it from Application. This is a singleton course used to initiate the volley library.
import android.app.Application; import android.text.TextUtils; import com.android.volley.Asking; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; public form MyApplication extends Application { public static final Cord TAG = MyApplication.class .getSimpleName(); private RequestQueue mRequestQueue; private static MyApplication mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; } public static synchronized MyApplication getInstance() { return mInstance; } public RequestQueue getRequestQueue() { if (mRequestQueue == cypher) { mRequestQueue = Volley.newRequestQueue(getApplicationContext()); } return mRequestQueue; } public <T> void addToRequestQueue(Asking<T> req, String tag) { // set the default tag if tag is empty req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req); } public <T> void addToRequestQueue(Request<T> req) { req.setTag(TAG); getRequestQueue().add(req); } public void cancelPendingRequests(Object tag) { if (mRequestQueue != naught) { mRequestQueue.cancelAll(tag); } } } 6. Open up AndroidManifest.xml and add together MyApplication grade to <awarding> node. Also add the INTERNET permission every bit we gonna make http calls.
<?xml version="one.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="info.androidhive.recyclerviewswipe"> <uses-permission android:name="android.permission.Internet" /> <application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@cord/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activeness android:proper name=".MainActivity" android:characterization="@cord/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.activity.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Now we have all the resources set up. Let'southward start adding the RecyclerView and rendering the data by parsing the json.
seven. Open layout file your MainActivity.coffee and add the RecyclerView. For my activity I have two layout files, activity_main.xml and content_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.back up.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-car" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/coordinator_layout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="info.androidhive.recyclerviewswipe.MainActivity"> <android.support.blueprint.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="@android:color/white" app:popupTheme="@manner/AppTheme.PopupOverlay" /> </android.support.pattern.widget.AppBarLayout> <include layout="@layout/content_main" /> </android.support.pattern.widget.CoordinatorLayout>
<?xml version="i.0" encoding="utf-viii"?> <android.support.constraint.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="info.androidhive.recyclerviewswipe.MainActivity" tools:showIn="@layout/activity_main"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="vertical" /> </android.back up.constraint.ConstraintLayout>
8. Create a course named Particular.class and add the below. This POJO grade contains menu item name, price, description and url of thumbnail epitome.
/** * Created by ravi on 26/09/17. */ public form Item { int id; String proper name; Cord description; double cost; String thumbnail; public Item() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.proper noun = name; } public String getDescription() { return description; } public void setDescription(String description) { this.clarification = clarification; } public double getPrice() { render price; } public void setPrice(double price) { this.price = price; } public String getThumbnail() { render thumbnail; } public void setThumbnail(Cord thumbnail) { this.thumbnail = thumbnail; } } nine. Create an xml layout named cart_list_item.xml under res ⇒ layout folder. This layout file renders each row in recycler view using the adapter class. Here we are calculation an ImageView for thumbnail and few TextViews to display the carte item proper name, description and price.
<?xml version="1.0" encoding="utf-eight"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:id="@+id/view_background" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/bg_row_background"> <ImageView android:id="@+id/delete_icon" android:layout_width="@dimen/ic_delete" android:layout_height="@dimen/ic_delete" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="@dimen/padd_10" android:src="@drawable/ic_delete_white_24dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="@dimen/padd_10" android:layout_toLeftOf="@id/delete_icon" android:text="@string/delete" android:textColor="#fff" android:textSize="13dp" /> </RelativeLayout> <RelativeLayout android:id="@+id/view_foreground" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:padding="@dimen/padd_10"> <ImageView android:id="@+id/thumbnail" android:layout_width="@dimen/thumbnail" android:layout_height="@dimen/thumbnail" android:layout_marginRight="@dimen/activity_padding_horizontal" android:scaleType="centerCrop" /> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/thumbnail" android:ellipsize="end" android:fontFamily="sans-serif-medium" android:maxLines="1" android:textColor="@color/item_name" android:textSize="17dp" /> <TextView android:id="@+id/description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/name" android:layout_marginTop="5dp" android:layout_toRightOf="@id/thumbnail" android:textColor="@color/description" android:textSize="12dp" /> <TextView android:id="@+id/toll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_toRightOf="@id/thumbnail" android:textColor="@colour/colorAccent" android:textStyle="bold" /> </RelativeLayout> </FrameLayout>
10. Create a class named CartListAdapter.java. This adapter course will be used to inflate the layouts with proper data in recycler view. I am too adding two additional methods to this class removeItem() and restoreItem(), to remove / add together rows to recycler view.
import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.bumptech.glide.Glide; import java.util.List; public course CartListAdapter extends RecyclerView.Adapter<CartListAdapter.MyViewHolder> { private Context context; individual List<Item> cartList; public class MyViewHolder extends RecyclerView.ViewHolder { public TextView proper noun, description, price; public ImageView thumbnail; public RelativeLayout viewBackground, viewForeground; public MyViewHolder(View view) { super(view); name = view.findViewById(R.id.name); description = view.findViewById(R.id.description); price = view.findViewById(R.id.cost); thumbnail = view.findViewById(R.id.thumbnail); viewBackground = view.findViewById(R.id.view_background); viewForeground = view.findViewById(R.id.view_foreground); } } public CartListAdapter(Context context, List<Item> cartList) { this.context = context; this.cartList = cartList; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.cart_list_item, parent, faux); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, final int position) { concluding Item item = cartList.become(position); holder.proper name.setText(item.getName()); holder.clarification.setText(particular.getDescription()); holder.price.setText("₹" + item.getPrice()); Glide.with(context) .load(item.getThumbnail()) .into(holder.thumbnail); } @Override public int getItemCount() { return cartList.size(); } public void removeItem(int position) { cartList.remove(position); // notify the item removed by position // to perform recycler view delete animations // NOTE: don't call notifyDataSetChanged() notifyItemRemoved(position); } public void restoreItem(Particular item, int position) { cartList.add together(position, item); // notify item added by position notifyItemInserted(position); } } eleven. Now we are at the core part of this article i.e adding swipe functionality to recycler view. To achieve this create a class named RecyclerItemTouchHelper.java and and extend the form from ItemTouchHelper.SimpleCallback and override the necessary methods.
> getDefaultUIUtil() will be used by ItemTouchHelper to detect whenever in that location is UI change on the view. We utilise this part to go on the background view in a static position and move the foreground view.
> In onChildDrawOver() the x-position of the foreground view is changed while user is swiping the view.
> RecyclerItemTouchHelperListener interface used to send the callback to implementing activeness. Here the listener will be triggered in MainActivity once the swipe is done.
import android.graphics.Sheet; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.View; /** * Created past ravi on 29/09/17. */ public course RecyclerItemTouchHelper extends ItemTouchHelper.SimpleCallback { private RecyclerItemTouchHelperListener listener; public RecyclerItemTouchHelper(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) { super(dragDirs, swipeDirs); this.listener = listener; } @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { render true; } @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { if (viewHolder != nothing) { final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground; getDefaultUIUtil().onSelected(foregroundView); } } @Override public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, bladder dY, int actionState, boolean isCurrentlyActive) { concluding View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground; getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY, actionState, isCurrentlyActive); } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground; getDefaultUIUtil().clearView(foregroundView); } @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground; getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY, actionState, isCurrentlyActive); } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { listener.onSwiped(viewHolder, management, viewHolder.getAdapterPosition()); } @Override public int convertToAbsoluteDirection(int flags, int layoutDirection) { return super.convertToAbsoluteDirection(flags, layoutDirection); } public interface RecyclerItemTouchHelperListener { void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position); } } 12. Finally open MainActivity.coffee and practice the changes as shown beneath.
> prepareCart() method fetches the json from the url, parses it and add together the items to adapter data listing
> The instance of RecyclerItemTouchHelper is created and assigned to RecyclerView. Here but LEFT direction is divers.
> onSwiped() method volition be called when the swipe is performed. Here the important footstep of deleting the row item is taken place. mAdapter.removeItem() is called to delete the row from the RecyclerView.
> Once the row is deleted, Snackbar is used to evidence a message with UNDO option. Up on clicking Disengage, the row is restored back using mAdapter.restoreItem() method.
> deletedItem, deletedIndex variables are used to temporarily store the detail deleted and the index until the Snackbar was shown.
import android.graphics.Color; import android.os.Bundle; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.back up.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.Carte; import android.view.View; import android.widget.Toast; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.JsonArrayRequest; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.json.JSONArray; import java.util.ArrayList; import java.util.List; public course MainActivity extends AppCompatActivity implements RecyclerItemTouchHelper.RecyclerItemTouchHelperListener { individual static final String TAG = MainActivity.class.getSimpleName(); private RecyclerView recyclerView; private List<Item> cartList; private CartListAdapter mAdapter; individual CoordinatorLayout coordinatorLayout; // url to fetch carte json private static last String URL = "https://api.androidhive.info/json/menu.json"; @Override protected void onCreate(Packet savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setTitle(getString(R.string.my_cart)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); recyclerView = findViewById(R.id.recycler_view); coordinatorLayout = findViewById(R.id.coordinator_layout); cartList = new ArrayList<>(); mAdapter = new CartListAdapter(this, cartList); RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext()); recyclerView.setLayoutManager(mLayoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); recyclerView.setAdapter(mAdapter); // adding particular touch helper // only ItemTouchHelper.LEFT added to find Correct to Left swipe // if you want both Correct -> Left and Left -> Correct // add laissez passer ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT as param ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, this); new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView); // making http telephone call and fetching menu json prepareCart(); } /** * method brand volley network telephone call and parses json */ private void prepareCart() { JsonArrayRequest request = new JsonArrayRequest(URL, new Response.Listener<JSONArray>() { @Override public void onResponse(JSONArray response) { if (response == null) { Toast.makeText(getApplicationContext(), "Couldn't fetch the card! Pleas try again.", Toast.LENGTH_LONG).show(); return; } Listing<Particular> items = new Gson().fromJson(response.toString(), new TypeToken<List<Particular>>() { }.getType()); // adding items to cart list cartList.articulate(); cartList.addAll(items); // refreshing recycler view mAdapter.notifyDataSetChanged(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError fault) { // error in getting json Log.d(TAG, "Fault: " + mistake.getMessage()); Toast.makeText(getApplicationContext(), "Mistake: " + error.getMessage(), Toast.LENGTH_SHORT).evidence(); } }); MyApplication.getInstance().addToRequestQueue(asking); } /** * callback when recycler view is swiped * item will be removed on swiped * undo selection will exist provided in snackbar to restore the detail */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position) { if (viewHolder instanceof CartListAdapter.MyViewHolder) { // get the removed item name to brandish information technology in snack bar String name = cartList.get(viewHolder.getAdapterPosition()).getName(); // fill-in of removed item for undo purpose concluding Item deletedItem = cartList.get(viewHolder.getAdapterPosition()); terminal int deletedIndex = viewHolder.getAdapterPosition(); // remove the item from recycler view mAdapter.removeItem(viewHolder.getAdapterPosition()); // showing snack bar with Disengage option Snackbar snackbar = Snackbar .make(coordinatorLayout, proper name + " removed from cart!", Snackbar.LENGTH_LONG); snackbar.setAction("Disengage", new View.OnClickListener() { @Override public void onClick(View view) { // undo is selected, restore the deleted detail mAdapter.restoreItem(deletedItem, deletedIndex); } }); snackbar.setActionTextColor(Color.Yellowish); snackbar.show(); } } @Override public boolean onCreateOptionsMenu(Menu carte du jour) { // Inflate the carte; this adds cartList to the action bar if it is present. getMenuInflater().inflate(R.carte.menu_main, carte); return true; } } Run the project and examination the app. Brand sure your device is having agile cyberspace connexion. Here is the screenshot of the final product.
I hope this article explained the swipe functionality very well. If you still take any queries, please post them in the comments section below.
Happy Coding 😀
Hi in that location! I am Founder at androidhive and programming enthusiast. My skills includes Android, iOS, PHP, Ruby on Rails and lot more. If y'all accept any idea that you would want me to develop? Let's talk: ravi@androidhive.info
Source: https://www.androidhive.info/2017/09/android-recyclerview-swipe-delete-undo-using-itemtouchhelper/
0 Response to "How Yo Remove Skip Hop Baby View Table"
Post a Comment