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 String I would use a

class AGoodClassName {
String s; boolean b;
}

Which is stored in the Adapter. You need to update b whenever the visibility changes.

其他提示

Old thread but it works for me as i need to show only the button in some list items of my ListView.

In case anyone is directed to this... This is the code i use to only show the button in some items of my list!

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.

For the record i'm using ButterKnife!

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if(convertView == null) {
        convertView = activity.getLayoutInflater().inflate(resource, parent, false);
        holder = new ViewHolder(convertView);
        convertView.setTag(holder);
    }
    holder = (ViewHolder) convertView.getTag();

    if(items.size() > 0) {
        MyItem item = items.get(position);
        holder.myButton.setVisibility(View.VISIBLE);
        if(!item.hasButton()) {
            holder.myButton.setVisibility(View.GONE);
        }
        holder.myButton.setFocusable(false);
        holder.myButton.setClickable(false);
    }
    return convertView;
}

public class ViewHolder {
    @InjectView(R.id.card_btn_layout) View myButton;

    public ViewHolder(View view) {
        ButterKnife.inject(this, view);
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top