Подтвердить что ты не робот

В чем преимущество шаблона ViewHolder в Android?

Когда вы разрабатываете программу для Android; и вы хотите иметь ArrayAdapter, вы можете просто иметь класс (чаще всего с суффиксом ViewHolder) или напрямую накачать ваш convertView и найти ваш вид по id.
Так в чем же преимущество использования ViewHolder?
Пример обоих здесь:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = ((Activity)getContext()).getLayoutInflater().inflate(R.layout.row_phrase, null);
    }
    ((TextView) convertView.findViewById(R.id.txtPhrase)).setText("Phrase 01");
}

Или создайте внутренний класс в ArrayAdapter следующим образом:

static class ViewHolder {   
    ImageView leftIcon;   
    TextView upperLabel;  
    TextView lowerLabel;  
}

и, наконец, в getView:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    if (view == null) {
        view = LayoutInflater.from(context).inflate(R.layout.row_layout,
                    null, false);
        holder = new ViewHolder();
        holder.leftIcon = (ImageView) view.findViewById(R.id.leftIcon);
    }
}
4b9b3361

Ответ 1

Понять, как работает переработка списков.

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

Вы не можете перерабатывать строку, которая в настоящее время используется. В приведенной выше ссылке объясняется, как работает механизм рециркуляции списков.

Итак, в чем преимущество использования ViewHolder?

Котировка документов

Ваш код может часто вызывать findViewById() во время прокрутки ListView, что может замедлить производительность. Даже когда адаптер возвращает завышенный вид для повторной переработки, вам все равно нужно искать элементы и обновлять их. Способом повторного использования findViewById() является использование шаблона дизайна "держатель вида".

    public View getView(int position, View convertView, ViewGroup parent) { 
             ViewHolder holder; 

             if (convertView == null) { // if convertView is null
                 convertView = mInflater.inflate(R.layout.mylayout, 
                         parent, false);
                 holder = new ViewHolder(); 
                     // initialize views  
                convertView.setTag(holder);  // set tag on view
            } else { 
                holder = (ViewHolder) convertView.getTag();
                        // if not null get tag 
                        // no need to initialize
            } 

            //update views here  
            return convertView; 
    }

Вы пропустили важную часть convertView.setTag(holder) и holder = (ViewHolder) ConvertView.getTag()

http://developer.android.com/training/improving-layouts/smooth-scrolling.html

Ответ 2

ViewHolder шаблон дизайна используется для ускорения рендеринга вашего ListView - на самом деле, чтобы заставить его работать плавно, findViewById довольно дорого (он выполняет разбор DOM) при использовании каждый раз, когда отображается элемент списка, он должен проходить иерархию компоновки , а также создавать объекты. Поскольку списки могут часто перерисовывать свои элементы во время прокрутки, такие накладные расходы могут быть существенными.

вы можете найти хорошее объяснение того, как это работает:

http://www.youtube.com/watch?v=wDBM6wVEO70&feature=youtu.be&t=7m

начиная с минуты 10, вы объяснили шаблон дизайна ViewHolder специалистами Google.

[править]

findViewById не создает новые объекты, он только обходит иерархию - здесь ссылка http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/view/ViewGroup.java#3610

Ответ 3

Когда вы переходите через свой ListView, в любой момент времени отображается только несколько представлений. Это означает, что вам не нужно создавать экземпляр представления для каждого элемента вашего адаптера; когда просмотр прокручивается за пределы экрана, его можно повторно использовать или перерабатывать.

Просмотр утилизации и шаблон ViewHolder не совпадают. Шаблон ViewHolder предназначен только для уменьшения количества вызовов view.findViewById(int), которые вы делаете. Шаблон ViewHolder работает только тогда, когда вы пользуетесь утилитой просмотра.

В getView(int position, View convertView, ViewGroup parent) параметр convertView имеет значение либо null, или это представление, которое было переработано: оно все равно будет привязано к данным из другого элемента списка.

Без шаблона ViewHolder вы все равно можете использовать переработку вида (т.е. не слепое создание экземпляров):

public View getView(int position, View convertView, ViewGroup parent) {
  View view = convertView;
  if (view == null) {
    view = // inflate new view
  }

  ImageView imageView = (ImageView) view.findViewById(R.id.listitem_image);
  TextView textView = (TextView) view.findViewById(R.id.listitem_text);
  TextView timestampView = (TextView) view.findViewById(R.id.listitem_timestamp);
  ProgressBar progressSpinnerView = (ProgressBar) view.findViewById(R.id.progress_spinner);

  // TODO: set correct data for this list item
  // imageView.setImageDrawable(...)
  // textView.setText(...)
  // timestampView.setText(...)
  // progressSpinnerView.setProgress(...)

  return view;
}

Выше приведен пример утилизации просмотра - мы не надуваем новый вид для каждой строки; мы только раздуваем представление, если нам не дают повторного использования. Избегать необходимости раздувать представление - это та часть, которая определенно поможет в производительности при прокрутке списка: воспользуйтесь утилитой просмотра.

Итак, что такое ViewHolder для этого? В настоящее время мы делаем 4x findViewById(int) для каждого, независимо от того, существовала ли сама строка. Поскольку findViewById(int) рекурсивно выполняет итерацию по ViewGroup, пока не найдет потомка с данным идентификатором, это немного бессмысленно для наших переработанных представлений - мы пересматриваем представления, к которым у нас уже есть ссылки.

Избегайте этого, используя объект ViewHolder для хранения ссылок на под-представления после их поиска:

private static class ViewHolder {
  final TextView text;
  final TextView timestamp;
  final ImageView icon;
  final ProgressBar progress;

  ViewHolder(TextView text, TextView timestamp, ImageView icon, ProgressBar progress) {
    this.text = text;
    this.timestamp = timestamp;
    this.icon = icon;
    this.progress = progress;
  }
}

View.setTag(Object) позволяет вам указать, что объект "Ведение" содержит произвольный объект. Если мы используем его для хранения экземпляра нашего ViewHolder после того, как мы выполним наши вызовы findViewById(int), тогда мы можем использовать View.getTag() для переработанных представлений, чтобы избежать необходимости повторять вызовы снова и снова.

public View getView(int position, View convertView, ViewGroup parent) {
  View view = convertView;
  if (view == null) {
    view = // inflate new view
    ViewHolder holder = createViewHolderFrom(view);
    view.setTag(holder);  
  }
  ViewHolder holder = view.getTag();
  // TODO: set correct data for this list item
  // holder.icon.setImageDrawable(...)
  // holder.text.setText(...)
  // holder.timestamp.setText(...)
  // holder.progress.setProgress(...)
  return view;
}

private ViewHolder createViewHolderFrom(View view) {
    ImageView icon = (ImageView) view.findViewById(R.id.listitem_image);
    TextView text = (TextView) view.findViewById(R.id.listitem_text);
    TextView timestamp = (TextView) view.findViewById(R.id.listitem_timestamp);
    ProgressBar progress = (ProgressBar) view.findViewById(R.id.progress_spinner);

    return new ViewHolder(text, timestamp, icon, progress);
}

Преимущества этой оптимизации являются сомнительными, но преимущества ViewHolder.

Ответ 4

Во-первых:

В ListView, когда вы прокручиваете ListView, вам нужно создать новый элемент и привязать его данные, поэтому, если у вас много элементов в ListView, это может привести к утечке памяти, поскольку больше объектов вы создали для элементов, но Android использует концепцию утилизации большей части своего API, и это означает, что вы создаете один объект и используете его вместо его уничтожения и объявляете новый, поэтому, когда вы прокручиваете ListView API, используя невидимые элементы, которые вы прокрутили, и передайте их вам в getView метод convertView, поэтому здесь вы имеете дело с большим количеством элементов ListView

Во-вторых:

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

надеюсь, что это поможет вам и вернуть меня в любую очевидную вещь.