当我滚动浏览时,为什么我的listview会重新排序?

时间:2012-01-04 17:37:10

标签: java android listview baseadapter

当我滚动浏览时,我的列表视图重新排序......这非常令人困惑。

这是我正在使用的自定义适配器:

    public class LoadExpenseList extends BaseAdapter{
        List<Expense> expenses;
        Context context;

        public LoadExpenseList(Context context, int textViewResourceId,
                List<Expense> expenses) {
            super();
            this.expenses = expenses;
            this.context = context;
        }

        public View getView(final int position, View convertView, ViewGroup parent){
            //View v = convertView;
            AvailableExpenseView btv;

            if (convertView == null) {
                btv = new AvailableExpenseView(context, expenses.get(position));
            } else {
                btv = (AvailableExpenseView) convertView;
            }           
            btv.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Log.i("Expense_Availables", "Item Selected!!");
                    Intent intent = new Intent(getActivity(), ItemDetailActivity.class);

                    int id = expenses.get(position).getExpenseItemId();
                    intent.putExtra("id", id);

                    startActivity(intent);
                }

            });

            btv.setOnLongClickListener(new OnLongClickListener() {

                @Override
                public boolean onLongClick(View arg0) {
                    // TODO Auto-generated method stub
                    return false;
                }

            });

            registerForContextMenu(btv);

            return btv;
        }

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

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

        @Override
        public long getItemId(int position) {
            return expenses.get(position).getExpenseItemId();
        }

    }

3 个答案:

答案 0 :(得分:4)

因为您的视图(AvailableExpenseView)是使用项构建的,所以当适配器尝试通过convertView重用视图时,您将获得一个已绑定到另一个项的视图。

不要使用您的模型项构建视图,而是调用类似convertView.setExpense(expenses.get(position))的内容。

ListView将尝试重用视图以提高性能。所以会发生的事情是列表中的第一个项目是使用新创建的视图显示的,稍后在滚动时会尝试重用以前创建的视图,通过convertView为您提供视图。请注意以下几行:

        if (convertView == null) {
            // You create a view using the proper item
            btv = new AvailableExpenseView(context, expenses.get(position)); 
        } else {
            // You don't override the item that was previously assigned 
            // when the view was created
            btv = (AvailableExpenseView) convertView;
        }    

如果convertView为null,则表示您正在创建新视图,但是您正在使用项目构建视图。所以我们假设这是在位置0调用的。使用列表中第一个的开销创建一个视图。稍后,listView想要获取视图,让我们说位置20,并说“ok允许重用我们用于位置0的视图”,因此它将此视图作为convertView传递,但此视图已经创建了位置0中的项目,您不会覆盖此项。因此,您最终使用的视图中有第一项表示第20项。

要解决此问题,您可以轻松地执行以下操作:

        AvailableExpenseView btv;

        if (convertView == null) {
            // dont create your view with an item
            btv = new AvailableExpenseView(context);
        } else {
            btv = (AvailableExpenseView) convertView;
        }

        // Assign the expense wether it is a newly created view or
        // a view that is reused
        btv.setExpense(expenses.get(position));

当然,您必须修改AvailableExpenseView并创建一个setExpense()方法来填充您的观点。

答案 1 :(得分:0)

您应该为已回收的AvailableExpenseView将费用设置为expense.get(position),并为新实例执行此操作。

当实例被回收时,只会在错误的行显示错误(错误的行所在的行,而不是你正在重用它们的行)。

更准确地说,你没有提供AvailableExpenseView的代码,但它可能会像

一样。
        public class AvailableExpenseView {

          private Expense expense = null; 

          public class AvailableExpenseView( Context context ) {
             super( context );
          }//cons

          /*
           Just add this method and use it.
          */

          public void setExpense( Expense expense ) {
            this.expense = expense;
          }//met
        }//class

然后,在适配器中执行以下操作:

        if (convertView == null) {
            btv = new AvailableExpenseView(context);
        } 
        btv = (AvailableExpenseView) convertView;
        btv.setExpense( expenses.get( expenses.get(position) ) );

使用松散构造函数的组件很好。考虑JVM内部的示例,其中不需要创建按钮。然后您可以通过正交方法自定义:属性的“setters”。以这种方式设计您的组件,使其易于使用,并且更加多元化。

答案 2 :(得分:0)

你的问题在这里:

if (convertView == null) {
  btv = new AvailableExpenseView(context, expenses.get(position));
} else {
  btv = (AvailableExpenseView) convertView;
} 

如果convertView为null,那么您将创建一个新的AvailableExpenseView,并在该位置使用Expense。这很好。

但是如果convertView不为null,那么您将引用现有的AvailableExpenseView。之前已将其(在前一种情况下)初始化为与您要在当前位置显示的费用不同的费用。

您有两个选择:在此if块之后设置btv的费用,以便无论您是创建新的AvailableExpenseView还是回收一个,都会使用正确的费用 -

或:在else块中,为循环视图设置正确的Expense对象。