Recycler View : Handling OnItemTouch For a Navigation Drawer
In my previous post I had shown you how to make material design navigation drawer with a header view, after that post some of the visitors requested me for a tutorial on how to handle the OnItemTouch or OnClick events for Recycler View for the navigation drawer and so in this post I am going to show you how to do just that. Before we start let me just announce that I have started the Email Subscription for this blog and if you would like to stay updated with the posts here then please subscribe. Thank you in advance.How To Handle Clicks For Items In Recycler View
There are two methods to handle the clicks for items in RecyclerView
1. Handle the clicks in the Adapter Class.
2. Handle Clicks in MainActivity Class.
Handling clicks in MainActivity is something that you would like to do most of the time, but if you want just a simple task to be triggered when a item is clicked then you could use the Adapter class to handle clicks. Now handling clicks in Adapter is pretty straight and easy and so first let's learn how to do that
Handling Clicks in Adapter Class
If you have been following the previous article you must know what we have done in our adapter, we have basically assigned all the passed values from the MainActiviy, Created a ViewHolder class and populated the list with the passed resourcesTo handle clicks we are going to use the ViewHolder class and make it implement OnClickListener, after that we need to override the onClick method and define what should happen when the click event has occurred. Finally, we are going to set the itemView.setOnClickListener(this). itemView here is the view passed to us by the onCreateViewHolder() method after the inflation of the xml file for the single row.
what we have done here is we have said that ViewHolder class is going to to be responsible for handling clicks by making it implement OnClickListner. Now in onClick method you would certainly need to know which item is being clicked i.e what is the position of the item being clicked. To know that we use the method getPosition()
You would get a better understanding once you look at the code, so here is how the Adapter class looks like after implementing the above method.
package com.android4devs.navigationdrawer; import android.app.Application; 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.TextView; import android.widget.Toast; /** * Created by hp1 on 28-12-2014. */ public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private static final int TYPE_HEADER = 0; // Declaring Variable to Understand which View is being worked on // IF the viaew under inflation and population is header or Item private static final int TYPE_ITEM = 1; private String mNavTitles[]; // String Array to store the passed titles Value from MainActivity.java private int mIcons[]; // Int Array to store the passed icons resource value from MainActivity.java private String name; //String Resource for header View Name private int profile; //int Resource for header view profile picture private String email; //String Resource for header view email Context context; // Creating a ViewHolder which extends the RecyclerView View Holder // ViewHolder are used to to store the inflated views in order to recycle them public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { int Holderid; TextView textView; ImageView imageView; ImageView profile; TextView Name; TextView email; Context contxt; public ViewHolder(View itemView,int ViewType,Context c) { // Creating ViewHolder Constructor with View and viewType As a parameter super(itemView); contxt = c; itemView.setClickable(true); itemView.setOnClickListener(this); // Here we set the appropriate view in accordance with the the view type as passed when the holder object is created if(ViewType == TYPE_ITEM) { textView = (TextView) itemView.findViewById(R.id.rowText); // Creating TextView object with the id of textView from item_row.xml imageView = (ImageView) itemView.findViewById(R.id.rowIcon);// Creating ImageView object with the id of ImageView from item_row.xml Holderid = 1; // setting holder id as 1 as the object being populated are of type item row } else{ Name = (TextView) itemView.findViewById(R.id.name); // Creating Text View object from header.xml for name email = (TextView) itemView.findViewById(R.id.email); // Creating Text View object from header.xml for email profile = (ImageView) itemView.findViewById(R.id.circleView);// Creating Image view object from header.xml for profile pic Holderid = 0; // Setting holder id = 0 as the object being populated are of type header view } } @Override public void onClick(View v) { Toast.makeText(contxt,"The Item Clicked is: "+getPosition(),Toast.LENGTH_SHORT).show(); } } MyAdapter(String Titles[],int Icons[],String Name,String Email, int Profile,Context passedContext){ // MyAdapter Constructor with titles and icons parameter // titles, icons, name, email, profile pic are passed from the main activity as we mNavTitles = Titles; //have seen earlier mIcons = Icons; name = Name; email = Email; profile = Profile; //here we assign those passed values to the values we declared here this.context = passedContext; //in adapter } //Below first we ovverride the method onCreateViewHolder which is called when the ViewHolder is //Created, In this method we inflate the item_row.xml layout if the viewType is Type_ITEM or else we inflate header.xml // if the viewType is TYPE_HEADER // and pass it to the view holder @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_ITEM) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row,parent,false); //Inflating the layout ViewHolder vhItem = new ViewHolder(v,viewType,context); //Creating ViewHolder and passing the object of type view return vhItem; // Returning the created object //inflate your layout and pass it to view holder } else if (viewType == TYPE_HEADER) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header,parent,false); //Inflating the layout ViewHolder vhHeader = new ViewHolder(v,viewType,context); //Creating ViewHolder and passing the object of type view return vhHeader; //returning the object created } return null; } //Next we override a method which is called when the item in a row is needed to be displayed, here the int position // Tells us item at which position is being constructed to be displayed and the holder id of the holder object tell us // which view type is being created 1 for item row @Override public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) { if(holder.Holderid ==1) { // as the list view is going to be called after the header view so we decrement the // position by 1 and pass it to the holder while setting the text and image holder.textView.setText(mNavTitles[position - 1]); // Setting the Text with the array of our Titles holder.imageView.setImageResource(mIcons[position -1]);// Settimg the image with array of our icons } else{ holder.profile.setImageResource(profile); // Similarly we set the resources for header view holder.Name.setText(name); holder.email.setText(email); } } // This method returns the number of items present in the list @Override public int getItemCount() { return mNavTitles.length+1; // the number of items in the list will be +1 the titles including the header view. } // Witht the following method we check what type of view is being passed @Override public int getItemViewType(int position) { if (isPositionHeader(position)) return TYPE_HEADER; return TYPE_ITEM; } private boolean isPositionHeader(int position) { return position == 0; } }
Now it's time to run the app and check whether the things are working the way we want it to be, so this is what you get once the app is up and running.
Perfect the app is handling the onClick events, so this covers the method of handling onClick for RecyclerView with Adapter class.
Handling Clicks in MainActiviy Class
Handling clicks in MainActivity with List View was pretty easy as List
View had a method OnItemClickListener() to handle the clicks, But
Recycler View doesn't provide such method, instead it provides an addOnItemTouchListener() This
method takes ItemTouchListener Object as parameter and assigns that
ItemTouchListner to the Recycler View. Now to understand how the things
are needed to be handled in ItemTouchListner you need to understand the
Touch FrameWork of Android which is out of the scope of this article but
I will try to explain most of the part to help you understand how we
handle clicks.
Here is an outline of the method we are using to handle the clicks
1. First we create a GestureDetector object to detect SingleTapUp
touch this object can be later called to verify if the touch event is a
SingleTapUp type of touch or some other type of touch (swipe touch,
long touch). Look at the code below to understand how to create a
GestureDetector object.
final GestureDetector mGestureDetector = new GestureDetector(MainActivity.this, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } });
GestureDetector object takes two parameters first one is the Context and
second one is the GestureListner. we have created a
SimpleOnGestureListener() which returns true if the MotionEvent i.e. The
touch even is SingleTapUp type of touch. So now if we
call mGestureDetector.onTouchEvent(motionEvent) and if it returns true
the the MotionEvent passed to it is of type SingleTapUp.
2. The next step is to set addOnItemTouchListener() of the Recycler View by passing the ItemTouchListener object to the this method, This is how the code looks.
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() { @Override public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) { View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY()); if(child!=null && mGestureDetector.onTouchEvent(motionEvent)){ Drawer.closeDrawers(); return true; } return false; } @Override public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) { } });
when you create a new OnItemTouchListner, you have to Override two methods which are
1. public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent){}
2. public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent){}
Now Recycler View can have touch event in two cases, once when the user
is trying to swipe the list up or down and second when the user is
clicking on the item in the Recycler View, we don't want recycler view
to think that the touch event which has occurred for click, is for
swiping and to help us tell this to recycler view we can use the
onInterceptTouchEvent, onInterceptTouchEvent takes over the touch event
before it is passed to recycler view this way we can check with
GestureDetector if the Touch is SingleTapUp type of touch and if it is
we can handle the touch and return true and if it's not then we simply
return false and let recycler view handle the touch.
Now we know what structure we need to handle the touch events, there is
just one more thing we to handle the touch events and that isthe
position of the item being clicked in the list, luckly its very easy to
obtain the position, to do that we can use the method from recyclerView.getChildPosition(item)
but as you see we need to pass the item view for which we want the
position. So we need to get the view which is being touched or clicked,
to get that we use
recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY())
here motionEvent is touchEvent and getX() and getY() provides the
location of the touch, findChildViewUnder takes that cordinate location
and returns the view under that cordinate, Now we just pass the view
returned by this method to .getChildPosition() to get the position of the item being clicked
This explains the exact method, now check out the code for final implementation
package com.android4devs.navigationdrawer; import android.content.Context; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.GestureDetector; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; public class MainActivity extends ActionBarActivity { //First We Declare Titles And Icons For Our Navigation Drawer List View //This Icons And Titles Are holded in an Array as you can see String TITLES[] = {"Home","Events","Mail","Shop","Travel"}; int ICONS[] = {R.drawable.ic_home,R.drawable.ic_events,R.drawable.ic_mail,R.drawable.ic_shop,R.drawable.ic_travel}; //Similarly we Create a String Resource for the name and email in the header view //And we also create a int resource for profile picture in the header view String NAME = "Akash Bangad"; String EMAIL = "akash.bangad@android4devs.com"; int PROFILE = R.drawable.aka; private Toolbar toolbar; // Declaring the Toolbar Object RecyclerView mRecyclerView; // Declaring RecyclerView RecyclerView.Adapter mAdapter; // Declaring Adapter For Recycler View RecyclerView.LayoutManager mLayoutManager; // Declaring Layout Manager as a linear layout manager DrawerLayout Drawer; // Declaring DrawerLayout ActionBarDrawerToggle mDrawerToggle; // Declaring Action Bar Drawer Toggle @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /* Assinging the toolbar object ot the view and setting the the Action bar to our toolbar */ toolbar = (Toolbar) findViewById(R.id.tool_bar); setSupportActionBar(toolbar); mRecyclerView = (RecyclerView) findViewById(R.id.RecyclerView); // Assigning the RecyclerView Object to the xml View mRecyclerView.setHasFixedSize(true); // Letting the system know that the list objects are of fixed size mAdapter = new MyAdapter(TITLES,ICONS,NAME,EMAIL,PROFILE,this); // Creating the Adapter of MyAdapter class(which we are going to see in a bit) // And passing the titles,icons,header view name, header view email, // and header view profile picture mRecyclerView.setAdapter(mAdapter); // Setting the adapter to RecyclerView final GestureDetector mGestureDetector = new GestureDetector(MainActivity.this, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } }); mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() { @Override public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) { View child = recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY()); if(child!=null && mGestureDetector.onTouchEvent(motionEvent)){ Drawer.closeDrawers(); Toast.makeText(MainActivity.this,"The Item Clicked is: "+recyclerView.getChildPosition(child),Toast.LENGTH_SHORT).show(); return true; } return false; } @Override public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) { } }); mLayoutManager = new LinearLayoutManager(this); // Creating a layout Manager mRecyclerView.setLayoutManager(mLayoutManager); // Setting the layout Manager Drawer = (DrawerLayout) findViewById(R.id.DrawerLayout); // Drawer object Assigned to the view mDrawerToggle = new ActionBarDrawerToggle(this,Drawer,toolbar,R.string.openDrawer,R.string.closeDrawer){ @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); // code here will execute once the drawer is opened( As I dont want anything happened whe drawer is // open I am not going to put anything here) } @Override public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); // Code here will execute once drawer is closed } }; // Drawer Toggle Object Made Drawer.setDrawerListener(mDrawerToggle); // Drawer Listener set to the Drawer toggle mDrawerToggle.syncState(); // Finally we set the drawer toggle sync State } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
So finally our drawer is up and running and the rows are now clickable,
with this we are done with implementing click events for navigation
drawer. Again if you liked this post and would like to stay updated for
the upcoming posts please make sure you subscribe to the mail list, also
let me know if you liked the post through the comments below.
0 comments :