Как работает механизм рециркуляции ListView

Итак, у меня есть эта проблема, которую я имел раньше, и, естественно, я попросил о помощи здесь. Ответ Luksprog был отличным, потому что я понятия не имел о том, как ListView и GridView оптимизировали себя при переработке Views. Поэтому с его советами я смог изменить, как я добавил Views в свой GridView. Проблема в том, что у меня есть что-то, что не имеет смысла. Это мой getView из моего BaseAdapter:


public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

Проблема заключается в том, что я прокручиваю, это происходит, а не в позиции 0... Похоже, позиция 6 и позиция 8, плюс она помещает два в позицию 8. Теперь я все еще пытаюсь получить доступ к ListView и GridView поэтому я не понимаю, почему это происходит. Одной из основных причин, почему я задаю этот вопрос, является помощь другим, которые, вероятно, не знают о ListView и GridView recycling View, или о том, как этот article ставит его, механизм ScrapView.

enter image description here

Редактировать позже

Добавление ссылки на беседу Google IO, в основном это все, что вам нужно, чтобы понять, как работает ListView. Ссылка была мертва в комментариях. Поэтому user3427079 был достаточно хорош, чтобы обновить эту ссылку. Здесь это для легкого доступа.

Ответ 1

Сначала я также не знал об утилизации списков и механизме использования convertview, но после целого дня исследований я в значительной степени понимаю механизмы представления списков, ссылаясь на изображение из android.amberfog enter image description here

Всякий раз, когда ваше представление списка заполняется адаптером, оно в основном показывает количество строк, которое список может отображать на экране, и количество строк не увеличивается даже при прокрутке списка. Это прием, который использует Android, чтобы listview работал более эффективно и быстро. Теперь, как вы можете видеть, внутренняя история просмотра списка, ссылающегося на изображение, первоначально у списка просмотра было 7 видимых элементов, затем, если вы прокрутите вверх, пока элемент 1 больше не будет виден, getView() передает это представление (т.е. item1) в переработчик и вы можете использовать

System.out.println("getview:"+position+" "+convertView);

внутри вашего

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);

        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);

        row.setTag(holder);

    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

В вашей logcat вы заметите, что convertview является нулевым для всех видимых строк, потому что изначально в утилите не было представлений (то есть элементов), поэтому ваш getView() создает новое представление для каждого из видимых элементов, но как только вы прокрутите вверх и элемент 1 выйдет из экрана, он будет отправлен в Recycler с его текущим состоянием (например, TextView 'text' или в моем случае, если флажок установлен, он будет связан с представлением и хранится в утилизаторе).

Теперь, когда вы прокручиваете вверх/вниз, ваше представление списка не будет создавать новое представление, оно будет использовать представление, которое находится в вашем утилизаторе. В вашей Logcat вы заметите, что 'convertView' не является нулевым, потому что ваш новый элемент 8 будет нарисован с использованием convertview, то есть, в основном, он берет представление элемента 1 из переработчика и надувает элемент 8 вместо него, и вы можете наблюдать что в моем коде. Если у вас был флажок и если вы отметили его в позиции 0 (скажем, у элемента 1 был флажок, и вы его установили), поэтому при прокрутке вниз вы увидите, что флажок элемента 8 уже установлен, поэтому просмотр списка использует тот же вид, не создает для вас нового из-за оптимизации производительности.

Важные вещи

1 Никогда не устанавливайте layout_height и layout_width вашего списка в wrap_content как getView() заставит ваш адаптер получить некоторый дочерний getView() для измерения высоты представлений, которые будут отображаться в виде списка, и может вызвать неожиданное поведение, например, возвращать convertview, даже если список не match_parent использует match_parent или фиксированную ширину/высоту.

2 Если вы хотите использовать какой-либо макет или представление после представления списка, вам могут layout_height в голову вопросы, если я установлю layout_height на fill_parent представление после представления списка не будет отображаться при переходе вниз по экрану, поэтому лучше поместить свой просмотр списка внутри макета. Например, Linear Layout и установите высоту и ширину этого макета в соответствии с вашими требованиями и присвойте атрибуту height и width вашего просмотра списка свой макет (например, если ширина макета равна 320, а высота равна 280), тогда ваш список должен иметь одинаковую высоту и ширину. Это скажет getView() о точной высоте и ширине представлений, которые будут отображаться, и getView() не будет снова и снова вызывать некоторые случайные строки, и другие проблемы, такие как возвращение представления преобразования даже до того, как прокрутка не произойдет, у меня есть тест это само по себе, если мой просмотр списка не был внутри lineaLayout, у него также были проблемы, такие как повторение вызова представления и преобразование представления, так как помещение Listview внутри LinearLayout работало для меня как волшебство (не знаю почему)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 [email protected]
01-01 14:49:36.636: I/System.out(13871): getview 1 and[email protected]
01-01 14:49:36.646: I/System.out(13871): getview 2 [email protected]
01-01 14:49:36.646: I/System.out(13871): getview 3 [email protected]
01-01 14:49:36.656: I/System.out(13871): getview 4 [email protected]
01-01 14:49:36.666: I/System.out(13871): getview 5 [email protected]
01-01 14:49:36.666: I/System.out(13871): getview 0 [email protected]
01-01 14:49:36.696: I/System.out(13871): getview 0 [email protected]
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

Но теперь это решено, я знаю, я не так хорош в объяснении, но, как я потратил весь день, чтобы понять, я думал, что другие новички, как я, могут получить помощь из моего опыта, и я надеюсь, что теперь вы, люди, будете иметь немного понимания структуры ListView, как это работает, поскольку это действительно грязно и хитро, поэтому новички нашли слишком много проблем с пониманием этого

Ответ 2

Соблюдайте в шаблоне Holder, если вы установите положение в объекте Holder, вы должны установить его каждый раз, например:

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

это пример из абстрактного класса, где

getHolder(position, view, parent);

выполняются все операции установки для

ImageViews, TextViews, etc..

Ответ 3

Используя шаблон Holder, вы можете достичь того, чего хотите:

Здесь вы можете найти описание этого шаблона:

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

Ответ 4

Recyclerview - это просто класс-оболочка для эффективного управления добавлением динамических представлений, то есть путем повторного использования уже созданного представления.

Подумайте об адаптере просмотра повторного заказа, порядке вызова методов is-

  • getItemCount - сначала вызывается, чтобы адаптер узнал, сколько представлений необходимо создать или управлять ими.
  • getItemViewType- Это вызывается для того, чтобы узнать, какой тип взглядов нужно надувать.
  • onCreateViewHolder - это вызывается далее, чтобы узнать, какое представление нужно надувать.
  • onBindViewHolder- подойдя к этому пошаговому адаптеру, даст вам экземпляр держателя вида, который содержит раздутый вид относительно позиции, поэтому здесь вы можете установить данные для видов.