ListView Items' background get mixed up when scrolling listView
I'm trying to change the background of a specific item in the list view titled "Different Item". As I scroll down the list, all the items' backgrounds are correct. When I reach the end of the list (where "Different Item" is), and scroll back up, there are some random items that get the same background as "Different Item". I believe this is due to the view holder pattern that I'm using and the fact that android recycles each view to use again, but I don't quite understand the issue here, and how to solve it. Here is my MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView listView = (ListView)findViewById(R.id.list_view);
listView.setAdapter(new CustomAdapter(this));
}
}
Here is my CustomAdapter.java class
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class CustomAdapter extends BaseAdapter {
private Context mContext;
private List<String> mItems;
public CustomAdapter(Context context) {
mContext = context;
mItems = new ArrayList<>();
for (int i = 0; i < 20; i++) {
mItems.add("Item " + i);
}
mItems.add("Different Item");
}
@Override
public int getCount() {
return mItems.size();
}
@Override
public Object getItem(int i) {
return mItems.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
View view;
ViewHolder viewHolder;
TextView textView;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.list_item_template, viewGroup, false);
} else {
view = convertView;
}
if (view.getTag() == null) {
textView = (TextView)view.findViewById(R.id.items_text_view);
viewHolder = new ViewHolder(textView);
view.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder)view.getTag();
textView = viewHolder.getTextView();
}
textView.setText(mItems.get(position));
if (mItems.get(position).equals("Different Item")) {
textView.setBackgroundColor(Color.parseColor("#DDDDDD"));
}
return view;
}
private class ViewHolder {
private TextView mTextView;
public ViewHolder(TextView textView) {
this.mTextView = textView;
}
public TextView getTextView() {
return this.mTextView;
}
}
}
Here is my activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</RelativeLayout>
and my list_item_template.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/items_text_view"
android:textSize="20sp"
android:padding="20dp"
tools:text="test text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
and screenshots of the problem: At first, when I scroll down the list, everything looks fine:
After scrolling the list view up and down, views get mixed up:
after this section :
if (mItems.get(position).equals("Different Item")) {
textView.setBackgroundColor(Color.parseColor("#DDDDDD"));
}
you should have an else like this:
else
textView.setBackgroundColor(Color.parseColor("#FFFFFF"));
it will solve your problem.
Your intuition is correct.
When you're setting the background on the "Different Item" view, at some point that view gets recycled and reused by ListView
when it goes off screen, and is needed again by the adapter.
It gets reused for other items that aren't "Different Item", and if you don't explicitly reset the background to the default color for those "Item [x]" items, you will run into this problem with inconsistent backgrounds.
Also, an else
statement in this context has zero impact on performance.