【问题标题】:Why does my listview reorder when I scroll through it?为什么我的列表视图在我滚动浏览它时会重新排序?
【发布时间】:2012-01-04 17:37:10
【问题描述】:

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

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

    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();
        }

    }

【问题讨论】:

    标签: java android listview baseadapter


    【解决方案1】:

    因为您的视图 (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 的视图,并说“好的,让我们重用我们用于位置 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() 方法来填充您的视图。

    【讨论】:

    • 你能解释一下你的意思吗?我不太清楚你的意思。
    【解决方案2】:

    你的问题在这里:

    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 对象。

    【讨论】:

      【解决方案3】:

      您应该为回收的AvailableExpenseView 设置Expenses 为费用.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 中不需要创建按钮的示例。然后稍后您可以通过正交方法进行自定义:属性的“设置器”。以这种方式设计您的组件,使它们易于使用,并且更加多价。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-15
        相关资源
        最近更新 更多