Android Persistent ListView recycling

460 Views Asked by At

I have been fighting with this problem for days. Looking for the solution on stackoverflow or anywhere a solution is given, tried all of them even tried recyclerView but nothing helped.

My PROBLEM is that I've a listview and two textView on a listItem. So whenever I click on list item, Visibility of one textView is GONE. But when I scroll down I see not only clicked one is gone but many others are gone as well. Heres my code

CustomAdapter.java class

package com.example.nara.testtt;

import android.content.Context;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.
import java.util.ArrayList;

public class CustomAdapter extends BaseAdapter {
private ArrayList<String> list = new ArrayList<String>();
private Context context;
public CustomAdapter(Context context){
        this.context = context;
    for(int i = 0; i < 50; i++){
        list.add("item at " + i);
    }
}

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

@Override
public String  getItem(int position) {
    return list.get(position);
}

@Override
public long getItemId(int position) {
    return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;
    if(convertView == null){
        LayoutInflater inflator = LayoutInflater.from(context);
        convertView = inflator.inflate(R.layout.list_item,null);

        holder = new ViewHolder(convertView);
        convertView.setTag(holder);
    } else{
        holder = (ViewHolder)convertView.getTag();
    }

    holder.tv1.setText(getItem(position));
    holder.tv2.setText(getItem(position));

    return convertView;
}

public static class ViewHolder{
    private TextView tv1;
    private TextView tv2;

    public ViewHolder(View view){
        tv1 = (TextView) view.findViewById(R.id.textView);
        tv2 = (TextView) view.findViewById(R.id.textView2);
    }
}
}

MainActivity.java

package com.example.nara.testtt;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
CustomAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    adapter = new CustomAdapter(this);

    ListView lv = (ListView) findViewById(R.id.listView);
    lv.setAdapter(adapter);
    lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            TextView tv = (TextView) view.findViewById(R.id.textView);
            tv.setVisibility(View.GONE);
            adapter.notifyDataSetChanged();
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

}

3

There are 3 best solutions below

5
On BEST ANSWER

In getView() after setting the text update the visibility of the textview to Visible, since listitems in the ListView will get recycled.

 holder.tv1.setText(getItem(position));
 holder.tv2.setText(getItem(position));
 holder.tv1.setVisibility(View.VISIBLE);
//Your code.....

Updated :

public class CustomAdapter extends BaseAdapter { private ArrayList list = new ArrayList(); private Context context;

    public CustomAdapter(Context context) {
        this.context = context;
        for (int i = 0; i < 50; i++) {
            list.add(new Bean("item at " + i));
        }
    }

    @Override
    public Bean getItem(int position) {
        return list.get(position);
    }

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

        Bean item = getItem(position);
        holder.tv1.setText(item.name);
        holder.tv2.setText(item.name);
        holder.tv1.setVisibility(item.visibility);
        return convertView;
    }
    // ViewHolder declaration 
    .......
public static class Bean {
    String name;
    int visibility = View.VISIBLE;

    public Bean(String name) {
        this.name = name;
    }
}

In the onItemClick on your activity.

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        TextView tv = (TextView) view.findViewById(R.id.textView);
        Bean bean = Adapter.getItem(position);
        bean.visibility = View.GONE
        tv.setVisibility(View.GONE);
        adapter.notifyDataSetChanged();
    }
4
On

The problem is that the views are getting recycled in your adapter. So when you are scrolling the old views are getting used for the new ones. That means if the visibility of the items of the old view are set to GONE are still GONE in the new ones. If you dont want to save the visibility you can just set them to visible in your adapter:

holder.tv1.setVisibility(View.VISIBLE);
holder.tv1.setText(getItem(position));
holder.tv2.setText(getItem(position));

Or you store boolean values in a list or something like that and ask there if the item was clicked:

if(clicked.get(i)) holder.tv1.setVisibility(View.GONE);
else holder.tv1.setVisibility(View.VISIBLE);
0
On

Replace

convertView = inflator.inflate(R.layout.list_item,null);

with

convertView = inflator.inflate(R.layout.list_item,container,false);