web-dev-qa-db-ja.com

Android MVVM startActivityのベストプラクティス

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のみを含めるのがベストプラクティスですか?

29

あなたの質問への答えはあなたの目標は何ですか?

懸念の分離に[〜#〜] mvvm [〜#〜]を使用して、Viewmodelを単体テストできるようにする場合Contextを必要とするすべてのものをViewmodelとは別にする必要があります。 Viewmodelにはアプリのコアビジネスロジックが含まれており、外部依存関係はありません。

しかし、私はあなたが行く場所が好きです:)アクティビティが開かれる決定がビューにある場合、それのためのJUnitテストを書くことは非常に難しいです。ただし、startActivity()呼び出しを実行するViewmodelにオブジェクトを渡すことができます。これで、Unit testでこのオブジェクトをモックし、正しいActivityが開かれていることを確認できます

19
Kaskasi

ViewModelの中に入れるのは絶対に完璧ですが、ViewModel/ActivityからFragmentを設定する必要があります。

MVVMアーキテクチャを学ぶためにたどることができるいくつかのリンクがあります。

アプローチAndroid with MVVM
Android MVVM
https://github.com/ivacf/archi
People-MVVM
Android上のMVVM:知っておくべきこと

9
Ravi Rupareliya

私がそれをする方法は、あなたの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)
})
7
gahfy

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}" />
3
lehongphucit

データバインディングドキュメントに従って。それを行うには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)}">
0
Moaz Rashad