3

I'm using a Adapter and RecyclerView. I try to change the color of the first item in my RecyclerView. I do this in my onBindViewHolder and the first one indeed changes how i want it to but if i check 8 views furter i see that that one is changed aswell. It's a bit consistant, item number 0 is changed, 1,2,3,4 isn't 5 is, 6,7,8,9 isn't 10 is etc...

If I go debugging i see that my if statement is getting triggerd alot of time's. If i search on internet I see people use the same kinda way (in onBindViewHolder). Dont know what im doing wrong :(

@Override
public void onBindViewHolder(@NonNull oScheduleAvailabilityViewHolder scheduleAvailabilityViewHolder, int position) {
    oSchedule oSchedule = oScheduleArrayList.get(position);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTypeface((oSchedule.getSelected()) ? Typeface.DEFAULT_BOLD : Typeface.SANS_SERIF);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setText(oSchedule.getScheduleName());

    if (position == 0) {
            scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(0xFFd45155);
            scheduleAvailabilityViewHolder.test.setBackgroundColor(0xFF1D3587);
        }
    }

I want only the first item (adapterPosition 0 or position 0) to be changed in color and background. Right now also a consistant amount of other views are.

Asad Ali Choudhry
  • 4,985
  • 4
  • 31
  • 36
Allart
  • 830
  • 11
  • 33
  • 6
    Pseudocode: `if (position == 0) { change color to 0xFF1D3587 } else { change color to default color }`. The way **RECYCLER**view works is it recycles your view. If you change the UI of the first view, let's say the background color then that view with the same background color might re-appear later – Zun Jun 25 '19 at 07:56
  • Thanks alot Zun this worked! I dont think its the best answer. Since the else now gets called alot of times and set's the color + background to diffrent color each time again it might go for big performance issue. For sure if you change more then just the textcolor. I would like it to trigger only once and then its done. – Allart Jun 25 '19 at 08:18
  • that's how recycler views are supposed to work. They are supposed to get 'recycled' or re-used and its upto the developer to define/refresh each item's view properties. That being said - there's another way - see my answer below. – Vinay W Jun 25 '19 at 08:28
  • What you are describing is how ListViews work. If you set them once, the Views will stick like that. RecyclerView will recycle each View. Also, setting multiple background colors does not impact performance. You can measure it yourself! – Zun Jun 25 '19 at 08:34
  • @Zun I understand what your saying (I think xD), what do you think about the answer i posted? – Allart Jun 25 '19 at 09:07
  • If you want an honest opinion then I'd say it's bad. You're not properly using the RecyclerView. You may as well use ListViews then – Zun Jun 25 '19 at 09:08

5 Answers5

2

I think this way it work's better for performance. It will trigger only the first item each time created/recycled.

private static final int TYPE_AVAILABILITY = 1;
private static final int TYPE_WEEK = 2;

I used this way once before in a project.

@Override
public void onBindViewHolder(@NonNull oScheduleAvailabilityViewHolder scheduleAvailabilityViewHolder, int position) {
    oSchedule oSchedule = oScheduleArrayList.get(position);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTypeface((oSchedule.getSelected()) ? Typeface.DEFAULT_BOLD : Typeface.SANS_SERIF);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setText(oSchedule.getScheduleName());

    if (scheduleAvailabilityViewHolder.getItemViewType() == TYPE_AVAILABILITY) {
        Log.i(TAG, "onBindViewHolder: TEST FIRST");
        scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(0xFFd45155);
        scheduleAvailabilityViewHolder.test.setBackgroundColor(0xFF1D3587);
    }
}
@Override
public int getItemViewType(int position) {
    if (position == 0) {
        return TYPE_AVAILABILITY;
    } else {
        return TYPE_WEEK;
    }
}
Allart
  • 830
  • 11
  • 33
2

Line which cause this weird behaviour,

if (position == 0) {
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(0xFFd45155);
    scheduleAvailabilityViewHolder.test.setBackgroundColor(0xFF1D3587);
}

Whenever you have to add if condition in recyclerView's onBindViewHolder method. Never use it alone. Use it with else part also.

If you are making UI or data update in if part then revert it back to normal in else part of code.

if (position == 0) {
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(0xFFd45155);
    scheduleAvailabilityViewHolder.test.setBackgroundColor(0xFF1D3587);
} else {
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor({DEFAULT_COLOR});
    scheduleAvailabilityViewHolder.test.setBackgroundColor({DEFAULT_COLOR});
}

If you are using if (...) else if (...), this will also create the issue, because UI will confused what I have to do when none of the condition matched. So always use else () bracket as default behaviour. i.e., if (...) else if (...) else ().

Note: This is applied to all the conditional formulas like switches, when and etc.

DHAVAL A.
  • 2,251
  • 2
  • 12
  • 27
1

Try
In class oSchedule, add a flag:

private boolean isFirstItem;

When construct data oScheduleArrayList, set first item oSchedule.isFirstItem = true;
In adapter:

@Override
public void onBindViewHolder(@NonNull oScheduleAvailabilityViewHolder scheduleAvailabilityViewHolder, int position) {
    oSchedule oSchedule = oScheduleArrayList.get(position);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTypeface((oSchedule.getSelected()) ? Typeface.DEFAULT_BOLD : Typeface.SANS_SERIF);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setText(oSchedule.getScheduleName());

    if (oSchedule.isFirstItem) {
        scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(0xFFd45155);
        scheduleAvailabilityViewHolder.test.setBackgroundColor(0xFF1D3587);
    }
}

When you need to modify your list and refresh recycler view in your activity:

for (int i = 0; i < oScheduleArrayList.size(); i++) {
    oScheduleArrayList.get(i).setIsFirstItem(i == 0);
}
adapter.notifyDataSetChanged();

Well, I am used to modify model for UI when using recyclerview, as it is manageable outside adapter. But if you just need to set 1 cell's UI, it would be much better using Vinay's method.

TylerQITX
  • 318
  • 2
  • 9
  • So where do you set the isFirstItem to true? With if (position == 0) in onBind again? – Allart Jun 25 '19 at 08:16
  • @Allart No, set when you construct oScheduleArrayList. Like in activity/fragment outside adapter. – TylerQITX Jun 25 '19 at 08:19
  • Okey so i did what you said, it works but i still have that list item number 0 is coloured, 1,2,3,4 are not 5 is 6,7,8,9 are not, 10 is etc... – Allart Jun 25 '19 at 08:47
0

The reason other views appear like the first item is because when the first item is 'recycled' as the, say, 8th item - it retains the view properties of the first item, unless you change them before its drawn.. You can do something like this to fix it

public void onBindViewHolder(@NonNull oScheduleAvailabilityViewHolder scheduleAvailabilityViewHolder, int position) {
    oSchedule oSchedule = oScheduleArrayList.get(position);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTypeface((oSchedule.getSelected()) ? Typeface.DEFAULT_BOLD : Typeface.SANS_SERIF);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setText(oSchedule.getScheduleName());

    if (position == 0) {
            scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(0xFFd45155);
            scheduleAvailabilityViewHolder.test.setBackgroundColor(0xFF1D3587);

    }else{
            scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(DEFAULT_COLOR_HERE);
            scheduleAvailabilityViewHolder.test.setBackgroundColor(DEFAULT_COLOR_HERE);

    }

Another solution is to use a different ItemType for the first item and the rest of the items. Using this technique, you can create different view holders (two, in your case) and the recycler view will only recycle the respective (non-first items, in your case) for a given item type.

Vinay W
  • 9,912
  • 8
  • 41
  • 47
  • Yes i saw this answer in my comment, please read my comment at his comment :P – Allart Jun 25 '19 at 08:20
  • Yes I saw that solution before, also used it before. But since i only change 1 thing in 2 Views I think its a bit much code for just those things :P I like to optimalise as much as I can. – Allart Jun 25 '19 at 08:35
0

It's happening because the adapter is using the same itemView again when your scrolling, and you change the color when the item was in position 0 and never changed it back to default color when the adapter is using it again, so what you have to do is

@Override
public void onBindViewHolder(@NonNull oScheduleAvailabilityViewHolder scheduleAvailabilityViewHolder, int position) {
    oSchedule oSchedule = oScheduleArrayList.get(position);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTypeface((oSchedule.getSelected()) ? Typeface.DEFAULT_BOLD : Typeface.SANS_SERIF);
    scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setText(oSchedule.getScheduleName());

    if (position == 0) {
            scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(0xFFd45155);
            scheduleAvailabilityViewHolder.test.setBackgroundColor(0xFF1D3587);
        }else{
           //retutrn to deaftlt color
             scheduleAvailabilityViewHolder.scheduleAvailabilityTextView.setTextColor(your deafult color);
             scheduleAvailabilityViewHolder.test.setBackgroundColor(your deafult color);
    }
    }