Есть несколько вопросов с вами Adapter
реализации. Слишком много для меня, чтобы дать вам советы о том, как исправить это. Я просто объясню, как вы можете эффективно реализовать Adapter
, и затем вы можете применить его к своему Adapter
.
Достаточно сказать, что вы должны оптимально переключиться на использование нового RecyclerView
, который имеет значительные улучшения по сравнению с старыми ListView
. Вы можете найти документацию RecyclerView
here и руководство Googles о том, как его использовать here.
Если вы хотите, чтобы отобразить данные в ListView
вы должны сначала создать макет для каждого из элементов в ListView
. Для этого примера я буду использовать этот макет:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp">
<CheckBox
android:id="@+id/checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
Чтобы связать данные, которые мы хотим отобразить в каждом элементе ListView
мы пишем новый класс, который содержит только данные в частных полях и добытчиками и сеттеров, чтобы получить и установите эти данные. Такие классы обычно называют моделями взглядов. Модель вид на макете, как выше может выглядеть примерно так:
public class ExampleViewModel {
// This is the text which will be set to the CheckBox
private String text;
// This boolean will be used to save the checked state of the CheckBox
private boolean checked;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}
Каждый экземпляр такой модели представления будет представлять собой один элемент в ListView
. Когда один из предметов входит в видимую область ListView
, он должен быть привязан к View
в ListView
(Это то, что мы должны реализовать в getView()
of Adapter
). Пока элемент виден, модель останется связанной с этим View
, но как только View
выйдет из видимой области ListView
, она будет переработана и привязана к другой модели вида, которая просто входит в видимую область. Это называется рециркуляцией вида, и это делается для сведения к минимуму объема памяти в ListView
и для увеличения общей производительности прокрутки и текучести. Views
- очень дорогие предметы, особенно надувающие Views
и findViewById()
, стоят очень высокой производительности, и основная точка зрения на переработку заключается в том, что вам нужно раздувать только небольшое количество Views
, которое затем можно использовать повторно, и поэтому вы избегаете дорогого надувания и findViewById()
позже.
Большинство из того, что я объяснял выше, происходит автоматически. То, что вы, как разработчик, должно сделать, это надуть правильные Views
в getView()
или повторно использовать их, если их уже есть, а затем привязать правильную модель просмотра к View
. Я знаю, что большая часть этого кажется довольно сложной и запутанной, если вы впервые узнаете об этом, но она становится намного проще и понятнее, когда мы начнем смотреть на код.
Итак, теперь у нас есть макет элементов вида в ListView
и модель представления, чтобы согласиться с этим. Теперь нам нужно написать другой класс, который обычно называют держателями. Эти держатели обзора по существу представляют собой классы контейнеров вокруг представлений в ListView
. Каждый держатель вида содержит один View
, связанный с товаром в ListView
, и они также заботятся о привязке данных модели вида к View
. Без дальнейших церемоний здесь есть держатель для просмотра, который следует вместе с моделью обзора сверху:
public class ExampleViewHolder {
// The reference to the CheckBox is saved so we only have to perform the findViewById() once.
private final CheckBox checkBox;
// A reference to the view model which is currently bound to this view holder
private ExampleViewModel currentModel;
// The View associated with this view holder is passed into the constructor from the Adapter.
public ExampleViewHolder(View view) {
// And here we look for all relevant views
// In our case we just need the CheckBox
this.checkBox = (CheckBox) view.findViewById(R.id.checkbox);
}
public void bind(ExampleViewModel viewModel) {
// Unset the listener in case there was one from a previous view model
this.checkBox.setOnCheckedChangeListener(null);
// Save a reference to the view model which is currently bound to this view holder
this.currentModel = viewModel;
// Bind the data to the CheckBox
this.checkBox.setText(viewModel.getText());
this.checkBox.setChecked(viewModel.isChecked());
// Reset the listener
this.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
currentModel.setChecked(isChecked);
}
});
}
}
Теперь мы почти закончили. Единственное, что в настоящее время отсутствует, чтобы подключить все это вместе в Adapter
:
public class ExampleAdapter extends BaseAdapter {
// Each type of view in the `ListView` gets its own id
// In this example we only have one type of View so we only need one id
private static final int EXAMPLE_VIEW_ID = 0;
// The default view id is just a fallback
private static final int DEFAULT_VIEW_ID = EXAMPLE_VIEW_ID;
private final LayoutInflater inflater;
private List<ExampleViewModel> viewModels;
public ExampleAdapter(Context context, List<ExampleViewModel> viewModels) {
// The view models are initially passed in through the constructor.
// You can pass an empty list into the Adapter if there is not data initially.
this.viewModels = viewModels;
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
if(viewModels == null) {
return 0;
}
return viewModels.size();
}
@Override
public Object getItem(int position) {
return viewModels.get(position);
}
@Override
public long getItemId(int position) {
final Object model = getItem(position);
// Here we check if the model is an instance of ExampleViewModel and if yes we return its id
if(model instanceof ExampleViewModel) {
return EXAMPLE_VIEW_ID;
}
return DEFAULT_VIEW_ID;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(getItemId(position) == EXAMPLE_VIEW_ID) {
final ExampleViewModel model = (ExampleViewModel) getItem(position);
final ExampleViewHolder viewHolder;
// If the convertView is null we need to inflate a new view
if(convertView == null) {
final View view = this.inflater.inflate(ExampleViewHolder.LAYOUT, parent, false);
viewHolder = new ExampleViewHolder(view);
// Here we set the viewHolder as tag to the View
// This is done so we can reuse the same view holder later on
// Essentially this is the integral part of the whole view recycling process
view.setTag(viewHolder);
} else {
// If the convertView is not null we can just get the view holder with getTag() from the View
viewHolder = (ExampleViewHolder) convertView.getTag();
}
// And we just need to bind the model to the view holder
viewHolder.bind(model);
}
return convertView;
}
}
И это все, что вам нужно. Это довольно эффективная реализация Adapter
. Если вы имеете дело с двумя или более разными типами представлений, вам нужно написать модель представления и просмотреть класс держателя для каждого типа. Вы можете написать интерфейс, который называется ViewModel
, который будет выглядеть примерно так:
public interface ViewModel {
}
Каждый вид модели ваш должен реализовывать этот интерфейс. Затем вы можете использовать List<ViewModel>
в Adapter
, который может содержать все различные виды моделей.
public class TypeOneViewModel implements ViewModel {
}
public class TypeTwoViewModel implements ViewModel {
}
Как только все ваши просматривать модели реализуют этот интерфейс, вы можете сделать это:
final List<ViewModel> models = new ArrayList<ViewModel>();
models.add(new TypeOneViewModel());
models.add(new TypeTwoViewModel());
...
И этот List
который теперь содержит несколько различных типов зрения моделей затем могут быть переданы в Adapter
. Adapter
будет выглядеть примерно так:
public class ExampleAdapter extends BaseAdapter {
private static final int TYPE_ONE_VIEW_ID = 0;
private static final int TYPE_TWO_VIEW_ID = 1;
private static final int DEFAULT_VIEW_ID = TYPE_ONE_VIEW_ID;
private final LayoutInflater inflater;
private List<ViewModel> viewModels;
public ExampleAdapter(Context context, List<ViewModel> viewModels) {
this.viewModels = viewModels;
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
if(viewModels == null) {
return 0;
}
return viewModels.size();
}
@Override
public ViewModel getItem(int position) {
return viewModels.get(position);
}
@Override
public long getItemId(int position) {
final ViewModel model = getItem(position);
if(model instanceof TypeOneViewModel) {
return TYPE_ONE_VIEW_ID;
}
if(model instanceof TypeTwoViewModel) {
return TYPE_TWO_VIEW_ID;
}
return DEFAULT_VIEW_ID;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(getItemId(position) == TYPE_ONE_VIEW_ID) {
final TypeOneViewModel model = (TypeOneViewModel) getItem(position);
final TypeOneViewHolder viewHolder;
if(convertView == null) {
final View view = this.inflater.inflate(TypeOneViewHolder.LAYOUT, parent, false);
viewHolder = new TypeOneViewHolder(view);
view.setTag(viewHolder);
} else {
viewHolder = (TypeOneViewHolder) convertView.getTag();
}
viewHolder.bind(model);
}
if(getItemId(position) == TYPE_TWO_VIEW_ID) {
final TypeTwoViewModel model = (TypeTwoViewModel) getItem(position);
final TypeTwoViewHolder viewHolder;
if(convertView == null) {
final View view = this.inflater.inflate(TypeTwoViewHolder.LAYOUT, parent, false);
viewHolder = new TypeTwoViewHolder(view);
view.setTag(viewHolder);
} else {
viewHolder = (TypeTwoViewHolder) convertView.getTag();
}
viewHolder.bind(model);
}
return convertView;
}
}
Вы также можете объединить ваши обладателям просматривать, написав абстрактный класс. Такой абстрактный класс будет выглядеть примерно так:
public abstract class ViewHolder<T extends ViewModel> {
protected final View itemView;
public ViewHolder(View view) {
this.itemView = view;
}
public abstract void bind(T model);
}
Если вы используете этот абстрактный класс в качестве базового класса для держателей зрения можно было бы написать их так:
public class TypeOneViewHolder extends ViewHolder<TypeOneViewModel> {
public TypeOneViewHolder(View view) {
super(view);
...
}
public void bind(TypeOneViewModel model) {
...
}
}
Хотя эта часть не действительно требуется. Самая важная часть при работе с несколькими различными типами элементов в ListView
состоит в том, что все модели реализуют общий интерфейс, поэтому вы можете смело помещать их в один и тот же List
.
В любом случае, все это выглядит намного проще и чище, чем ваш Adapter
, не так ли? Таким образом, у вас есть идеальное разделение между данными в ListView
и Views
, которые отображают данные, и это намного удобнее обслуживать. Вы можете легко реализовать анимацию в держателе представления, не заботясь о том, чтобы переработать представление, и многие требования стали намного проще реализовать. Конечно, RecyclerView
берет все это на следующий уровень. Он работает почти так же, но имеет несколько серьезных улучшений по сравнению с ListView
, я действительно предлагаю вам взглянуть на него.
Одна вещь, я совсем забыл: Вы можете выставить внутреннюю List
зрения моделей с добытчика, так что вы можете изменить модели вид с внешней стороны. Добавить методы, как это в Adapter
:
public List<ExampleViewModel> viewmodels() {
return viewModels;
}
public void setViewModels(List<ExampleViewModel> models) {
viewModels = models;
}
Затем вы можете изменить модели просмотра в Adapter
, как это:
adapter.setViewModels(newData);
...
adapter.viewmodels().add(viewModel);
И когда вы закончите изменение данных вы можете обновить ListView
по телефону notifyDataSetChanged()
на Adapter
.
'onResume()' вызывается при возврате к предыдущему 'Activity' /' Fragment'. –
@XaverKapeller Это здорово. Благодарю. В любом случае, я вызвал notifydatasetchanged withing onResume, но listview не будет обновляться. – Slay
Вы переопределяете 'onResume()' одного из 'Фрагментов' внутри' ViewPager'? Потому что это не сработает. Переопределите 'onResume()' в 'Fragment' или' Activity', который содержит 'ViewPager'. –