2

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;
 }
}
guipivoto
  • 18,327
  • 9
  • 60
  • 75
Tecksky Android
  • 133
  • 3
  • 14
  • Hi Techsky. Since the view is re-used, you can not modify its color direcly inside a onClickListener... You must update your adapter and then, you adapter will control the colors of the view... – guipivoto Jul 14 '16 at 13:39

3 Answers3

1

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>
Kushan
  • 5,855
  • 3
  • 31
  • 45
  • Remove the OnItemClickListener if you use this -> Associating clicks with viewholder members prevents jumbling – Kushan Jul 14 '16 at 13:32
  • Thanks for your answer and valuable effort,But i need to bind other listview on item click of this listview based on its value. And that is in MainActivity that's why i used update view in activity and then basis on this value i need to filter value of another listview. – Tecksky Android Jul 14 '16 at 13:38
  • Hmm.. sorry couldn't help you out with that. anyway, you now know why the jumbling was happening. The way around is using the ViewHolder to set colors etc... Why not try a RecyclerView, it compulsarily makes one use a ViewHolder and is much more efficient that a listview. – Kushan Jul 14 '16 at 13:41
  • Here i can use Recyclerview instead but in another page i have used gridview and there is also same issue. That's why i thought that there is issue within adapter and suggest me if you know solution for gridview as well. – Tecksky Android Jul 15 '16 at 06:48
  • Recyler view has a GridLayoutManager :) use that to make a grid layout – Kushan Jul 15 '16 at 07:05
  • Thanks a Lot,Recyclerview helps me for fix all issue which i getting from listview and gridview... – Tecksky Android Jul 18 '16 at 06:42
0

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).

guipivoto
  • 18,327
  • 9
  • 60
  • 75
  • Hi thanks for effort. I wanted to know that what is the issue within adapter. As you said that we have to do all listener of listview within adapter but in my app there is another activity in which i used gridview and its all listener used within adapter but there is same issue occured. You can see the link [Gridview Adapter issue](http://stackoverflow.com/questions/38247794/listview-update-another-raw-on-update-of-selected-raw) – Tecksky Android Jul 15 '16 at 06:54
0

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

Jay Ou
  • 63
  • 2
  • 9