Android中Listview的行顺序在滚动Listview时随机更改

12

我有一个ListView和一个BaseAdapter。我从网络服务下载产品并在ListView上显示它们。它在ListView上正确显示。

但是,当我滚动ListView时,它会随机显示行,有时第三行显示在第一位置,中间位置的行显示在最后位置等等。

这是我的适配器类

public class ProductListAdapter extends BaseAdapter implements OnClickListener{

    LayoutInflater inflater;
    ArrayList<Product> arrProducts;
    Context c;

    public ProductListAdapter(Context c, ArrayList<Product> arrProducts) {
        super();
        inflater = LayoutInflater.from(c);
        this.arrProducts = arrProducts;
        this.c = c;
    }

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

    @Override
    public Object getItem(int position) {
        return arrProducts.get(position);
    }

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

    @Override
    public View getView(int position, View v, ViewGroup parent) {
        Product product = null;
        if(v == null) {
            v = inflater.inflate(R.layout.product_listview_row, null);
            product = (Product) getItem(position);
            v.setTag(product);
        }else {
            product = (Product)v.getTag();
        }

        v.setOnClickListener(this);

        TextView tvProductname = (TextView)v.findViewById(R.id.tvProductname);
        tvProductname.setText(product.getTitle());

        String strReviewCount = product.getReviews();
        TextView tvReviewsCounts = (TextView)v.findViewById(R.id.tvReviews);
        if(strReviewCount != null) tvReviewsCounts.setText(strReviewCount +" Reviews");

        Button btnPrice = (Button)v.findViewById(R.id.btnPrice);
        btnPrice.setText(product.getSellingPrice());

        return v;
    }

    @Override
    public void onClick(View v) {
        Product product = (Product) v.getTag();
        Intent i = new Intent(c, ProductDetails.class);
        i.putExtra(Product.PROD_ID, product.getId());
        startActivity(i);
    }

    @Override
    public int getItemViewType(int position) {
        return super.getItemViewType(position);
    }

    @Override
    public int getViewTypeCount() {
        return super.getViewTypeCount();
    }
}

这是XML布局中的ListView

<ListView android:id="@+id/lstProducts" style="@style/fill_parent"
        android:dividerHeight="1dp" android:divider="#dbd3c5"
        android:scrollbars="vertical" android:layout_below="@id/layLine"></ListView>

当我像这样更改BaseAdapter的getView()方法时,它可以正常工作。

@Override
    public View getView(int position, View v, ViewGroup parent) {
        Product product = null;
        v = inflater.inflate(R.layout.product_listview_row, null);
        product = (Product) getItem(position);
        v.setTag(product);
        v.setOnClickListener(this);

        TextView tvProductname = (TextView)v.findViewById(R.id.tvProductname);
        tvProductname.setText(product.getTitle());

        String strReviewCount = product.getReviews();
        TextView tvReviewsCounts = (TextView)v.findViewById(R.id.tvReviews);
        if(strReviewCount != null) tvReviewsCounts.setText(strReviewCount +" Reviews");

        Button btnPrice = (Button)v.findViewById(R.id.btnPrice);
        btnPrice.setText(product.getSellingPrice());

        return v;
    }
但是,这种类型的问题是我在ListView行中显示产品图像时没有考虑到的。使用这种类型的代码,在滚动ListView时getView()方法总是创建新的视图,因此我不得不一遍又一遍地下载图像。我使用了ImageLoader类,它使用缓存内存来下载图像,但问题是当我将图像设置到ImageView上多次时,它会给出内存不足错误
请帮我解决这个问题。
谢谢。
2个回答

10

我因为一个指向正确解决方案的链接而来到这里。

问题在于当你创建一个新视图时,你只将产品绑定到视图一次。你应该在每次调用getView时调用此方法:

product = (Product) getItem(position);
v.setTag(product);

不过,在创建视图后,您可以调用v.setOnClickListener(this)(它不会改变)。

这应该就可以了。这里只是误用了视图类型实现。我建议阅读我的答案...


我知道我的答案不是最好的方法,但为了临时目的,我使用了视图类型来管理它。希望你的解决方案能够奏效并得到验证。 - Lalit Poptani
谢谢。我希望你不会感到冒犯,那并不是我的本意。问题在于人们认为这个“技巧”是正常和有效的,但是当后来出现问题或者他们想使用真正的视图类型实现时,就会变得非常困惑...我只是想让事情清晰明了,而不是制造谣言;-) - Knickedi
不,对我来说得到确切的解决方案也很好。个人认为你的答案肯定会起作用,我也会尝试一下。感到难过从来不是问题,我也在这里学习。这是从你那里得到的新东西。谢谢。 - Lalit Poptani
此外,关于适配器的一篇非常好的文章:http://www.piwai.info/android-adapter-good-practices/ - eightyfive

5
因此,根据Knickedi所提出的建议, getView()方法应该如下所示:
public View getView(int position, View v, ViewGroup parent) {

    TextView tvProductname;
    TextView tvReviewsCounts;
    Button btnPrice;

    // Phase 1...
    if (v == null) {
        v = inflater.inflate(R.layout.product_listview_row, null);
        v.setOnClickListener(this);

        tvProductname = (TextView)v.findViewById(R.id.tvProductname);
        tvReviewsCounts = (TextView)v.findViewById(R.id.tvReviews);
        btnPrice = (Button)v.findViewById(R.id.btnPrice);

        v.setTag(R.id.tvProductname, tvProductname);
        v.setTag(R.id.tvReviews, tvReviewsCounts);
        v.setTag(R.id.btnPrice, btnPrice);
    }
    else {
        tvProductname = (TextView) v.getTag(R.id.tvProductname);
        tvReviewsCounts = (TextView) v.getTag(R.id.tvReviews);
        btnPrice = (Button) v.getTag(R.id.btnPrice);   
    }

    // Phase 2...
    Product product = (Product) getItem(position);

    tvProductname.setText(product.getTitle());
    btnPrice.setText(product.getSellingPrice());

    String strReviewCount = product.getReviews();
    if(strReviewCount != null)
        tvReviewsCounts.setText(strReviewCount +" Reviews");

    return v;
}

请注意,public void setTag(int key, Object tag) 已在 API 级别 4 中添加。 - eightyfive

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