Паттерн ViewHolder в реализации списка ListView


Паттерн ViewHolder, по некоторым данным, позволяет увеличить производительность списка ListView на 15-20%, что является очень актуальным для больших массивов данных. В этой статье мы рассмотрим как раз пример использования этого паттерна.

Паттерн ViewHolder в реализации списка ListView

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

Layout для Активити — res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/lview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </ListView>
</LinearLayout>

Layout для отдельного пункта списка res/layout/list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/txtItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="7dp"
        android:textSize="18sp"/>
    
</LinearLayout>

Создадим класс адаптера DataAdapter.java (наследуется от BaseAdapter)

package ru.androiddocs.viewholderexample;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class DataAdapter extends BaseAdapter {

    private Context mContext;
    private String[] mData;

    public DataAdapter(Context context, String[] objects) {
        mContext = context;
        mData = objects;
    }

    static class ViewHolder {
        TextView txtItem;
    }

    @Override
    public String getItem(int i) {
        return mData[i];
    }

    @Override
    public View getView(int position, View convertView, ViewGroup viewGroup) {
        ViewHolder viewHolder;

        if (convertView == null){
            LayoutInflater inflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_item, viewGroup, false);
            viewHolder = new ViewHolder();
            viewHolder.txtItem = (TextView) convertView.findViewById(R.id.txtItem);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.txtItem.setText(getItem(position));

        return convertView;
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public int getCount() {
        return mData.length;
    }
}

Здесь мы видим внутренний класс ViewHolder, в котором будем хранить ссылку на TextView

static class ViewHolder {
    TextView txtItem;
}

Как видно из кода, в методе getView(), мы «переиспользуем» старые View, чтобы не создавать новые. Это также благотворно влияет на использование ресурсов памяти. Если нет старого convertView, то мы создаем и наполняем новый. Ссылку на TextView мы сохраняем в ViewHolder:

viewHolder.txtItem = (TextView) convertView.findViewById(R.id.txtItem);

Сохраняем и получаем доступ до ViewHolder с помощью методов setTag() и getTag(). При многократных обращениях, по сути, получив нужную ссылку, мы сразу сеттим текст:

viewHolder.txtItem.setText(getItem(position));

Класс MainActivity.java

package ru.androiddocs.viewholderexample;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.widget.ListView;


public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView lView = (ListView) findViewById(R.id.lview);
        DataAdapter adapter = new DataAdapter(this, getDataSet());
        lView.setAdapter(adapter);
    }

    private String[] getDataSet() {

        String[] mDataSet = new String[100];
        for (int i = 0; i < 100; i++) {
            mDataSet[i] = "item" + i;
        }
        return mDataSet;
    }
}

один комментарий на “Паттерн ViewHolder в реализации списка ListView

  1. diman

    Отлично , два дня городил костыли , пока ViewHolder не нашел.

    Спасибочки!!!!

    Reply

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*