在 ViewHolder 中绑定

Binding in ViewHolder

这将是理论题。

作为每个人,我们在应用程序的许多部分使用 RecyclerView。有时 RecyclerView 包含不同的项目,不仅是图像,还有广告、提示等。这就是为什么我们可以在 Adapter 中使用 getViewType() 方法。

但是当我们有很多 viewType 并且在 Adapter 中绑定它时会出现问题。所以问题来了,在 ViewHolder 中绑定数据是不是很好的模式?

假设我们有应用列表。

为简单起见,每个应用程序都有名称。我们的 ViewHolder 看起来像这样:

class AppViewHolder extends RecyclerView.ViewHolder {

     public TextView nameText;

     AppViewHolder(View itemView) {
          super(itemView)
          nameText = (TextView) itemView.findViewById(R.id.text_name);
     }
}

现在我们可以添加绑定方法了:

public void bind(App app) {
     nameText.setText(app.getName());
}

这个模式好吗?

另一种解决方案是使用 ViewModel。因为我们在 RecyclerView 中有不同的项目,我们的适配器可以包含 class 的列表,这是每个 ViewModel 的基础 class。

所以基本 class 是:

class RecyclerViewItem {}

现在 class 这是 App 的 ViewModel。

class AppRecyclerViewItem extends RecyclerViewItem {

     App app;

     ...
}

我们的适配器只有 RecyclerViewItems 列表:

class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    List<RecyclerViewItem> items;

    ...
}

那么使用这种方法(我的意思是使用 ViewModel)是在 ViewHolder 中还是在 ViewModel 中添加绑定方法更好?

我想说 none 你的解决方案很好。 第一个只适用于在适配器中只出现一次的项目。创建一个方法来填充 ViewHolder 内的行不是一个好的解决方案,因为不同类型的 ViewHolders 会增长并且您的 RecyclerView.Adapter 会获得越来越多的行。

第二种方法也不行。模型就是模型,应该只包含数据而不是应用程序的业务逻辑。

我会建议一个解决方案来保持 RecyclerView.Adapter 干净并传递创建 ViewHolder 的逻辑并将数据填充给代表,这将在 ie 中注册。 RecyclerView.Adapter 构造函数。 此技术解释更多 here

这样,即使在多个不同的 ViewHolder 上注册也不会专门向 RecyclerView.Adapter 添加样板,而是将创建和填充 RecyclerView.Adapter 行的逻辑委托给这些委托。

但这只是一个建议。

对于我的应用程序,我一直在使用这样的东西:

public abstract class BindableViewHolder<T> extends RecyclerView.ViewHolder {
  public BindableViewHolder(View itemView) {
      super(itemView);
  }

  public abstract void bind(T thing);
}

想法是将绑定的实现委托给 ViewHolder。此解决方案适用于您想要将 相同类型的对象 绑定到不同的查看器的情况,但我认为它可以轻松扩展到更一般的情况。

这是一个非常简单的适配器示例。我认为它说明了一切,希望对您有所帮助!

public class ShowsAdapter extends RecyclerView.Adapter<BindableViewHolder<Show>> {

  private final DataProvider<Show> showsProvider;

  public ShowsAdapter(DataProvider<Show> showsProvider) {
      this.showsProvider = showsProvider;
  }

  @Override
  public BindableViewHolder<Show> onCreateViewHolder(ViewGroup parent, int viewType) {
      return ShowViewHolder.create(parent);
  }

  @Override
  public void onBindViewHolder(BindableViewHolder<Show> holder, int position) {
      holder.bind(showsProvider.get(position));
  }

  @Override
  public int getItemCount() {
      return showsProvider.size();
  }
}

这里,ShowViewHolder是BindableViewHolder的具体子类,

使用委托有很好的解决方案 - http://hannesdorfmann.com/android/adapter-delegates and it can be even used for DataBinding (with simple changes, https://github.com/drstranges/DataBinding_For_RecyclerView)。通过这种方式,数据绑定不在 ViewHolder 中,也不在 RecyclerView Adapter 中,而是 ItemDelegate 这样做,因此您的代码保持清晰和可读。

按照这种方法,所有视图类型都可以委托给 DelegateManager:

public abstract class AbsDelegationAdapter<T> extends RecyclerView.Adapter {

protected AdapterDelegatesManager<T> delegatesManager;
protected T items;
//...

    @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return delegatesManager.onCreateViewHolder(parent, viewType);
    }

    @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        delegatesManager.onBindViewHolder(items, position, holder);
    }

    @Override public int getItemViewType(int position) {
        return delegatesManager.getItemViewType(items, position);
    }
}

您的代码将如下所示:

mAdapter = new BindableAdapter<>(
        new UserDelegate(actionHandler), // you can create custom delegate
        //new ModelActionItemDelegate<BaseModel>(actionHandler, User.class, R.layout.item_user, BR.user), // or use generic
        new AnotherItemDelegate());

其中 ItemDelegate 可以是这样的:

public class UserDelegate extends BaseAdapterDelegate<BaseModel, ItemUserBinding> {

    @Override
    public boolean isForViewType(@NonNull final List<BaseModel> items, final int position) {
        return items.get(position) instanceof User;
    }

    @NonNull
    @Override
    public BindingHolder<ItemUserBinding> onCreateViewHolder(final ViewGroup parent) {
        // create ViewHolder
        return BindingHolder.newInstance(R.layout.item_user, LayoutInflater.from(parent.getContext()), parent, false);
    }

    @Override
    public void onBindViewHolder(@NonNull final List<BaseModel> items, final int position, @NonNull final BindingHolder<ItemUserBinding> holder) {
        // Just bind data
        final User user = (User) items.get(position);
        holder.getBinding().setUser(user);
    }

}