簡體   English   中英

如何訪問RecyclerView適配器的ViewHolder的數據源?

[英]How to access the data source of a RecyclerView adapter's ViewHolder?

我的RecyclerView適配器的構造函數如下所示:

Context context;
List<ConnectionItem> connections;

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

在適配器聲明之后,我為RecyclerView聲明了一個靜態ViewHolder類,它還處理任何按鈕的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);
    }
}

問題是無法從ViewHolder靜態類中的靜態上下文訪問connections 我的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() 但它仍然是一個觀點。

但是,你有一個帶有Model數據作為參數的構造函數,並且它有一個難聞的氣味。

  • ViewHolder應該具有的唯一屬性/變量是View

  • 您的ViewHolder構造函數應該調用的唯一東西是findViewById()

你還沒有真正談論過你的適配器,但你會注意到你重寫了兩個方法: onCreateViewHolderonBindViewHolder

onCreateViewHolder您可以構建適用於您的視圖類型的ViewHolder 而已。 您可能需要根據視圖類型來擴展不同的布局,但您只是在這里處理視圖。 不要將任何Model數據傳遞給構造函數中的ViewHolder

onBindViewHolder ,您可以在此處將View連接到Model數據。

我經常做的事情是為我的ViewHolder定義一個bind()方法,以便清楚地傳遞什么樣的模型數據。 bind()方法獲取數據並調用setText和類似方法,使視圖反映該適配器位置的Model數據。

但現在我們來到RecyclerViewAdapter設計的丑陋部分。 適配器沒有OnItemClickListener

谷歌認為這是一個更好的設計; 無論如何,事件應該由列表項視圖處理。 我明白了。 但問題是事件必須滿足其模型數據,並且它是具有模型數據的適配器,而不是列表項視圖。

谷歌強調,你不能使用像頭寸這樣的final價值; 你需要調用getAdapterPosition()來索引模型數據。

所以現在我已經采用了適應所有這些限制的模式。

  • 我使用一個采用position參數的方法為事件監聽器定義一個interface

  • 我在適配器中創建了一個偵聽器實例

  • 每次我綁定到ViewHolder我都會傳遞此偵聽器實例(因此ViewHolder確實有對偵聽器的引用,但不是對適配器的引用)

  • ViewHolder的事件處理程序中,我調用getAdapterPosition()來獲取列表項的位置,然后我用該位置調用listener方法

  • 適配器獲取偵聽器回調,訪問正確的模型數據並執行所需的操作

所以這是一個例子:我有一個已經選擇的項目列表,即在線購物車中的產品有一個帶有大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());訪問適配器列表數據connections.get(getAdapterPosition());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM