I've a strange problem with my recyclerView adapter, I just want to show/hide ImageView depending of the selection but when I call the notifyItemChanged(selection) in my click listener, it call onCreateViewHolder and take a delay to refresh de view, I don't know why and I didn't find another solution to perform what I need.
This is my adapter:
public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.ChannelHolder> {
private ArrayList<Integer> channelList;
private Integer selection = 0;
public ChannelAdapter(ArrayList<Integer> channelList) {
this.channelList = channelList;
}
#NotNull
#Override
public ChannelHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder: ");
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_channel_item_selected, parent, false);
ChannelHolder holder = new ChannelHolder(view);
return holder;
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(ChannelHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: "+position);
holder.textViewChannelId.setText("#"+channelList.get(position));
holder.textViewChannel.setText(channelList.get(position).toString());
if(position==selection){
holder.imageViewSelectorLeft.setVisibility(View.VISIBLE);
holder.imageViewSelectorRight.setVisibility(View.VISIBLE);
}
else{
holder.imageViewSelectorLeft.setVisibility(View.INVISIBLE);
holder.imageViewSelectorRight.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
return channelList.size();
}
public class ChannelHolder extends RecyclerView.ViewHolder {
TextView textViewChannel, textViewChannelId;
ImageView imageViewSelectorLeft, imageViewSelectorRight;
public ChannelHolder(View itemView) {
super(itemView);
textViewChannelId = itemView.findViewById(R.id.textViewChannelId);
textViewChannel = itemView.findViewById(R.id.textViewChannel);
imageViewSelectorLeft = itemView.findViewById(R.id.imageViewSelectorLeft);
imageViewSelectorRight = itemView.findViewById(R.id.imageViewSelectorRight);
itemView.setOnClickListener(v -> {
notifyItemChanged(selection);
selection=getAdapterPosition();
notifyItemChanged(selection);
});
}
}
}
Do I miss something or am I doing it by the wrong way?
Thanks in advance for any help
Edit :
I tried to use notifyItemChanged with a payload set to 1 and override onBindViewHolder to get the payload but it still call onCreateViewHolder, even when mSupportsChangeAnimations is set to false
By default, your RecyclerView will have a DefaultItemAnimator attached to it. When you call notifyItemChanged() on your adapter, the system will eventually call through to the DefaultItemAnimator to find out whether it needs to create a new ViewHolder or if it can "re-use" the existing one.
#Override
public boolean canReuseUpdatedViewHolder(#NonNull ViewHolder viewHolder,
#NonNull List<Object> payloads) {
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}
The superclass implementation:
#Override
public boolean canReuseUpdatedViewHolder(#NonNull RecyclerView.ViewHolder viewHolder) {
return !mSupportsChangeAnimations || viewHolder.isInvalid();
}
These suggest that there are two easy ways to make sure that the ViewHolder is reused instead of recreated:
Make sure that the payloads list is not empty. This is done by calling adapter.notifyItemChanged(position, payload). It doesn't matter what the payload is, as long as it is non-null.
Set mSupportsChangeAnimations to false for your DefaultItemAnimator.
DefaultItemAnimator animator = (DefaultItemAnimator) recyclerView.getItemAnimator();
animator.setSupportsChangeAnimations(false);
You can call your adapter like this
YourAdapter adapter = new YourAdapter(yourList, yourActivity.this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
and then you can put this code on itemClick in adapter
if(position==getAdapterPosition()){
holder.imageViewSelectorLeft.setVisibility(View.VISIBLE);
holder.imageViewSelectorRight.setVisibility(View.VISIBLE);
}
else{
holder.imageViewSelectorLeft.setVisibility(View.INVISIBLE);
holder.imageViewSelectorRight.setVisibility(View.INVISIBLE);
}
Related
I am displaying a recycler view on the whole screen the recycler view has only item an image view i want to know how i can get the positon of the imageview shown on my screen outside the recycler adapter.
I have tried to implement an interface but the results are not accurate.I want to save the current position instantly.
My recycler adapter:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHOlder> {
private List<ImageList> mlist;
private showPageNumber page;
public RecyclerAdapter(List<ImageList> dataList, showPageNumber page) {
mlist = dataList;
this.page = page;
}
#NonNull
#Override
public MyViewHOlder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item,parent,false);
return new MyViewHOlder(view);
}
#Override
public void onBindViewHolder(#NonNull MyViewHOlder holder,int position) {
Picasso.get().load(mlist.get(position).getUrl()).into(holder.photoView);
page.showPage(position);
holder.pageNumber.setText(position+1+"");
}
#Override
public int getItemCount() {
return mlist.size();
}
class MyViewHOlder extends RecyclerView.ViewHolder{
ImageView photoView;
TextView pageNumber;
public MyViewHOlder(#NonNull View itemView) {
super(itemView);
photoView = itemView.findViewById(R.id.pdfImage);
pageNumber = itemView.findViewById(R.id.pageNumber);
}
}
public interface showPageNumber{
void showPage(int position);
}
}
Whenever I implements the showPageNumber interface on my MainActivity the showPage method does't give accurate results on scrolling the items in recycler view.
I am not completely sure what you like to accomplish but if you like to find out which item is currently shown you might want to try this:
Assuming you are using a LinearLayoutManager on your RecyclerView you could listen to Scroll events an evaluate which page is shown when the ReclycerView settles after scrolling
RecyclerView recyclerView = findViewById(R.id.rcv);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
// if you just want to know if the new "page" is completely visible
if(newState == RecyclerView.SCROLL_STATE_SETTLING){
int pagePosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
// if you just want to know if the new "page" comes in view
int pagePosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
}
}
});
Alright, I stumbled into this problem and I ran here, got disappointed with the output I got from other answers and here.
But here is what I found.
The concept behind it is to monitor where the recyclerview is going to and update a global variable. In my case I had cancelled scroll gestures on the recyclerView and I wanted it to only scroll smoothly when I press a button.
So, here I monitored the recyclerview position and updated my values accordingly.
//Global variable:
var rvPosition = 0
onClick(v: View?){
when (v?.id) {
R.id.schedule_previous -> {
if (rvPosition > 0) {
smoothScroller.targetPosition = rvPosition - 1
binding.scheduleRv.layoutManager?.startSmoothScroll(smoothScroller)
rvPosition -=1
}
}
R.id.schedule_next -> {
if (rvPosition < dataset.size) {
smoothScroller.targetPosition = rvPosition + 1
binding.scheduleRv.layoutManager?.startSmoothScroll(smoothScroller)
rvPosition +=1
}
}
R.id.schedule_users_card -> {}
}
}
What I would suggest is creating a lambda that takes a value (here it would be the item position), and assigns it to the variable you want.Then, pass that lambda to the recycler and use it there to assign value to your variable.
That way, now you have your variable value wherever you want.
Simple enough if I do say so myself ;)
I want my recycler view rows to be highlighted after a specific interval of time, say after 2 seconds.
Searched all over the internet but no luck so far.
How about, in the recycler adapter OnBindViewHolder method, put a [Handler.postDelayed](https://developer.android.com/reference/android/os/Handler.html#postDelayed(java.lang.Runnable, long)) in which you set the specific time where you want the item to change.
Inside the runner that you pass in the handler, you put in a boolean flag to check if the row will have a different colour/behaviour + a notifyDataSetChanged() in the adapter. (You will have to change your data object to accomodate this new variable)
The question is not very clear. I had two questions in mind that I mentioned in the comment of the question.
Do you want to highlight some specific rows?
Do you want to toggle the highlight after each two seconds?
So I'm going for a general solution for both.
Let us assume the object you're populating in your each row is like the following.
public class ListItem {
int value;
boolean highlight = false;
}
The list of ListItem object can be inserted in an ArrayList to be populated in the RecyclerView. Here is your adapter which may look like this.
// Declare the yourListItems globally in your Activity
List<ListItem> yourListItems = new ArrayList<ListItem>();
populateYourListItems();
public class YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public class YourViewHolder extends RecyclerView.ViewHolder {
private final TextView valueTextView;
private final LinearLayout background;
public YourViewHolder(final View itemView) {
super(itemView);
valueTextView = (TextView) itemView.findViewById(R.id.value_text_view);
background = (LinearLayout) itemView.findViewById(R.id.background);
}
public void bindView(int pos) {
int value = yourListItems.get(pos).value;
boolean isHighlighted = yourListItems.get(pos).hightlight;
valueTextView.setText(value);
// Set the background colour if the highlight value is found true.
if(isHighlighted) background.setBackgroundColor(Color.GREEN);
else background.setBackgroundColor(Color.WHITE);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_activity_log, parent, false);
return new YourViewHolder(v);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
try {
if (holder instanceof YourViewHolder) {
YourViewHolder vh = (YourViewHolder) holder;
vh.bindView(position);
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public int getItemCount() {
if (yourListItems == null || yourListItems.isEmpty())
return 0;
else
return yourListItems.size();
}
#Override
public int getItemViewType(int position) {
return 1;
}
}
Now when you want to highlight some specific items of your RecyclerView you need to just set the highlight value to true and then call notifyDataSetChanged() to bring into the change in effect.
So you might need a timer like the following which will highlight your rows as per your demand in every two seconds.
// Declare the timer
private Timer highlightTimer;
private TimerTask highlightTimerTask;
highlightTimer = new Timer();
highlightTimerTask = new TimerTask() {
public void run() {
highLightTheListItems();
}
};
highlightTimer.schedule(highlightTimerTask, 2000);
Now implement your highLightTheListItems function as per your need.
public void highLightTheListItems() {
// Modify your list items.
// Call notifyDataSetChanged in your adapter
yourAdapter.notifyDataSetChanged();
}
Hope that helps. Thanks.
Do you mean highlight as in colour the row's background? If so, you could do this in your listViewAdapter
#Override
public View getView(final int position, View row, ViewGroup parent){
if (row==null){
row = LayoutInflater.from(getContext()).inflate(mResource, parent, false);
}
if(foo){
row.setBackgroundColor(getResources().getColor(R.color.translucent_green));
}
else row.setBackgroundColor(Color.TRANSPARENT);
return row;
}
Then in colours.xml
<color name="translucent_green">#667cfc00</color>
The first 2 numbers(66) is the alpha value, ie opacity. The next 6 are RBG in hexadecimal.
I have implemented my RecyclerView with it's Custom Adapter as follows
Global Declarations as follows
private LinearLayoutManager linearLayoutManager;
private int pastVisibleItems, visibleItemCount, totalItemCount;
private CustomRecyclerViewAdapter customRecyclerViewAdapter;
First I created Adapter Instance inside onCreate() method which has Empty Array inside it and set it to recyclerView
linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
Utility.ItemDecorationConst);
recyclerView.addItemDecoration(dividerItemDecoration);
customRecyclerViewAdapter = new CustomRecyclerViewAdapter(getActivity());
recyclerView.setAdapter(customRecyclerViewAdapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
visibleItemCount = linearLayoutManager.getChildCount();
totalItemCount = linearLayoutManager.getItemCount();
pastVisibleItems = linearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
loading = false;
customRecyclerViewAdapter.addProgressBarEntry();
controller.getNextPage(PublisherAppContainerFragment.this);
}
}
}
});
After rendering complete View when I get data from AsyncTask for filling in recyclerView
I call following method of the Adapter to fill data
customRecyclerViewAdapter.addAll(myArray);
note : addAll() is not any overridden method
following is code of my CustomRecyclerViewAdapter
class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> {
ArrayList<MyModel> arrayList = new ArrayList<>();
Context context;
public CustomRecyclerViewAdapter(Context context) {
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewHolder viewHolder = null;
//inflated some view
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
//binded data to holder
}
#Override
public int getItemCount() {
return arrayList.size();
}
public void addAll(ArrayList myArray) {
this.arrayList.addAll(myArray)
}
public void clear() {
arrayList.clear();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public CardView cardView;
public ViewHolder(View view) {
super(view);
this.cardView = (CardView) view.findViewById(R.id.card_view);
this.cardView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
//handle operations
}
}
}
So whenever I get data from AsynTask I call method addAll() and recyclerView works like charm.
Now, My question is how it's working very well even though I have never called notifyDataSetChanged() on the adapter. Are there any previously registered Observers for the adapter? who observes if the dataset which has been returned in public int getItemCount() has been changed?
As I have read from documentation
void notifyDataSetChanged ()
Notify any registered observers that the data set has changed.
that means even though there are some observers registered you need to notify them using notifyDataSetChanged(). Right?
I also called
boolean flag = customRecyclerViewAdapter.hasObservers();
to know if there are any observers registered? Flag is True.
So anyone would please help me understand how exactly these things work?
If you look at the source of RecyclerView setAdapter call you will find a method setAdapterInternal(adapter, false, true);which is responsible for
Replaces the current adapter with the new one and triggers listeners.
This method is responsible for swapping the old adapter with the new one and internally it also registers for the custom Data Observer. This is the reason you are getting the flag as true
Based on what I can see of your code, I would say that there are not any observers attached to your RecyclerView that are picking up changes and keeping the list updated. What is more likely is that you are just getting "lucky" as when you scroll through the list the layout manager is continually calling getItemCount() on the adapter to determine if it should show more items. Whenever you call addAll(), you silently update the item count and it just happens to appear that observers were notified of the changes.
This is definitely a bug, and you would more likely see its effects in your implementation if you were dependent on a particular observer to monitor some aspect of the list, or doing more than just appending new items to the bottom (for example altering or inserting between existing items). The correct implementation as you pointed out is to call notifyDataSetChanged() whenever the list is updated, or even better be more specific with what changed if you can. For example, you can use:
public void addAll(ArrayList myArray) {
int positionStart = getItemCount() - 1;
this.arrayList.addAll(myArray);
notifyItemRangeInserted(positionStart, myArray.size());
}
public void clear() {
int oldSize = getItemCount();
arrayList.clear();
notifyItemRangeRemoved(0, oldSize);
}
My question is how it's working very well even though I have never called notifyDataSetChanged() on the adapter
It's because the addAll method by default calls the notifyDataSetChanged().
public void addAll(T ... items) {
synchronized (mLock) {
if (mOriginalValues != null) {
Collections.addAll(mOriginalValues, items);
} else {
Collections.addAll(mObjects, items);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
And
public void addAll(#NonNull Collection<? extends T> collection) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.addAll(collection);
} else {
mObjects.addAll(collection);
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
Here's the link - https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ArrayAdapter.java
EDIT - I see that you have your own addAll method which is calling addAll method of ArrayList.
This is how addAll method works -
private ArrayList<String> ex1 = new ArrayList();
private ArrayList<String> ex2 = new ArrayList();
private ArrayList<String> ex3 = new ArrayList();
ex1.add("one");
ex2.add("two");
ex3.addAll(ex1);
ex3.addAll(ex2);
System.out.println(ex3);
OUTPUT - [one, two]
This is what happening in your case.
I have shown progress bar and once I fetch data I hide the progress bar and make recyclerView visible - If in layout or code you set RecyclerView visibility GONE then layout will not happen and that is why Adapter.getItemsCount() not get called. So if you fetch data and populate adapter array with it and then change RecyclerView visibility from GONE to VISIBLE it will trigger update.
In case you don't call notifyDataSetChanged() RecyclerView will not know about update. I guess there is something else in your code that trigger RecyclerView update. To clarify this behavior let's use some dummy adapter:
private class DummyViewHolder extends RecyclerView.ViewHolder {
public DummyViewHolder (View itemView) {
super(itemView);
}
}
private class Adapter extends RecyclerView.Adapter<DummyViewHolder> {
private int mDummySize = 5;
#Override
public DummyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.dummy_view, parent, false);
return new DummyViewHolder(view);
}
#Override
public void onBindViewHolder(DummyViewHolder holder, int position) {
}
void setSize(int size) { this.mDummySize = size; }
#Override
public int getItemCount() {
return mDummySize;
}
}
And in onCraete() :
ViewHolder v = ...
final Adapter adapter = ..
...
//postpone adapter update
(new Handler()).postDelayed(new Runnable() {
#Override
public void run() {
adapter.setSize(10);//and nothing happend only 5 items on screen
}
}, 5000);
Overview: I'm having a chat application. Till now, I was using CursorAdapter with a Listview to load my chat items in the list. But now, I'm planning to refactor the code to use RecyclerView with RecyclerView.Adapter and a "Load More" functionality like whatsapp.
Issue: Memory consumption. With CursorAdapter, items not in viewable area were getting Garbage Collected, but now since I'm using an ArrayList of my CustomModal, once you load all the items in the list (by clicking on the "Load More" button) I'm seeing high memory consumption in the memory logs (No Garbage Collection).
My guess is now, I'm loading all the items in an ArrayList and that is causing the issue. Is that it?
Is there a way to avoid the issue or optimize the problem?
EDIT:
Can't post the complete code here, but here is a snippet of the kind of Adapter that I've implemented:
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.MyViewHolder> {
private ArrayList<MyModal> mMyModals;
public MessageAdapter(ArrayList<MyModal> mMyModals) {
this.mMyModals = mMyModals;
//... Some fields initialization here
}
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
public void toggleLoadMore(boolean isLoadMoreEnabled){
if(isLoadMoreEnabled){
//..Checks if load more is already enabled or not
//..If not then enables it by adding an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}else{
//..Checks if load more is already disabled or not
//..If not then disables it by removing an item at 0th poition of MyModal list
//..Then notifyDataSetChanged()
}
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder messageViewHolder = null;
View itemLayoutView = null;
MyModal.MessageType messageType = MyModal.MessageType.getMessageTypeFromValue(viewType);
switch (messageType){
case MESSAGE_TYPE1:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout1, null);
messageViewHolder = new Type1ViewHolder(itemLayoutView);
break;
case MESSAGE_TYPE2:
itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout2, null);
messageViewHolder = new Type2ViewHolder(itemLayoutView);
break;
}
return messageViewHolder;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final MyModal myModal = mMyModals.get(position);
MyModal.MessageType messageType = myModal.getMessageType();
holder.initialize(myModal);
}
#Override
public int getItemCount() {
return (mMyModals != null)?mMyModals.size():0;
}
#Override
public int getItemViewType(int position) {
return mMyModals.get(position).getMessageType().getValue();
}
public abstract class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemLayoutView) {
super(itemLayoutView);
}
public abstract void initialize(MyModal myModal);
}
class Type1ViewHolder extends MyViewHolder {
//...Variables
public Type1ViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
class Type2ViewHolder extends MyViewHolder {
//...Variables
public TextViewHolder(View itemLayoutView) {
super(itemLayoutView);
//...variables initialization here
}
#Override
public void initialize(MyModal myModal) {
//...Setting values in view using myModal
}
}
}
First of all :
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals = myModals;
//... Some fields initialization here
notifyDataSetChanged();
}
Here you are creating a new arraylist and assigning it to your mMyModals. This means there are 2 arraylists at this point and they take up twice the amount of space than required. GC doesnt work the way you expect it to. Since the arraylist is initialized in your activity it will persist as long as the arraylist persists and so will the initial arraylist.
Instead of creating a new arraylist in your activity and passing it to changeList. Just clear your old arraylist and pass that.And also in adapter changeList method you can do the below
public void changeList(ArrayList<MyModal> myModals, boolean isLoadMoreEnabled){
this.mMyModals.clear();
this.mMyModels.addAll(myModels);
//... Some fields initialization here
notifyDataSetChanged();
}
Please let me know if i am not clear. Also show your activity code if this does not work.
Instead of replacing the whole ArrayList and calling notifyDataSetChanged, try adding the items to the ArrayList and then call notifyItemRangeInserted(int positionStart, int itemCount), maybe that could work. Also, you dont have to replace the Adapter's ArrayList. Your Activity/Fragment probably has the same ArrayList, just editing this list in your Activity/Fragment and then calling notifyItemRangeInserted(int positionStart, int itemCount) should do the trick. Also, instead of retrieving all the messages, you could also try to only get the next X amount of messages, so you wont retrieve the messages you already retrieved before (if you didn't do that already).
I have a RecyclerView that will contain list of item retrieved from the internet. So at first, the list will be empty. After the data retrieved from the internet, it will update the list and call notifyDataSetChanged().
I can adapt the data into the RecyclerView just fine. But, I have an ImageButton for each of item which has different Image if it's clicked. If I initialize the flags array inside onBindViewHolder, each time I scrolled the RecyclerView, the flag array will be reinitialize to false. If I initialize it in the Adapter constructor, it will be 0 index since the list will be empty at first. Where should I put array initializing in adapter if the data will come at some amount of time later?
Below is my code, but the flag array (isTrue) is always reinitialize each time I scrolled my RecyclerView.
public class SomethingAdapter extends RecyclerView.Adapter<SomethingAdapter.ViewHolder> {
private ArrayList<String> someList;
private boolean[] isTrue;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView someText;
public ImageButton someButton;
public ViewHolder(View v) {
super(v);
someText = (TextView) v.findViewById(R.id.text);
someButton = (ImageButton) v.findViewById(R.id.button);
}
}
public SomethingAdapter(ArrayList<String> someList) {
this.someList = someList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
//TODO: This thing will make isTrue always reinitialize if scrolled
this.isTrue = new boolean[someList.getResults().size()];
viewHolder.someText.setText(someList.get(position));
if (isTrue[position]) {
viewHolder.someButton.setImageResource(R.drawable.button_true);
} else {
viewHolder.someButton.setImageResource(R.drawable.button_false);
}
viewHolder.someButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isTrue[position]) {
//Connect to the internet and if response is positive {
//isTrue[position] = false;
//viewHolder.someButton.setImageResource(R.drawable.button_false);
//}
} else {
//Connect to the internet and if response is positive {
//isTrue[position] = true;
//viewHolder.someButton.setImageResource(R.drawable.button_true);
//}
}
}
});
}
#Override
public int getItemCount() {
return someList.size();
}
Initialize it when you add items to someList.
Also, don't add click listener in your onBind, create it in onCreateViewHolder. You cannot use position in the click callback, instead you should be using ViewHolder#getAdapterPosition.
See docs for details:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onBindViewHolder(VH, int)