GitHubのシンプルなアプリプロジェクト には、カスタムJavaファイルが2つしかありません。
Adapter
に表示するためのViewHolder
およびRecyclerView
が含まれています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();
}
}
_
私の問題:
ViewHolder
s 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
}
}
_
クラスの分離を維持するには、次のように、アダプタのインターフェイスを定義することをお勧めします。
_public interface OnBluetoothDeviceClickedListener {
void onBluetoothDeviceClicked(String deviceAddress);
}
_
次に、これにセッターをアダプターに追加します。
_private OnBluetoothDeviceClickedListener mBluetoothClickListener;
public void setOnBluetoothDeviceClickedListener(OnBluetoothDeviceClickedListener l) {
mBluetoothClickListener = l;
}
_
次に、内部的にViewHolder
のonClick()
で:
_if (mBluetoothClickListener != null) {
final String addresss = deviceAddress.getText().toString();
mBluetoothClickListener.onBluetoothDeviceClicked(address);
}
_
次に、MainActivity
にAdapter
へのリスナーを渡します。
_mDeviceListAdapter.setOnBluetoothDeviceClickedListener(new OnBluetoothDeviceClickedListener() {
@Override
public void onBluetoothDeviceClicked(String deviceAddress) {
confirmConnection(deviceAddress);
}
});
_
これにより、特定の動作に拘束されることなく、後でアダプターを再利用できます。
静的な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);
MainActivityをアダプタのコンストラクタパラメータとして渡し、フィールドに格納できます。または、イベントバスを使用します-それを行うにはいくつかの方法があります-私はフィールドに行きます
アダプターで、メインアクティビティへのコールバックを提供するインターフェースを作成します。
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);
このようなActivityのインスタンスを使用して、Activityメソッドを呼び出すことができます。MainActivity内でコードの下に書き込みます
mDeviceListAdapter = new DeviceListAdapter(MainActivity.this);
内部アダプター
private MainActivity _mainActivity;
public DeviceListAdapter(MainActivity activity){
this._mainActivity=activity;
}
OnClickメソッドの内部
_mainActivity.yourActivityMethod(address);