Implement onItemClickListener using MVP pattern - android

I am learning MVP and got confused where and how should I implement onClickListener while not ruining mvp concept here.
Followed this guide: https://android.jlelse.eu/recyclerview-in-mvp-passive-views-approach-8dd74633158
My implementation.
Adapter:
public class RepositoriesRecyclerAdapter extends RecyclerView.Adapter<RepositoriesRecyclerAdapter.RepoViewHolder> {
private final RepositoriesListPresenter presenter;
public RepositoriesRecyclerAdapter(RepositoriesListPresenter repositoriesPresenter) {
this.presenter = repositoriesPresenter;
}
#Override
public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RepositoryViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell_repo_view, parent, false));
}
#Override
public void onBindViewHolder(RepositoryViewHolder holder, int position) {
presenter.onBindRepositoryRowViewAtPosition(position, holder);
}
#Override
public int getItemCount() {
return presenter.getRepositoriesRowsCount();
}
}
RepositoryViewHolder's
public class RepositoryViewHolder extends RecyclerView.ViewHolder implements RepositoryRowView {
TextView titleTextView;
TextView starsCountTextView;
public RepositoryViewHolder(View itemView) {
super(itemView);
titleTextView = itemView.findViewById(R.id.repoTitleText);
starsCountTextView = itemView.findViewById(R.id.repoStarsCountText);
}
#Override
public void setTitle(String title) {
titleTextView.setText(title);
}
#Override
public void setStarCount(int starCount) {
starsCountTextView.setText(String.format("%s ★", starCount));
}
}
RepositoryRowView
interface RepositoryRowView {
void setTitle(String title);
void setStarCount(int starCount);
}
All guides I saw was about creating onClickListener object in Adapter and then use it in ViewHolder, but in this implementation, I override all adapter function in my presenter and passing onClickListener (android related stuff) would contradict mvp pattern. What to do in this case. Maybe someone could write a solution - really confused.
My main goal would be to click a recyclerview item and get item name (via toast)

OnClickListener is an interface from Android SDK. Your presenter should not know anything about the Andriod SDK. It should be pure Java so it can be tested just by using Unit test on the JVM. It shouldn't know anything about views, RecyclerView, Adapter nor ViewHolder.
Your onBindViewHolder doesn't violate this principle because it's separated by an abstract interface - RepositoryRowView.
You should implement OnClickListener in adapter/viewholder and call your presenter from there.
public class RepositoryViewHolder extends RecyclerView.ViewHolder implements RepositoryRowView, View.OnClickListener {
TextView titleTextView;
TextView starsCountTextView;
RepositoriesListPresenter presenter;
public RepositoryViewHolder(View itemView, RepositoriesListPresenter presetner) {
super(itemView);
titleTextView = itemView.findViewById(R.id.repoTitleText);
starsCountTextView = itemView.findViewById(R.id.repoStarsCountText);
this.presenter = presenter;
itemView.setOnClickListener(this);
}
#Override
public void setTitle(String title) {
titleTextView.setText(title);
}
#Override
public void setStarCount(int starCount) {
starsCountTextView.setText(String.format("%s ★", starCount));
}
#Override
public void onClick(View view) {
if (presenter != null) {
presenter.onItemInteraction(getAdapterPosition());
}
}
}

Instead of calling the presenter inside your adapter, I would rather make an interface of the click to call it from the view, since you will instantiate this adapter in your view, it's a good thing to keep the MVP pattern with the click of the elements inside your view and not in the adapter itself.
This example is in Kotlin, but I'm sure you will understand it.
First, just make a simple interface to call your click event whenever the user clicks on any item in your list.
class EquipmentAdapter(private val context: Context,private var equipmentList:ArrayList<Equipment>,itemListener:RecyclerViewClickListener): RecyclerView.Adapter<EquipmentAdapter.EquipmentViewHolder>() {
interface RecyclerViewClickListener {
fun recyclerViewListClicked(v: View?, position: Int)
}
companion object{
var itemClickListener: RecyclerViewClickListener? = null
var equipmentSearchList:ArrayList<Equipment>? = null
}
init {
equipmentSearchList = equipmentList
itemClickListener = itemListener
}
Then , inside your ViewHolder you should call this interface to handle the click
inner class EquipmentViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener {
val equipmentName:TextView = itemView.txt_equipmentname
init {
itemView.setOnClickListener(this)
}
override fun onClick(v: View?) {
itemClickListener?.recyclerViewListClicked(v, adapterPosition)
}
}
Lastly, just implement the interface of the click in the view that you are calling the adapter, and then just manage the presenter interactions there instead inside the adapter
class EquipmentActivity : BaseActivity(), EquipmentContract.EquipmentView, EquipmentAdapter.RecyclerViewClickListener ...
And implement the click method
override fun recyclerViewListClicked(v: View?, position: Int) {
presenter.onItemInteraction(position)
}
Doing this, you are making sure that the click of the elements in the list are being made from the view itself and not from the adapter, here, you can interact with the presenter as always and also do more things that will keep your project clean.

Related

Fragment in a Adapter of RecyclerView JAVA

I have a fragment Users which has 3 other fragments in it (tabs). For one tab ( called Friends2Fragment ) I made a recycler View and made an adapter for it. In each item of RecyclerView I have a button "Add friend" and I want to call it from Friends2Fragment, not to call it from the adapter because I can't use Firestore Database properly.
RecyclerViewInterface:
public interface RecyclerViewInterface {
void onItemClick(int position, String button_pressed);
}
Friends2Fragment.java :
public void onStart(){
super.onStart();
recyclerView = (RecyclerView) v.findViewById(R.id.recycler);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
friendslist = new ArrayList<>();
myAdapter = new MyAdapter(friendslist,v.getContext());
recyclerView.setAdapter(myAdapter);
------ Firestore operations ------
}
#Override
public void onItemClick(int position, String button_pressed) {
switch ( button_pressed ){
case "ADD_FRIEND":
Log.d(TAG, "item clicked: " + friendslist.get(position).username);
}
}
MyAdapter.java :
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {
Context context;
public ArrayList<User> userArrayList;
public MyAdapter(ArrayList<User> userArrayList, Context context) {
this.userArrayList = userArrayList;
this.context = context;
}
public Context getContext() {
return context;
}
public ArrayList<User> getUserArrayList() {
return userArrayList;
}
#NonNull
#Override
public MyAdapter.myViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
MyAdapter.myViewHolder myViewHolder = new MyAdapter.myViewHolder(v);
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((Friends2Fragment)context).onItemClick(myViewHolder.getAdapterPosition(),"ADD_FRIEND");
}
});
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyAdapter.myViewHolder holder, int position) {
User user = userArrayList.get(position);
holder.usernamerecycle.setText(user.username);
}
#Override
public int getItemCount() {
return userArrayList.size();
}
public void filterList(List<User> filteredList){
userArrayList = (ArrayList<User>) filteredList;
notifyDataSetChanged();
}
public class myViewHolder extends RecyclerView.ViewHolder{
TextView usernamerecycle;
Button addbutton;
View rootview;
public myViewHolder(#NonNull View itemView) {
super(itemView);
rootview = itemView;
usernamerecycle = itemView.findViewById(R.id.usernamerecycler);
addbutton = itemView.findViewById(R.id.addfriendbutton);
}
}
}
The problem is at this line : ((Friends2Fragment)context).onItemClick(myViewHolder.getAdapterPosition(),"ADD_FRIEND"); in onCreateViewHolder method in MyAdapter.
I have this error : Inconvertible types; cannot cast 'android.content.Context' to 'com.example.birthday.Fragments.Friends2Fragment'
Please help me ..
A Fragment isn't a Context (that's not one of its supertypes) so that cast is impossible, that's why you're getting the error.
I think you should organise it like this: your Adapter holds a bunch of User objects, right? It displays those, and you have a click listener on each ViewHolder that knows which index in the User list it's currently displaying, and it wants to inform some listener when it's clicked. That index is an internal detail really, it would make more sense to look up the actual User, and provide that to the listener.
The simplest way is to just provide your fragment as a listener. First store it in your adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {
// store a reference to your fragment
private Friends2Fragment listener;
// add a function to provide that fragment
public void setListener(Friends2Fragment: listener) {
this.listener = listener
}
...
public MyAdapter.myViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
...
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
// look up the actual user
User user = userArrayList.get(myViewHolder.getAdapterPosition());
// call a function on your fragment
listener.onItemClick(user, "ADD_FRIEND");
}
}
});
}
Then add the callback function your adapter uses, and also set your fragment on the adapter as a listener:
// Friends2Fragment
// You should REALLY be doing this in onViewCreated or something, so this setup happens once.
// You're losing all your state by creating a new adapter whenever the user returns to the app
public void onStart(){
...
myAdapter = new MyAdapter(friendslist,v.getContext());
// set the fragment as the listener
myAdapter.setListener(this);
recyclerView.setAdapter(myAdapter);
}
// now add the function the adapter calls
private void onItemClick(User user, String someString) {
// handle the clicked user
}
A better way is to create an interface with all the events that need to be handled, and make your Fragment implement those. It breaks the hard association with the Fragment since you could pass any object that implements those functions, and it's also clearer because the interface kinda documents all the data the adapter produces, and that a listener needs to be able to handle. Something like this:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myViewHolder> {
// the listener is now something that implements the Callbacks interface
private Callbacks listener;
...
// nesting it inside MyAdapter makes the path MyAdapter.Callbacks, which makes it clear
// exactly what it is and what it relates to, and kinda gives the Adapter "ownership"
interface Callbacks {
void addFriend(User user)
}
And then you just make the Fragment implement that interface
public class Friends2Fragment() extends Fragment implements MyAdapter.Callbacks {
...
// implement all the callbacks you need to handle
override public void addFriend(User user) {
// do the thing
}
// set it in the same way, since this Fragment implements MyAdapter.Callbacks
myAdapter.setListener(this);
Which is a bit neater and cleaner, I think - but slightly more work. Also if you notice, I renamed the callback function from the generic handleItemClick to the more specific addFriend - so instead of having to pass a String saying what kind of click it is, you just have a function for each event you want to handle, and you can name them appropriately

How to use bind function of groupie library in java?

I am trying to show a list of user in recyclerView and trying to connect the textview layout in bind method in groupie library and i don't know how to link the id of layout to recyclerview viewHolder? and also how to use picasso library in viewholder?
private void fetchUser(){
DatabaseReference fireBaseReference = FirebaseDatabase.getInstance().getReference().child("/userList");
fireBaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
GroupAdapter groupA = new GroupAdapter<ViewHolder>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
Log.d("userList",snapshot.toString());
Users string = snapshot.getValue(Users.class);
groupA.add(new UserItem());
}
recyclerView.setAdapter(groupA);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
class UserItem extends Item<ViewHolder>{
// public UserItem(Users users){
// }
private Users users = new Users();
#Override
public void bind(#NonNull ViewHolder viewHolder, int position) {
viewHolder.itemView.findViewById(R.id.user_name_from_user_list);
viewHolder.
Picasso.get().load(users.getUri()).into(viewHolder.itemView.findViewById(R.id.user_photo_from_user_list));
}
#Override
public int getLayout() {
return R.layout.user_list_view_layout;
}
}
Groupie abstracts away the complexity of multiple item view types. Each Item declares a view layout id, and gets a callback to bind the inflated layout. That's all you need; you can add your new item directly to a GroupAdapter and call it a day.
Item with Kotlin
The Item class gives you simple callbacks to bind your model object to the generated fields. Because of Kotlin Android extensions, there's no need to write a view holder.**
class SongItem(private val song: Song) : Item() {
override fun getLayout() = R.layout.song
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.title.text = song.title
viewHolder.artist.text = song.artist
}
}
Item with data binding:
The Item class gives you simple callbacks to bind your model object to the generated binding. Because of data binding, there's no need to write a view holder.
If you're converting existing ViewHolders, you can reference any named views (e.g. R.id.title) directly from the binding instead.
#Override public void bind(SongBinding binding, int position) {
binding.title.setText(song.getTitle());
}
or you can do this way
#Override
public void bind(#NonNull final ViewHolder viewHolder, final int position) {
circleImageView = viewHolder.itemView.findViewById(R.id.circleImageViewForLatestMessage);
userMessage = viewHolder.itemView.findViewById(R.id.textView2);
userName = viewHolder.itemView.findViewById(R.id.textView41);
userName.setText(name);
userMessage.setText(messages);
You can also mix and match BindableItem and other Items in the adapter, so you can leave legacy viewholders as they are by making an Item.
For more information visit Groupie Library

How delete a row of recycler view from ViewHolder

thanks to stop by.
Not a long time ago someone explain to me that you should'nt store data in ViewHolder, I get why. But then it complicate something.
I want to remove a Row if user click on a button inside the row. So i need to access adapter. But I can't store it on ViewHolder. What the way ?
I'am also looking for the best recyclerview article that you know because it look like people don't know what they talking about in most of the stack overflow and give bad advise (Store data inside ViewHolder)
EDIT : I'am trying one solution but getTag return me null when I'am in adapter
Here is my item and the binding :
public final class ItemViewMail extends RecyclerView.ViewHolder {
private AppCompatImageButton cancelButton;
public ItemViewMail(View itemView) {
super(itemView);
this.cancelButton = itemView.findViewById(R.id.profile_item_edit_email_cancel_image_button);
}
public void bind(Data data, View.OnClickListener deleteOnClickListener) {
this.itemView.setTag(data);
if (cancelButton != null) {
cancelButton.setOnClickListener(deleteOnClickListener);
}
}
}
My adapter :
private ArrayList<Data> rowDataArrayList;
MyPVPViewAdapter(ArrayList<Data> rowDataArrayList) {
this.rowDataArrayList = rowDataArrayList;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int itemType) {
return new ItemViewMail(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.profile_item_edit_email, viewGroup ,false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder myViewHolder, int position) {
((ItemViewMail) myViewHolder).bind((Data) rowDataArrayList.get(position).getData(), onClickListener);
}
#Override
public int getItemViewType(int position) {
return rowDataArrayList.get(position).getType();
}
#Override
public int getItemCount() {
return rowDataArrayList.size();
}
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
Data data = (Data) v.getTag();
}
};
}
You can remove the item from the adapter and notify it about the removed row and it will update smoothly with the removal. Also remove the item from your data source, seems like you're doing that already though.
mAdapter.getItems().remove(mPosition);
mAdapter.notifyItemRemoved(mPosition);
data class MyItem(val name: String)
class MyViewHolder(itemView: View,
private val onRemoveClickListener: View.OnClickListener) : RecyclerView.ViewHolder(itemView) {
fun bind(item: MyItem) {
itemView.setOnClickListener(onRemoveClickListener)
itemView.setTag(item)
//todo: eg. itemView.name.setText(item.name)
}
}
class MyRecyclerViewAdapter(private val onRemoveClickListener: View.OnClickListener): RecyclerView.Adapter<MyViewHolder>() {
var items = listOf<MyItem>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.viewholder_my)
return MyViewHolder(view, onRemoveClickListener)
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(items[position])
}
}
Now you can create MyRecyclerViewAdapter inside your activity/fragment/etc and put onClickListener as a parameter.
class MyActivity: AppCompatActivity() {
private lateinit var adapter: MyRecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = MyRecyclerViewAdapter(View.OnClickListener { view ->
val clickedItem = view.getTag() as MyItem
adapter.items.remove(clickedItem)
adapter.notifyDataSetChanged()
})
}
}
I want to remove a Row if user clic on a button inside the row. So i need to access adapter. But I can't store it on ViewHolder. What the way ?
The base implementation of RecyclerView.ViewHolder provides two methods you can call when you need access to position information: getAdapterPosition() and getLayoutPosition(). In your case, it sounds like you want the first one.
This lets you write code like this:
public void soSomethingOnClick() {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
myDataStructure.removeItemAt(position);
myAdapter.notifyItemRemoved(position);
}
}
Not a long time ago someone explain to me that you should'nt store data in ViewHolder, I get why. But then it complicate something.
I'm not sure what you heard, but storing information in a ViewHolder is totally fine. You have to be careful to make sure you always update whatever data you're storing so that it doesn't get out of sync when a ViewHolder is recycled, but very many ViewHolder implementations take express advantage of the fact that ViewHolders are a great place to save data.

Android MVP - how to work with logic dependent on Android classes?

I'm working on an existing app, migrating it towards the MVP architecture. In one of the RecyclerView.Adapters, there's quite a lot of logic in regards to how a header View should look like. According to the MVP architecture, such logic should be moved to the Presenter and the presenter should use the View to display things. Also, I've read quite a lot about how if you have Android classes in your presenter - that basically indicates that something went wrong.
So my thoughts so far are to create a Presenter for the ViewHolder when the ViewHolder (also the View from the MVP standpoint) is created and to move my logic there. Howeverm the problem I'm having is that the logic is based on Android classes such as Paint, Rect, Spannable and so on.
So how do I solve this problem in the cleanest way possible? Do I move all the logic to the View? Or should I keep the logic in my Presenter, but move only the calculations related to the specific Android objects to the View? As an example that would look something like that:
in presenter:
double textLines = mView.getTextLines(text, 0 , text.length());
in view:
Paint paint = mTextView.getPaint();
Rect bounds = new Rect();
paint.getTextBounds(text, start, end, bounds);
double parentWidth = parentView.getWidth() - parentView.getPaddingLeft() - parentView
.getPaddingRight();
return Math.ceil((double) bounds.width() / parentWidth);
Or is there another better way?
Every piece of code that depends on Android classes should be kept on the view implementation (Activiy, Fragment, ViewHolder etc) - the view should be as dumb as possible. I think you don't need a presenter for the ViewHolder, as it usually should not have logic.
If you have unit tests (and you should), the process becomes easier to grasp because you will be forced to move Android dependent classes to somewhere else in order to be able to test it.
For example, a real case scenario I have with RecyclerView is something like this: one view interface for the Activity, one view interface for the ViewHolder, one presenter for the Adapter and one presenter for the Activity. The following is a partial, non compilable example just for demonstration, where you can see the separation of concerns between the layers.
So, in your case, the approach is correct: the view interface just have a method that returns a string "from somewhere", while in the real android view you use the actual classes and methods from the Android API to retrieve the requested data.
My example is below.
// Definitions for the event adapter stuff
public interface EventAdapterContract {
interface View {
void onFetchEventsFailed(String reason);
void notifyDataSetChanged();
}
interface Presenter {
void getItemViewType(int position);
void getItemCount();
void bindEventRow(int position, ViewHolder holder, int backgroundColor);
}
interface ViewHolder {
void setTitle(String value);
void setHour(String value);
void setBackgroundColor(int color);
}
}
// Adapter presenter implementation. Again, partial code for simplicity
public class EventAdapterPresenter implements EventAdapterContract.Presenter {
private final EventAdapterContract.View view;
private final List<Event> events;
static int VIEW_FINISHED = 0;
static int VIEW_pending = 0;
public EventAdapterPresenter(EventAdapterContract.View view, EventAPI api) {
this.view = view;
this.events = new ArrayList<>();
// EventAPI is an interface (Retrofit in this case), which can
// also be tested with plain junit
}
#Override
public int getItemCount() {
return events.size();
}
#Override
public int getItemViewType(int position) {
Event item = events.get(position);
return item.isFinished() ? VIEW_FINISHED : VIEW_PENDING;
}
#Override
public void bindEventRow(int position, EventAdapterContract.ViewHolder holder, int backgroundColor) {
Event event = events.get(position);
holder.setTitle(event.getTitle());
holder.setHour(String.format("Event time: %s", event.getStartTime()));
holder.setBackgroundColor(backgroundColor);
}
}
// The adapter itself. Please mind that this is a partial
// piece of code just for the sake of demonstration,
// I ommited several parts to keep it simple
public class EventAdapter
extends RecyclerView.Adapter<RecyclerView.ViewHolder>
implements EventAdapterContract.View {
private EventAdapterContract.Presenter presenter;
public class EventAdapter(Contex context) {
// Regular adapter contructor
// ...
presenter = new EventAdapterPresenter(this, someImplOfEventApi);
}
#Override
public void onFetchEventsFailed(String reason) {
// Show an AlertDialog
}
#Override
public int getItemViewType(int position) {
return presenter.getItemViewType(position);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int backgroundColor = ContextCompat.getColor(context, R.color.colorEventPending);
presenter.bindEventRow(position, holder, backgroundColor);
}
}
public class EventRowViewHolder
extends RecyclerView.ViewHolder
implements View.OnClickListener, EventAdapterContract.ViewHolder.Row {
private TextView title;
private TextView hour;
public EventRowViewHolder(View view) {
super(view);
// init widgets etc...
view.setOnClickListener(this);
}
#Override
public void setTitle(String value) {
title.setText(value);
}
#Override
public void setHour(String value) {
hour.setText(value);
}
#Override
public void setBackgroundColor(int color) {
this.itemView.setBackgroundColor(color);
}
#Override
public void onClick(View view) {
EventBus.getDefault().post(new OpenEventDetailsMessage(orderId));
}
}

RecyclerView CardView onItemClick

Got stuck on this problem for few days hope to find the solution.
I am unable to pass the data written on the card (Card View) to the next activity.
For clicking card, I am implementing its onClickListner in its recyclerView Adapter.
I want to pass the position of the card clicked but since card View does not support onItemClickListner I am facing the problem.
RecyclerViewAdapter class
public class RVAdapter extends recyclerView.Adapter<RVAdapter.PersonViewHolder> implements View.OnClickListener, View.OnLongClickListener {
int position;
#Override
public void onBindViewHolder(PersonViewHolder holder, int i) {
setEventsForViews(holder, i);
}
private void setEventsForViews(PersonViewHolder holder, int i) {
position = i;
holder.cv.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Navigator.toAddFriends(context, placeList.get(getPosition()).getPlaceId());
}
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cv;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
}
}
I tried to set up a variable position and tried to pass its value but since onBindViewHolder is called intially for all items in the view and I end up in setting the value of position as last one. I know this is the wrong approach but as I am new to this card View thing please suggest me any good and clean way to do this.
Thanks in advance.
Create a new interface, like:
public interface RecyclerViewClickListener {
public void recyclerViewListClicked(View v, int position);
}
On your Fragment/Activity, implement the new interface:
public class MyFragment extends BaseFragment implements RecyclerViewClickListener { }
Now, you will be forced to implement the interface method recyclerViewListClicked(View v, int position) on your Fragment/Activity.
Do that so you can do whatever you want when one item is clicked. Do it like so:
#Override
public void recyclerViewListClicked(View v, int position) {
myList.get(position);
}
Now lets go to your Adapter.
On your adapter constructor, add a new listener parameter. It's type is the interface we have just created (don't forget to create a private static listener and set it on your constructor):
private static RecyclerViewClickListener mListener;
public myListAdapter(List<Thing> things, final Context context, RecyclerViewClickListener itemClickListener) {
mThings = things;
mContext = context;
mListener = itemClickListener;
}
Inside your adapter, on your ViewHolder, implement View.OnClickListener like:
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
You will now be forced do #Override the onClickMethod inside your ViewHolder. Do it:
#Override
public void onClick(View v) { }
On your ViewHolder constructor, set the onClickListener of your view. Like:
public MyViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
myCardViewLayout.setOnClickListener(this);
}
Now implement the behaviour to your ViewHolder onClickMethod. In this case, I call the recyclerViewListClicked method of the static listener we declared in our adapter. This method is the one we have #Overriden in our fragment/activity, so it requires the position parameter.
Get the position by calling getLayoutPosition() inside your view holder. It will look like this:
#Override
public void onClick(View v) {
mListener.recyclerViewListClicked(v, getLayoutPosition());
}
Finally, get back to your fragment/activity, and pass the listener to the adapter constructor with this (since now we implement the interface RecyclerViewClickListener it can just pass itself as a listener to the adapter):
mRecyclerView.setAdapter(new MyListAdapter(this.mList, this.getActivity(), this))
Now, when you click on a myCardViewLayout on your list items it's going to call the recyclerViewListClicked(View v, int position) of your Fragment/Activity and you will be able to work with the position parameter as you'd like.
You can do this to any layout you want to get a click event from inside your list. Just replace myCardViewLayout.setOnClickListener(this) inside your ViewHolder constructor with myDesiredToHaveOnClickListenerLayout.setOnClickListener(this).
Hope this helps.
#EDIT:
Some tips:
Remove implements View.OnClickListener, View.OnLongClickListener from your adapter.
And looks like you don't need to call setEventsForViews(holder, i) on your onBindViewHolder anymore. Confirm?
#UPDATE
This is how your ViewHolder will look like:
public static class PersonViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
CardView cv;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
cv.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mListener.recyclerViewListClicked(v, getLayoutPosition());
}
}

Categories

Resources