Я пришел к такому же выводу, что наилучшим способом является предоставление модели представления для привязки макета, содержащей элемент, отображаемый в представлении переработчика. Я создал решение общего назначения для этого сценария.
Адаптер можно увидеть ниже и поддерживает несколько типов макетов.
public abstract class ViewModelBaseAdapter<T extends Diffable, VM extends ViewModel>
extends ListAdapter<T, DoubleItemViewHolder<T, VM>> {
private final int itemVariableId;
private final int viewModelVariableId;
/**
* Constructor
*
* @param diffCallback the comparison strategy between items in {@code this} adapter
* @param variableId the variable in the data binding layout to set with the items
*/
public ViewModelBaseAdapter(int itemVariableId, int viewModelVariableId) {
super(new DiffUtil.ItemCallback<T>() {
@Override
public boolean areItemsTheSame(@NonNull Diffable oldItem,
@NonNull Diffable newItem) {
return oldItem.isSame(newItem);
}
@Override
public boolean areContentsTheSame(@NonNull Diffable oldItem,
@NonNull Diffable newItem) {
return oldItem.isContentSame(newItem);
}
});
this.itemVariableId = itemVariableId;
this.viewModelVariableId = viewModelVariableId;
}
@NonNull
@Override
public DoubleItemViewHolder<T, VM> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()), viewType, parent, false);
return new DoubleItemViewHolder<>(binding, itemVariableId, viewModelVariableId);
}
@Override
public void onBindViewHolder(@NonNull DoubleItemViewHolder<T, VM> holder, int position) {
holder.bind(getItem(position), getItemViewModel(position));
}
@Override
public abstract int getItemViewType(int position);
/**
* Provides the {@code ViewModel} to be bound together with the item at
* a specified position.
*
* @param position the position of the item
* @return the view model
*/
public abstract VM getItemViewModel(int position);
}
Интерфейс и ViewHolder определены следующим образом.
public interface Diffable {
boolean isSame(Diffable other);
boolean isContentSame(Diffable other);
}
public final class DoubleItemViewHolder<V1, V2> extends RecyclerView.ViewHolder {
private final ViewDataBinding binding;
private final int firstVariableId;
private final int secondVariableId;
/**
* Constructor
*
* @param binding the binding to use
* @param firstVariableId the first variable set on the binding
* @param secondVariableId the second variable set on the binding
*/
public DoubleItemViewHolder(ViewDataBinding binding,
int firstVariableId,
int secondVariableId) {
super(binding.getRoot());
this.binding = Objects.requireNonNull(binding);
this.firstVariableId = firstVariableId;
this.secondVariableId = secondVariableId;
}
/**
* Sets the data binding variables to the provided items
* and calls {@link ViewDataBinding#executePendingBindings()}.
*
* @param firstItem the first item to bind
* @param secondItem the second item to bind
* @throws NullPointerException if {@code firstItem} or {@code secondItem} is {@code null}
*/
public void bind(@NonNull V1 firstItem, @NonNull V2 secondItem) {
Objects.requireNonNull(firstItem);
Objects.requireNonNull(secondItem);
binding.setVariable(firstVariableId, firstItem);
binding.setVariable(secondVariableId, secondItem);
binding.executePendingBindings();
}
}
Теперь, когда «плита котла» настроена, она становится простой в использовании.
Пример
Цель этого примера - предоставить полный ответ, включая настройку для всех, кто хочет использовать этот подход, его можно обобщить очень просто.
Сначала определяются модели.
public class AppleModel implements Diffable {
// implementation...
}
public class DogModel implements Diffable {
// implementation...
}
Затем мы представляем в модели представления diffables следующим образом.
private final MutableLiveData<List<Diffable>> diffables = new MutableLiveData<>();
public LiveData<List<Diffable>> getDiffables() {
return diffables;
}
И реализуйте адаптер, переопределив ViewModelBaseAdapter.
public class ModelAdapter
extends ViewModelBaseAdapter<Diffable, MyViewModel> {
private final MyViewModel myViewModel;
public SalesmanHistoryAdapter(MyViewModel myViewModel) {
super(BR.item, BR.vm);
myViewModel = myViewModel;
}
@Override
public int getItemViewType(int position) {
final Diffable item = getItem(position);
if (item instanceof AppleModel) {
return R.layout.item_apple_model;
}
if (item instanceof DogModel) {
return R.layout.item_dog_model;
}
throw new IllegalArgumentException("Adapter does not support " + item.toString());
}
@Override
public MyViewModel getItemViewModel(int position) {
// You can provide different viewmodels if you like here.
return myViewModel;
}
}
Затем вы прикрепляете эти элементы и адаптер к виду рециркулятора в макете.
<variable
name="adapter"
type="ModelAdapter" />
<variable
name="vm"
type="MerchantLogViewModel" />
<androidx.recyclerview.widget.RecyclerView
list_adapter="@{adapter}"
list_adapter_items="@{vm.diffables}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
Они крепятся с помощью этого адаптера для переплета.
@BindingAdapter(value = {
"list_adapter",
"list_adapter_items"
})
public static <T> void setRecyclerViewListAdapterItems(RecyclerView view,
@NonNull ListAdapter<T, ?> adapter,
@Nullable final List<T> items) {
Objects.requireNonNull(adapter);
if (view.getAdapter() == null) {
view.setAdapter(adapter);
Timber.w("%s has no adapter attached so the supplied adapter was added.",
view.getClass().getSimpleName());
}
if (items == null || items.isEmpty()) {
adapter.submitList(new ArrayList<>());
Timber.w("Only cleared adapter because items is null");
return;
}
adapter.submitList(items);
Timber.i("list_adapter_items added %s.", items.toString());
}
Где ваш макет элемента (здесь отображается только для DogModel, но то же самое для AppleModel).
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="DogModel" />
<variable
name="vm"
type="MyViewModel" />
</data>
<!-- Add rest below -->
Теперь вы можете использовать модель представления long с элементом в макете, используя привязку данных.