web-dev-qa-db-ja.com

RecyclerView.AdapterのViewHolderからMainActivityメソッドを呼び出す方法は?

GitHubのシンプルなアプリプロジェクト には、カスタムJavaファイルが2つしかありません。

  1. MainActivity.Java BluetoothおよびUI関連のソースコードが含まれています
  2. DeviceListAdapter.Java BluetoothデバイスをAdapterに表示するためのViewHolderおよびRecyclerViewが含まれています

app screenshot

MainActivity.Java には、ユーザーがRecyclerViewでBluetoothデバイスをタップしたときに呼び出されるメソッドが含まれています。

_public void confirmConnection(String address) {
    final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Do you want to pair to " + device + "?");
    builder.setPositiveButton(R.string.button_ok, 
      new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            device.createBond();
        }
    });
    builder.setNegativeButton(R.string.button_cancel, null);
    builder.show();
}
_

そして、ViewHolderクラス( DeviceListAdapter.Java 内)でクリックリスナーが定義されます。

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

  private ArrayList<BluetoothDevice> mDevices = new ArrayList<BluetoothDevice>();

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

    private TextView deviceAddress;

    public ViewHolder(View v) {
        super(v);
        v.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        String address = deviceAddress.getText().toString();

        Toast.makeText(v.getContext(),
                "How to call MainActivity.confirmConnection(address)?",
                Toast.LENGTH_SHORT).show();
    }
  }
_

私の問題:

ViewHolders onClickメソッドからconfirmConnection(address)メソッドを呼び出す方法は?

私は2つのJava=ファイル間でViewHolderクラス宣言を移動し続け、それを独自のファイルに入れてみましたが、正しい方法が見つかりません。

ViewHolderクラスにフィールドを追加し、(いつ?)MainActivityインスタンスへの参照をそこに格納する必要がありますか?

UPDATE:

これは私にとってはうまくいきますが、回避策のようです(そしてLocalBroadcastReceiverを使用することも考えていました-これはさらにハックな回避策になるでしょう)-

_    @Override
    public void onClick(View v) {
        String address = deviceAddress.getText().toString();

        try {
            ((MainActivity) v.getContext()).confirmConnection(address);
        } catch (Exception e) {
            // ignore
        }
    }
_
12

クラスの分離を維持するには、次のように、アダプタのインターフェイスを定義することをお勧めします。

_public interface OnBluetoothDeviceClickedListener {
    void onBluetoothDeviceClicked(String deviceAddress);
}
_

次に、これにセッターをアダプターに追加します。

_private OnBluetoothDeviceClickedListener mBluetoothClickListener;

public void setOnBluetoothDeviceClickedListener(OnBluetoothDeviceClickedListener l) {
    mBluetoothClickListener = l;
}
_

次に、内部的にViewHolderonClick()で:

_if (mBluetoothClickListener != null) {
    final String addresss = deviceAddress.getText().toString();
    mBluetoothClickListener.onBluetoothDeviceClicked(address);
}
_

次に、MainActivityAdapterへのリスナーを渡します。

_mDeviceListAdapter.setOnBluetoothDeviceClickedListener(new OnBluetoothDeviceClickedListener() {
    @Override
    public void onBluetoothDeviceClicked(String deviceAddress) {
        confirmConnection(deviceAddress);
    }
});
_

これにより、特定の動作に拘束されることなく、後でアダプターを再利用できます。

27
kcoppock

静的なViewHolderからコールバックを呼び出すことを探している人のために、以下を実行します。アダプターを用意しましょう:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private final int resource;
    private final List<Item> items;
    private final LayoutInflater inflater;
    ...
    private Callback callback;

    private static class ViewHolder extends RecyclerView.ViewHolder {
        ...
    }

    public interface Callback {
        void onImageClick(int position);
        void onRemoveItem(int position);
    }
}

次に、setCallbackメソッドを追加して、アクティビティ/フラグメントから呼び出す必要があります。また、コールバックを静的にしないでください(多くのクラスで同じアダプターを使用すると問題が発生する可能性があります)。 ViewHolder内にフィールドを作成する必要があります。そう:

    public MyAdapter(Context context, int resource, List<Item> items, Callback callback) {
        super();
        this.resource = resource;
        this.items = items;
        this.inflater = LayoutInflater.from(context);
        this.callback = callback;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final ViewHolder viewHolder = (ViewHolder) holder;
        final Item item = this.items.get(position);
        viewHolder.caption.setText(item.caption);
        viewHolder.callback = callback;
    }

    // A method to set a callback from activity/fragment.
    public void setCallback(Callback callback) {
        this.callback = callback;
    }

    public static class Item {
        public long id;
        public String number;
        public String caption;

        ...
    }

    private static class ViewHolder extends RecyclerView.ViewHolder {
        protected final TextView caption;
        // A reference to an adapter's callback.
        protected Callback callback;

        public ViewHolder(View view) {
            super(view);
            this.caption = (TextView) view.findViewById(R.id.caption);
        }

        private View.OnClickListener onClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int id = v.getId();
                if (id == R.id.image) {
                    // Invoke the callback here.
                    if (callback != null) {
                        callback.onImageClick(getLayoutPosition());
                    }
                }
            }
        };
    }
}

アダプターを作成した後、それを呼び出すことができます。

adapter = new MyAdapter(getActivity(), R.layout.item,
        new ArrayList<MyAdapter.Item>(), null);

adapterListener = new MyAdapter.Callback() {
    @Override
    public void onImageClick(int position) {
        // Some actions.
    }

    @Override
    public void onRemoveItem(int position) {
        // Some actions.
    }
};

adapter.setCallback(adapterListener);
5
CoolMind

MainActivityをアダプタのコンストラクタパラメータとして渡し、フィールドに格納できます。または、イベントバスを使用します-それを行うにはいくつかの方法があります-私はフィールドに行きます

3
ligi

アダプターで、メインアクティビティへのコールバックを提供するインターフェースを作成します。

public interface MyCallback{
    void onItemClicked();
}

private MyCallback listener;

public setOnItemClickListener(MyCallback callback){
    listener = callback;
}

メインアクティビティで実装してください。

public class MainActivity extends AppCompatActivity implements MyCallback

次に、コールバックを実装します

@Override
public void onItemClick(){
    //do work
}

次に、アダプタからのコールバックを設定します

mDeviceListAdapter.setOnItemClickListener(this);
2
tyczj

このようなActivityのインスタンスを使用して、Activityメソッドを呼び出すことができます。MainActivity内でコードの下に書き込みます

mDeviceListAdapter = new DeviceListAdapter(MainActivity.this);

内部アダプター

 private MainActivity _mainActivity;
 public DeviceListAdapter(MainActivity activity){
 this._mainActivity=activity;
 }

OnClickメソッドの内部

 _mainActivity.yourActivityMethod(address);
1