自定义ListView适配器,ImageView表现奇怪

4
我有一个自定义的ListView适配器,用于为列表创建行。我的问题是,它似乎无法区分各个ImageView之间的差异。当我向上或向下滚动时,它似乎随机选择ImageView放置到指定位置。文本信息(从此片段中省略)不会出现问题,按预期工作。
这是我适配器的相关方法:
  public View getView( int position, View convertView, ViewGroup parent )
  {
    View v = convertView;

    if( v == null )
    {
      LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      v = vi.inflate( R.layout.generic_row, null );
    }

    // find the image
    ImageView favImage = (ImageView)v.findViewById( R.id.toggle_favorite );

   // when clicked...
   favImage.setOnClickListener( new OnClickListener() {

     @Override
     public void onClick( View v )
     {
       // make the gray star a yellow one
       int newImage = R.drawable.ic_star_yellow_embossed;
       ((ImageView)v).setImageBitmap(BitmapFactory.decodeResource(getContext().getResources(), newImage));
     }

   });

  return v;
  }

@Waqas:不要理会那个,那是我在课堂上复制、粘贴和剥离代码时的失误。它在代码中的命名是正确的。 - Josh
2个回答

3
那种行为是由于在滚动列表时,ListView 会回收行视图,因此您会得到已被用户操作(图像已更改)的行,而这些行位于应未修改图像的位置。为避免这种情况,您需要以某种方式保存列表中每一行的 ImageView 的状态,并使用此状态在 getView() 方法中设置正确的图像。因为您没有说明如何实现您的 adapter,所以我将向您展示一个简单的示例。
首先,您应该存储 ImageView 的状态。我使用了一个 ArrayList<Boolean> 作为自定义 adapter 的成员,如果该列表中的位置(对应于列表中行的位置)是 false,则图像为默认值,否则如果为 true,则用户单击了它,我们应该放置新图像:
private ArrayList<Boolean> imageStatus = new ArrayList<Boolean>();

在您的自定义适配器构造函数中初始化此列表。例如,如果您在适配器中放置了某些东西的列表,则应将imageStatus设置为与该列表一样大,并填充false(默认/起始状态):

//... initialize the imageStatus, objects is the list on which your adapter is based
for (int i = 0; i < objects.size(); i++) {
    imageStatus.add(false);
}

然后在你的getView()方法中:

View v = convertView;

            if (v == null) {
                LayoutInflater vi = (LayoutInflater) getContext()
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.adapters_adapter_with_images, null);
            }

            // find the image
            ImageView favImage = (ImageView) v
                    .findViewById(R.id.toggle_favorite);
            // Set the image bitmap. If the imageStatus flag for this position is TRUE then we
            // show the new image because it was previously clicked by the user
            if (imageStatus.get(position)) {
                int newImage = R.drawable.ic_star_yellow_embossed;
                favImage.setImageBitmap(BitmapFactory.decodeResource(
                        getContext().getResources(), newImage));
            } else {
                // If the imageStatus is FALSE then we explicitly set the image
                // back to the default because we could be dealing with a
                // recycled ImageView that has the new image set(there is no need to set a default drawable in the xml layout)                                       
                int newImage = R.drawable.basket_empty; //the default image
                favImage.setImageBitmap(BitmapFactory.decodeResource(
                        getContext().getResources(), newImage));
            }
            // when clicked... we get the real position of the row and set to TRUE
            // that position in the imageStatus flags list. We also call notifyDataSetChanged
            //on the adapter object to let it know that something has changed and to update!
            favImage.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Integer realPosition = (Integer) v.getTag(); //get the position from the view's tag
                    imageStatus.set(realPosition, true); //this position has been clicked be the user
                    adapter.notifyDataSetChanged(); //notify the adapter
                }

            });
            // set the position to the favImage as a tag, we later retrieve it
            // in the onClick method
            favImage.setTag(new Integer(position));
            return v;

        }

如果您不计划动态修改列表(删除/添加行),那么这应该可以很好地工作,否则,您还需要注意修改imageStatus列表以反映更改。您没有说明您的行数据是什么,另一种方法(如果您计划在用户单击图像后执行某些操作(除了更改))是将图像状态合并到行的数据模型中。关于此,以下是一些教程:Android ListView高级交互Commonsware-Android摘录(交互行)。

2

在找到默认图像的引用后,您需要立即定义它:

// find the image
ImageView favImage = (ImageView)v.findViewById( R.id.toggle_favorite );
//setting to default
favImage.setImageResource(R.drawable.default_image);
// when clicked...
favImage.setOnClickListener....

你需要这样做,因为一旦图片被更改并且你滚动了ListView,它会重新出现,这是因为ListView回收了项视图。因此你需要在getView中定义它,以便在列表滚动时使用默认图像。


我不明白。我正在尝试在图像被点击时执行此操作。我已经在 .xml 文件中设置了它的默认图像。 - Josh
在XML中设置它只需要一次,这可以通过if(v == null)实现,但是当ListView回收视图时,它不会从XML中读取,而是直接回收视图,在这种情况下,您的ImageView包含了在单击时定义的旧图像。只需在代码中设置默认图像,我相信它会起作用。 - waqaslam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接