I have a problem with a listview and ArrayAdapter.
My aim is to have a button on each row which allows the user to hide (or show) the TextView contained on this row.
But when I test my code, if I click the first row’s button, it hides the first TextView but also another TextView 9 rows below.
I imagine it’s the normal recycling mechanism operation, but I don’t really understand it as I assumed that on the onClick method, the View parameter was unique.
adapter_test.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:padding="5dip">
<ImageButton
android:id="@+id/adapter_test_button_showhide"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignParentLeft="true"
android:src="@drawable/ic_action_pause_light">
</ImageButton>
<!-- label -->
<TextView android:id="@+id/adapter_test_text_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf ="@id/adapter_test_button_showhide">
</TextView>
</RelativeLayout>
AdapterTest.java
public class AdapterTest extends ArrayAdapter<String>
{
// Holder
static class ViewHolder {TextView txtLabel ; ImageButton btnShowHide;}
//Initialize adapter
public AdapterTest(Context context, int resource, List<String> items) {super(context, resource, items);}
@Override
public View getView(int position, View v, ViewGroup parent)
{
// view Holder
ViewHolder viewHolder;
//Inflate the view
if(v==null)
{
//linearView = new LinearLayout(getContext());
LayoutInflater li = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = li.inflate(R.layout.adapter_test, null);
// Create holder
viewHolder = new ViewHolder();
viewHolder.txtLabel = (TextView)v.findViewById(R.id.adapter_test_text_label);
viewHolder.btnShowHide = (ImageButton)v.findViewById(R.id.adapter_test_button_showhide);
v.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolder) v.getTag();
}
// Load screen with data;
LoadScreenFromItem (viewHolder,getItem(position));
return v;
}
public void LoadScreenFromItem(ViewHolder viewHolder, String item)
{
// Remove handler
viewHolder.btnShowHide.setOnClickListener(null);
// Add handler
viewHolder.btnShowHide.setOnClickListener(handleOnClickShowHide());
// Set textt
viewHolder.txtLabel.setText(item);
}
private View.OnClickListener handleOnClickShowHide()
{
return new View.OnClickListener()
{
public void onClick(View v)
{
View parent = (View)v.getParent();
TextView listserie = (TextView) parent.findViewById(R.id.adapter_test_text_label);
// hide or show label
if (listserie.isShown()) listserie.setVisibility(View.INVISIBLE);
else listserie.setVisibility(View.VISIBLE);
}
};
}
}
Question :
My question is : Is there a way to do what I want ?
The problem is quite hard to find in the code.
You are reusing your
ViewHolder, which is very good as it saves you time to reallocate new memory every time.The problem in your code is: You are not resetting the visibility of the
TextView, leading to the problem that a reused View will inherit the visibility settings of any other TextView.In order to solve the bug you will have to store the visibility of each item together with the item itself in your adapter and reload the visibility settings when restoring the view.
Instead of
StringI would use aWhich is stored in the Adapter. You need to update
bwhenever the visibility changes.