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