Оптимізація адаптера та View Holder
У минулій темі було створено кастомний адаптер, який давав змогу працювати зі складними списками об'єктів:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class StateAdapter extends ArrayAdapter<State> {
private LayoutInflater inflater;
private int layout;
private List<State> states;
public StateAdapter(Context context, int resource, List<State> states) {
super(context, resource, states);
this.states = states;
this.layout = resource;
this.inflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent) {
View view=inflater.inflate(this.layout, parent, false);
ImageView flagView = view.findViewById(R.id.flag);
TextView nameView = view.findViewById(R.id.name);
TextView capitalView = view.findViewById(R.id.capital);
State state = states.get(position);
flagView.setImageResource(state.getFlagResource());
nameView.setText(state.getName());
capitalView.setText(state.getCapital());
return view;
}
}
Цей адаптер має один великий мінус — при прокручуванні в ListView, якщо в списку дуже багато об'єктів, то для кожного елемента, коли він потрапить в зону видимості, буде повторно викликатися метод getView, в якому буде знову створюватися новий об'єкт View. Це збільшуватиме споживання пам'яті та знижуватиме продуктивність. Тому оптимізуємо код методу getView:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(this.layout, parent, false);
}
ImageView flagView = convertView.findViewById(R.id.flag);
TextView nameView = convertView.findViewById(R.id.name);
TextView capitalView = convertView.findViewById(R.id.capital);
State state = states.get(position);
flagView.setImageResource(state.getFlagResource());
nameView.setText(state.getName());
capitalView.setText(state.getCapital());
return convertView;
}
Параметр convertView вказує на елемент View, який використовується для об'єкта в списку по позиції position. Якщо раніше вже був створений View для цього об'єкта, то параметр convertView вже містить певне значення, яке ми можемо використовувати.
В цьому випадку ми будемо повторно використовувати вже створені об'єкти і підвищимо продуктивність, однак цей код можна ще більше оптимізувати. Справа в тому, що отримання елементів за id також є відносно затратною операцією. Тому далі оптимізуємо код StateAdapter, змінивши його наступним чином:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class StateAdapter extends ArrayAdapter<State> {
private LayoutInflater inflater;
private int layout;
private List<State> states;
public StateAdapter(Context context, int resource, List<State> states) {
super(context, resource, states);
this.states = states;
this.layout = resource;
this.inflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflater.inflate(this.layout, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
State state = states.get(position);
viewHolder.imageView.setImageResource(state.getFlagResource());
viewHolder.nameView.setText(state.getName());
viewHolder.capitalView.setText(state.getCapital());
return convertView;
}
private class ViewHolder {
final ImageView imageView;
final TextView nameView, capitalView;
ViewHolder(View view) {
imageView = view.findViewById(R.id.flag);
nameView = view.findViewById(R.id.name);
capitalView = view.findViewById(R.id.capital);
}
}
}
Для зберігання посилань на використані елементи ImageView і TextView визначений внутрішній приватний клас ViewHolder, який в конструкторі отримує об'єкт View, що містить ImageView та TextView.
У методі getView, якщо convertView рівний null (тобто якщо раніше для об'єкта не створена розмітка), створюємо об'єкт ViewHolder, який зберігаємо в тегу convertView:
convertView.setTag(viewHolder);
Якщо ж розмітка для об'єкта в ListView вже була створена, то назад отримуємо ViewHolder з тегу:
viewHolder = (ViewHolder) convertView.getTag();
Потім для ImageView та TextView в ViewHolder встановлюються значення з об'єкта State:
viewHolder.imageView.setImageResource(state.getFlagResource());
viewHolder.nameView.setText(state.getName());
viewHolder.capitalView.setText(state.getCapital());
Тепер ListView, особливо при великих списках, буде працювати плавніше та продуктивніше, ніж у попередній версії.
