Почему мой список переупорядочивается, когда я прокручиваю его? - PullRequest
3 голосов
/ 04 января 2012

Мои списки переупорядочиваются, когда я прокручиваю его ... это очень сбивает с толку.

Вот пользовательский адаптер, который я использую:

    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 ]

4 голосов
/ 04 января 2012

Поскольку ваше представление (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(), чтобы заполнить ваше представление.

0 голосов
/ 04 января 2012

Ваша проблема здесь:

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

если convertView имеет значение null, то вы создадите новый AvailableExpenseView с расходом в позиции. Это хорошо.

Но если convertView не равен null, вы будете ссылаться на существующий AvailableExpenseView. Ранее он был инициализирован (в предыдущем случае) с расходом, отличным от того, который вы хотите показать в текущей позиции.

У вас есть два варианта: установить Расход для btv ПОСЛЕ этого блока if, чтобы использовать правильный расход при создании нового AvailableExpenseView или его повторного использования -

или: в блоке else задайте правильный объект расходов для переработанного вида.

0 голосов
/ 04 января 2012

Вы должны установить Expenses для cost.get (position) для переработанного AvailableExpenseView, а также для новых экземпляров.

при повторном использовании экземпляров, которые просто отображают неправильные расходы на неправильной строке (строка, в которой они были созданы, а не строка, в которой вы их повторно используете).

Если быть более точным, вы не дадите свой код для 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, где для создания кнопки ничего не нужно. Затем в дальнейшем вы можете настроить с помощью ортогональных методов: «сеттеры» свойств. Разработайте свои компоненты таким образом, чтобы сделать их более простыми в использовании и более поливалентными.

...