Tecksky Android
Tecksky Android

Reputation: 133

Listview view update randomly on scroll

I am using a listview which bind some item and on click i wanted to update view of listview but when i scroll it that effect is applied on other views which randomly changes position if scroll more time.

This is my code.

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
    android:id="@+id/twvTest"
    android:layout_width="match_parent"
    android:layout_height="match_parent"></ListView>
</LinearLayout>

MainActivity.java

public class MainActivity extends Activity {
ListView twvTest;
ArrayList<DataModel> dataModels;
NameListAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    twvTest = (ListView) findViewById(R.id.twvTest);

    dataModels = new ArrayList<>();
    adapter = new NameListAdapter(MainActivity.this, dataModels);
    twvTest.setAdapter(adapter);
    twvTest.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            for (int i = 0; i < twvTest.getCount(); i++) {
                TextView txtName = (TextView) view.findViewById(R.id.txtName);
                if (i == position) {
                    txtName.setTextColor(Color.GREEN);
                    txtName.setBackgroundColor(Color.YELLOW);
                } else {
                    txtName.setTextColor(Color.YELLOW);
                    txtName.setBackgroundColor(Color.GREEN);
                }
            }
        }
    });
    fillList();
}

private void fillList() {
    for (int i = 0; i < 30; i++) {
        DataModel dataModel = new DataModel();
        dataModel.setName("Name : " + i);
        dataModels.add(dataModel);
    }
    adapter.notifyDataSetChanged();
  }
}

NameListAdapter.java

public class NameListAdapter extends BaseAdapter {
Context context;
ArrayList<DataModel> dataModels;
LayoutInflater inflater;

public NameListAdapter(Context context, ArrayList<DataModel> dataModels) {
    this.context = context;
    this.dataModels = dataModels;
    inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public int getCount() {
    return dataModels.size();
}

@Override
public Object getItem(int position) {
    return dataModels.get(position);
}

@Override
public long getItemId(int position) {
    return dataModels.indexOf(getItem(position));
}

private class Holder {
    TextView txtName;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Holder holder;
    if (convertView == null) {
        holder = new Holder();
        convertView = inflater.inflate(R.layout.test_adapter_raw, parent, false);
        holder.txtName = (TextView) convertView.findViewById(R.id.txtName);
        convertView.setTag(holder);
    } else {
        holder = (Holder) convertView.getTag();
    }
    holder.txtName.setText(dataModels.get(position).getName());
    return convertView;
  }
}

test_adapter_raw.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#ffffff"
android:orientation="vertical">

<TextView
    android:id="@+id/txtName"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="5dp"
    android:background="#949494"
    android:gravity="center"
    android:text="Text"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#fff"
    android:textStyle="bold" />
</LinearLayout>

DataModel.java

public class DataModel {
String name;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
 }
}

Upvotes: 2

Views: 768

Answers (3)

guipivoto
guipivoto

Reputation: 18687

You can not change the view directly during onClick() because the view is re-used....

So, you change the color of a View, that view will be probably re-used in another position and then, another positions will display the colored View.

You have to control the view color in your Adapter. This way, your Activity only updates the Adapter and call notifyDataSetChanged() to make the ListView updating the contents.

You Activity Code

twvTest.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        // Inform the adapter that following item should be displayed in Yellow
        adapter.displayViewInYellow(position);
        adapter.notifyDataSetChanged();        
    }
});

Your Adapter Code

public class NameListAdapter extends BaseAdapter {

    boolean [] arrayOfViewsThatMustShowYellow;

    public NameListAdapter(Context context, ArrayList<DataModel> dataModels) {
        ...
        arrayOfViewsThatMustShowYellow = new boolean[getCount()];
    }

    public void displayViewInYellow(int position) {
        // boolean array to track view that must be displayed in Yellow
        arrayOfViewsThatMustShowYellow[position] = true;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        .....

        // Following code could be simplified to convertView.setEnabled(arrayOfViewsThatMustShowYellow[position])
        if(arrayOfViewsThatMustShowYellow[position] == true) {
            holder.txtName.setTextColor(Color.GREEN);
            holder.txtName.setBackgroundColor(Color.YELLOW);
        } else {
            holder.txtName.setTextColor(Color.YELLOW);
            holder.txtName.setBackgroundColor(Color.GREEN);
        }
    }
}

Note

This is an example... It is just to share the idea about how to do it... Remember, if need to update any view, you need to track this change inside the Adapter (and never outside - Like in ClickListeners or Activity).

Upvotes: 0

Kushan
Kushan

Reputation: 5984

This is happening because ListView Recycles your views. Thus you same view is being used for other positions. To avoid the jumbling set a clicklistener inside your Adapter and handle the color inside the Holder.

public class NameListAdapter extends BaseAdapter {
Context context;
ArrayList<DataModel> dataModels;
LayoutInflater inflater;

public NameListAdapter(Context context, ArrayList<DataModel> dataModels) {
this.context = context;
this.dataModels = dataModels;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public int getCount() {
    return dataModels.size();
}

@Override
public Object getItem(int position) {
    return dataModels.get(position);
}

@Override
public long getItemId(int position) {
    return dataModels.indexOf(getItem(position));
}

private class Holder {
    TextView txtName;
    LinearLayout root_layout;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Holder holder;
    if (convertView == null) {
        holder = new Holder();
        convertView = inflater.inflate(R.layout.test_adapter_raw, parent, false);
        holder.txtName = (TextView) convertView.findViewById(R.id.txtName);
        holder.root_layout=(LinearLayout)convertView.findViewById(R.id.root_layout)
        convertView.setTag(holder);
    } else {
        holder = (Holder) convertView.getTag();
    }
    holder.txtName.setText(dataModels.get(position).getName());

    holder.root_layout.setOnClickListener(new View.OnClickListener(){

                holder.txtName.setTextColor(Color.GREEN);
                holder.txtName.setBackgroundColor(Color.YELLOW);

                //implement your own logic to remove color on second click
    });


    return convertView;
  }
}

Use an Id for your Listitems root layout as follows

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#ffffff"
android:orientation="vertical">

<TextView
android:id="@+id/txtName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:background="#949494"
android:gravity="center"
android:text="Text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#fff"
android:textStyle="bold" />
</LinearLayout>

Upvotes: 1

Jay Ou
Jay Ou

Reputation: 63

If the names have empty value, the viewholder will set ramdom value for it.set "" for empty name maybe can fit it.

Upvotes: 0

Related Questions