如何访问RecyclerView适配器的ViewHolder的数据源?

问题描述:

我RecyclerView的适配器的构造是这样的:如何访问RecyclerView适配器的ViewHolder的数据源?

Context context; 
List<ConnectionItem> connections; 

public ConnectionsListAdapter(Context context, List connections) { 
    this.context = context; 
    this.connections = connections; 
} 

适配器的声明后,我宣布一个静态ViewHolder类的RecyclerView,也可以用来处理任何按钮的onClicks:

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 

    public Context context; 

    public ImageView connectionImage; 
    public TextView connectionName; 
    public ImageButton startMsg; 

    public ViewHolder(View itemView, List<ConnectionItem> connections) { 
     super(itemView); 
     ... 
     startMsg.setOnClickListener(this); 
    } 

    @Override 
    public void onClick(View v) { 
     Intent intent = new Intent(v.getContext(), ChatActivity.class); 
     intent.putExtra("name", connections.get(getAdapterPosition()).getName()); // Error because accessing from static context 
     intent.putExtra("id", connections.get(getAdapterPosition()).getUid()); // Error because accessing from static context 
     context.startActivity(intent); 
    } 
} 

的问题是connections无法从ViewHolder静态类的静态上下文中访问。我的ViewHolder从RecyclerView适配器获取信息的最佳方式是什么?作为一种变通方法,我传递的数据源到ViewHolder的构造和对于该数据源中的ViewHolder一个新的实例变量:

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
    public Context context; 
    public List<ConnectionItem> connections; 

    public ImageView connectionImage; 
    public TextView connectionName; 
    public ImageButton startMsg; 

public ViewHolder(View itemView, List<ConnectionItem> connections) { 
     super(itemView); 
     this.connections = connections; 
     ... 
     startMsg.setOnClickListener(this); 
} 

    @Override 
    public void onClick(View v) { 
     Intent intent = new Intent(v.getContext(), ChatActivity.class); 
     intent.putExtra("name", connections.get(getAdapterPosition()).getName()); // Okay now 
     intent.putExtra("id", connections.get(getAdapterPosition()).getUid()); // Okay now 
     context.startActivity(intent); 
    } 

请问我的路都打破ViewHolder模式或创建任何其他未来的问题?

代替

你的责任/关注点有点混乱。

在MVC中,ViewHolder确实是一个View。它只是一个引用子视图的对象,因此不必一遍又一遍地调用findViewById()。但这仍然是一个看法。

但是,您有一个模型数据作为参数的构造函数,并且它具有难闻的气味。

  • 唯一属性/变量您ViewHolder应该是View秒。

  • ViewHolder构造应该永远被调用的唯一事情是findViewById()

你还没有真正谈过适配器很多,但你会发现,你重写两种方法:onCreateViewHolderonBindViewHolder

onCreateViewHolder之内,您构建了适用于您的视图类型的ViewHolder。而已。您可能需要根据视图类型膨胀不同的布局,但您只需在此处理视图。不要将任何模型数据传递给构造函数中的ViewHolder

onBindViewHolder,这里是您将视图挂接到模型数据的位置。

我经常做的事情是为我的ViewHolder定义一个bind()方法,以便非常清楚Model数据正在被切换。 bind()方法获取数据并调用setText和类似的方法来使视图反映适配器位置的模型数据。

但是现在我们来看看RecyclerViewAdapter设计的丑陋部分。适配器没有OnItemClickListener

谷歌认为这是一个更好的设计;无论如何,事件应该由列表项目视图来处理。我明白了。但问题是事件必须满足其模型数据,并且它是具有模型数据的适配器,而不是列表项视图。

谷歌强调,你不能使用final的值,如位置;您需要拨打getAdapterPosition()以索引模型数据。

所以现在我已经去了一个适应所有这些约束的模式。

  • 我使用带位置参数的方法为事件侦听器定义了一个interface

  • 我在适配器

  • 创建的侦听器的实例,我绑定到一个ViewHolder我通过这个监听器实例(所以ViewHolder确实有给听者一个参考,但适配器每次)

  • ViewHolder事件处理程序,我称之为getAdapterPosition()以获取列表项的位置,然后调用侦听器方法与位置

  • 适配器获得听者回调,访问正确的模型数据,并执行所需的操作

所以这里有一个例子:我有一个在线购物车,已选定的项目,即产品的列表有一个大X的删除按钮。现在我必须处理该删除按钮。

定义接口:

interface OnItemRemovedListener { 

     void itemRemoved(int position); 
    } 

在适配器在适配器构造函数创建监听器的实例

private OnItemRemovedListener mCallback; 

进行设置:

mCallback = new OnItemRemovedListener() { 
     @Override 
     public void itemRemoved(int position) { 
      mItemList.remove(position); 
      notifyDataSetChanged(); 
     } 
    }; 

ViewHolder子类:

public static class ProductItemViewHolder extends RecyclerView.ViewHolder { 

    private TextView mProductName; 

    private Button mRemoveButton; 

    private OnItemRemovedListener mListener; 

    public ProductItemViewHolder(View itemView) { 
     super(itemView); 
     mProductName = (TextView) itemView.findViewById(R.id.product_name); 
     mRemoveButton = (Button) itemView.findViewById(R.id.remove_button); 
    } 

    public void bind(Model data, OnItemRemovedListener listener) { 

     mProductName.setText(data.getProductName()); 
     mListener = listener; 
     mRemoveButton.setOnClickListener(new OnClickListener() { 

      @Override 
      public void onClick(View view) { 
       int position = getAdapterPosition(); 
       if (position >= 0) { 
        mListener.itemRemoved(position); 
       } 
      } 
     }); 
    } 
} 

然后onBindViewHolder覆盖看起来是这样的:

@Override 
    public void onBindViewHolder(ProductItemViewHolder holder, int position) { 

     holder.bind(mItemList.get(position), mCallback); 
    } 

在您的适配器,:

public ConnectionsListAdapter(Context context, List connections) { this.context = context; this.connections = connections; }

你应该有

public ConnectionsListAdapter(Context context, List<ConnectionItem> connections) { this.context = context; this.connections = connections; }

另外,我不认为有保持你的ViewHolder类的静态的需要。所以你可以通过connections.get(getAdapterPosition());访问适配器列表数据