В прошлой теме был создан кастомный адаптер, который позволял работать со сложными списками объектов:
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 особенно при больших списках будет работать плавнее и производительнее, чем в прошлой теме: