MVVMとDataBindingを使用してAndroidアプリを作成しています。また、アクティビティを開始するViewModel内に関数があります。ViewModel内でonClick呼び出しを行うことはできますか?
このような。
public class MyViewModel {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
そして私のXMLでは:
...
Android:onClick="@{(v) -> viewModel.onClick(v, viewModel.product.id)}">
または、それをビューに移動し、EventBusまたはRxから呼び出して、ViewModelにPOJOのみを含めるのがベストプラクティスですか?
あなたの質問への答えはあなたの目標は何ですか?
懸念の分離に[〜#〜] mvvm [〜#〜]を使用して、Viewmodel
を単体テストできるようにする場合Context
を必要とするすべてのものをViewmodel
とは別にする必要があります。 Viewmodel
にはアプリのコアビジネスロジックが含まれており、外部依存関係はありません。
しかし、私はあなたが行く場所が好きです:)アクティビティが開かれる決定がビューにある場合、それのためのJUnitテストを書くことは非常に難しいです。ただし、startActivity()
呼び出しを実行するViewmodel
にオブジェクトを渡すことができます。これで、Unit testでこのオブジェクトをモックし、正しいActivity
が開かれていることを確認できます
ViewModel
の中に入れるのは絶対に完璧ですが、ViewModel
/Activity
からFragment
を設定する必要があります。
MVVMアーキテクチャを学ぶためにたどることができるいくつかのリンクがあります。
アプローチAndroid with MVVM
Android MVVM
https://github.com/ivacf/archi
People-MVVM
Android上のMVVM:知っておくべきこと
私がそれをする方法は、あなたのViewModelで:
val activityToStart = MutableLiveData<Pair<KClass<*>, Bundle?>>()
これにより、開始されたアクティビティのクラス、およびバンドルに渡されたデータを確認できます。次に、アクティビティに次のコードを追加できます。
viewModel.activityToStart.observe(this, Observer { value ->
val intent = Intent(this, value.first.Java)
if(value.second != null)
intent.putExtras(value.second)
startActivity(intent)
})
MVVMの原則では、View(アクティビティ/フラグメント)のみがViewModelへの参照を保持し、ViewModelはViewへの参照を保持すべきではないことを指摘しています。
あなたの場合、アクティビティを開始するには、次のようにします。
MyViewModel.class
public class MyViewModel {
public static final int START_SOME_ACTIVITY = 123;
@Bindable
private int messageId;
public void onClick() {
messageId = START_SOME_ACTIVITY;
notifyPropertyChanged(BR.messageId); //BR class is automatically generated when you rebuild the project
}
public int getMessageId() {
return messageId;
}
public void setMessageId(int message) {
this.messageId = messageId;
}
}
そして、あなたのMainActivity.class
@BindingAdapter({"showMessage"})
public static void runMe(View view, int messageId) {
if (messageId == Consts.START_SOME_ACTIVITY) {
view.getContext().startActivity(new Intent(view.getContext(), SomeActivity.class));
}
}
@Override
protected void onPause() {
super.onPause();
finish(); //only call if you want to clear this activity after go to other activity
}
最後に、activity_main.xml
<Button
Android:onClick="@{()-> myViewModel.onClick()}"
bind:showMessage="@{myViewModel.messageId}" />
データバインディングドキュメントに従って。それを行うには2つの方法があります。
1- MethodReferences :ビューをパラメーターとして関数に渡す必要があります。そうしないと、コンパイル時エラーが発生します。
この方法を使用する場合は、このようなイベントを処理する別のクラスを例としてここで実行します。
MyHandler
public class MyHandler {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
<data>
<variable
name="viewModel"
type="com.example.ViewModel"
<variable
name="myHandler"
type="com.example.MyHandler" />
</data>Android:onClick="@{myHandler.onClick(viewModel.product.id)}">
2- リスナーバインディング :この例のようにビューを渡す必要はありません。
ただし、startActivityを使用する場合は、viewModelでAndroidViewModelを拡張し、applicaionオブジェクトを使用します。
ViewModel
public class MyViewModel extends AndroidViewModel {
public void onClick(long productId) {
Intent intent = new Intent(getApplication(), ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
Android:onClick="@{() -> viewModel.onClick(viewModel.product.id)}">