I have two View holders . On clicking a button in one view I want a text to get updated in another view of the Recycler.
It works fine with getRootview().
But on scrolling when the view gets hidden, recycler crashes (as getRootView no longer returns anything).
How can I implement this ?
ViewHolder1:
public static class CartHeader extends RecyclerView.ViewHolder {
public TextView list_cart_header_textView_total;
private TextView list_cart_header_textView_title;
}
ViewHolder2:
public class CartDBItem extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView list_cart_product_imageView_add;
}
onClick of list_cart_product_imageView_add:
CartHeader ch=new CartHeader(view.getRootView());
storeHeader=Double.parseDouble(ch.list_cart_header_textView_total.getText().toString());
Lists in Android use the Model-View-Controller paradigm. The RecyclerView is the View, of course, and the adapter is the Model.
Within the Controller (onClick handler), when you want to change something in the View you change the Model then update the View. So you change the text at its source in the adapter, then call notifyDataSetChanged() to let the RecyclerView know to refresh its views from the adapter.
You should only access view holders when creating their layouts or binding their data.
Related
I don't see why it is effective to apply the listener to the holder.
I spent much of the day looking at articles. The topics here on stackoverflow covered the range well.
The choices I found:
Attach to TextView - creates multiple listeners.
Attach to ViewHolder - again creates multiple listeners, unless you create a listener and then use that to attach which you can also do with the TextView.
Attach a onItemTouchListener - a surprisingly complicated way.
What I did not see was just a straight forward implements View.OnClickListener attached to the adapter. If you do this, then generate the one public void onClick(View view), and give the adapter a copy of the RecyclerView and then assign the Adapter (this) as the listener to the item view you have inflated in onCreateViewHolder. Then access the position through the RecyclerView getChildAdapterPosition function. See code snippets.
//create adapter class implementing the on click listener
public class WordListAdapter extends RecyclerView.Adapter<WordListAdapter.WordViewHolder> implements View.OnClickListener {
private final LinkedList<String> mWordList;
private LayoutInflater mInFlater;
private final RecyclerView recyclerView;
//in constructor, pass in RecyclerView created in MainActivity
public WordListAdapter(Context context, LinkedList<String> wordList, RecyclerView rView) {
recyclerView = rView; <----
mInFlater = LayoutInflater.from(context);
this.mWordList = wordList;
}
//implementation of View.OnClickListener makes
#Override
public void onClick(View view) {
//get the position of the item that was clicked
int mPosition = recyclerView.getChildAdapterPosition(view); <----
//other code to do what you want with the list(eg. String element = mWordList.get(mPosition);)
}
//And you set your pointer in the adapter class
#Override
public WordListAdapter.WordViewHolder onCreateViewHolder( #NonNull ViewGroup parent, int viewType) {
View mItemView = mInFlater.inflate(R.layout.wordlist_item, parent, false);
mItemView.setOnClickListener(this); <----
return new WordViewHolder(mItemView, this);
}
It seams to me that this answer is simple, and has the advantage of a single thread on click listener that is all handled in the list adapter.
I guess, I mostly wanted to share my solution on a closed topic but I certainly welcome any suggestions as to why this might not be better. This is actually, my solution to challenge2 in Android fundamentals 04.5 RecyclerView.
The primary reason is is suggested to attach the onClickListener to the view holder as opposed to the adapter itself is because the view is what is being clicked on when a user clicks on an item within the Recyclerview.
The Holder you refer to is a class that contains the view and applying the click listener to this holder allows you to differentiate which view (position / item) is actually being clicked.
It's similar to explaining why you would attach a click listener to the button in a layout versus the entire layout itself. Sure, you could attach the click listener to the entire layout, but a user might intend to click on something else in your view as opposed to the button and you would have no way of distinguishing which one was clicked since your clickListener does not help to identify which.
Hi All!
I am trying to create a listView with the same row layout. But I am changing the elements of the row dynamically. From the diagram above, you can see that I populate my listView with rows but then the buttons on the row will have to be different based on the type of row. The rest of the information in the row is the same.
Is there a way I can pass a flag into the adapter to make it add/remove elements from the layout based on the type of row? My adapter extends BaseAdapter.
Yes, Make your ListView adapter take a model that contains a flag as such in my answer.
This should work if you are converting your JSON object to a specific POJO with a parser like GSON. If you are trying to load your adapter from a JSON object I would recommend against that, so good luck.
public class Row{
private RowType rowType;
//all other row attributes
}
public enum RowType{
YesNo, Cancel, //all other possible scenarios
}
public class YourAdapter extends BaseAdapter{
private List<Row> rowsToPopulate;
//Override appropriate methods
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//load your view etc....
//Check the flag to load appropriate fragment
switch(rowsToPopulate.get(position).getRowType()){
case YesNo:
//Load your yes no fragment to your row view
case Cancel:
//Load your cancel fragment to your row view
}
return yourView;
}
This information should be contained in the model that the adapter knows about.
In the getView(..) method, you should be able to check if <model>.getType() is a specific type. If so, you can set visibility of specific components in your layout and change the UI accordingly.
If the type is not a part of your response, you can keep a Map<Model, Type> inside the adapter and update the adapter when you have new data, and have a convenience method to convert this to a list of model objects to display in the UI.
I create a RecyclerView with CardViews in my app. Now I want to add inside each CardView a new List of custom Views, with a custom layout (see example Image). When the add button is pressed, there should be insert a new row of this layout inside the card.
How do I realize that? In the internet I readed, that a ListView inside the CardView is not possible, because a ListView is scrollable, and there should not be two scrollable Views on the activity (thats what I found..)
the red marked row is the custom row.
for the RecylcerView I used the ViewHolder, CustomAdapter, etc.
Do I need this for the rows inside to? or is there a simpler way?
What I would do is attached a click handler to the Views is your ViewHolder. In this event handler you can delegate to a presenter of some sort that will add/remove/edit the View in the card holder.
private class MyHolder extends ViewHolder
implements View.OnClickListener {
...
public MyHolder(View itemView, MyPresenter presenter) {
super(itemView);
itemView.setOnClickListener(this);
...
}
#Override
public void onClick(View v) {
presenter.addRow(v);
}
}
I am working on xamarin android apps. I used recycler view for displaying the images in each row. Now for each item image there is some link which I need to redirect. How can I achieve this onclick action for each image in recycler view.
Please help me in this regard.
Thanks
When creating a recycler view, you need to create a RecyclerView adapter which (among other things) implements methods for creating and binding a viewholder to the item in the recycler view. Somewhere in your code (oftentimes within this recycler view adapter class), you need to define the viewholder that you will use for your recyclerview items. This is where you should assign the onClickListener to your imageView.
Here is an example of a viewholder definition that I think may help you:
public class YourViewHolder extends RecyclerView.ViewHolder {
protected ImageView yourImage;
public YourViewHolder(View v) {
super(v);
final View theView = v;
yourImage = (ImageView) v.findViewById(R.id.yourImage);
yourImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// image clicked... do something
}
});
}
}
Let me know if you need more information on how to set up the recycler view adapter class (I have assumed that you already have done this, but I could be mistaken).
i have a listview with pictures and texts in each element. I have to be able to change one of the image if the user clicks on it and change it back to the original if the user clicks it again. I have a base adapter class that i use to add the data to the listview. in the getview method i set a onclick listener to the image to change it. The problem i'm having is it only changes if i scroll away from the element and come back to it. how can i change so that it will update the listview with out scrolling away from it.
You can use the method
notifyDataSetChanged();
after you change the image src in your listViewAdapter (who extends BaseAdapter)
eg.
public class ProductItemListView extends ListView {
public ProductItemListView() {
super(context);
mProductAdapter = new ProductItemAdapter(context);
this.setAdapter(mProductAdapter);
}
}
public class ProductItemAdapter extends BaseAdapter {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
...
myImage.setBackgroundResource(R.drawable.other_image);
notifyDataSetChanged();
}
}
Simply set a OnItemClickListener on your ListView.
The rest is pretty straight-forward using ImageView.setImageDrawable or similar.