web-dev-qa-db-ja.com

androidデータバインディングなしのMVVM

質問:データバインディングを使用せずに、MVVMでAndroidアプリを実装できますか?.

私が解決しようとしている問題は非常に単純です。バックエンドAPIからアイテムのリストを読み取り、それらをRecylerviewに表示します。

私がどのように実装しているか:

ビュー内-アクティビティとRecyclerViewAdapterモデルがあります:ApiResponseとデータモデルネットワーク-レトロフィットAPIサービス、RxJava2

viewModel部分の場合-基本的にRetrofitServiceを呼び出し、RxJava呼び出しを使用してデータを取得するViewModelクラス(何からも派生しない)があります。

ViewModelには次のような呼び出しがあります:

 void getItems();
 void addItemData();
 void removeItem();

rXJava2でサービスを呼び出す

 ObServable<ApiResponse> getItems();
 ObServable<ApiResponse> addItemData();
 ObServable<ApiResponse> removeItem();  

ビューはViewModelオブジェクトをインスタンス化します。 ViewModelは、作成中にAdapterオブジェクトのインスタンスを取得します。ビューで、ボタンをクリックすると、ViewModel#getItems()メソッドを呼び出すアクティビティのClickHandlerが呼び出されます。 ViewModelにはアダプタへのリンクがあるため、viewModelはアダプタ内のアイテムを更新して、RecyclerViewが自動的に更新されるようにします。

これがMVVMにとって正しいアプローチであるかどうかはわかりません。

データバインディングは、私にはスパゲッティのように思えます。

繰り返しますが、MVVMをAndroidでDataBindingなしで実装できますか?アプローチは大丈夫ですか?

8
tinker

はい!あなたはできる。しかし、私はあなたのアプローチがより良くなることができると思います。ビューモデルにビューへの参照があってはならないことに注意してください。 ViewModelはオブザーバブルを公開します。ビューでは、これらのオブザーバブルを観察し、変更に対応する必要があります。あなたはこのようなものを持つことができます:

注:この例はKotlinとLiveDataを使用しているため、なぜですか?しかし、これを使用してJava&Rxで使用できます。

ItemsViewModel : ViewModel() {
  private val items = MutableLiveData<List<Items>>()

  fun getAllItems() : LiveData<List<Items>> {
      return items
    }

   //..
}

ItemsActivity : Activity() {
  private var itemsAdapter: ItemsAdapter? = null
  private var viewModel: ItemsViewModel? = null

  override fun onCreate(savedInstance: Bundle) {
  // ...

   // Create your Adapter
   itemsAdapter = ItemsAdapter()
   recyclerView.adapter = itemsAdapter

   // Create and observe your view model
   viewModel =  ViewModelProviders.of(this).get(ItemsViewModel::class.Java)

   viewModel.getAllItems().observe(this, Observer {
      it?.let {
          adapter?.datasource = it
      }
}

この場合、ビューはビューモデルを監視し、アダプタに通知します。次に、アダプタで、データバインディングを使用せずに、通常どおりバインドを実行します。

9

間違いなく可能ですが、MVVMの「バインディング」部分をどのように解釈するかは完全にあなた次第です。私たちのチームでは、Androidデータバインディングの代わりにRxJavaでMVVMを使用します。ViewModelには、次のような出力と入力のインターフェイスがあります。

interface TasksViewModel {
        // inputs
        Observer<Task> taskAddedTrigger();
        Observer<Task> taskClickedTrigger();
        Observer<Task> taskCompletedTrigger();

        // outputs   
        Observable<Boolean> isLoading();
        Observable<List<Task>> tasks();
}

次に、ViewModelはRxJavaを使用して、非常に機能的なスタイルで入力を出力にマップします。 Fragmentは、ユーザー入力を受信するたびにViewModelに入力を提供します。出力をサブスクライブし、ViewModelの出力が変更されると、それに応じてユーザーインターフェイスを更新します。 ここにブログ投稿があります トピックを詳細にカバーしています(免責事項:私はそれを書きました)

5
Malte

MVVMの際立った特徴は、ViewModelViewに直接結合されていないことです(実際、ViewModelを別のレイアウトにバインドできます)。これは、単体テストの容易さにも影響を及ぼします。 Adapterを参照することで、技術的にはMVCに似ています。データバインディングを使用する必要はありませんが、真のMVVMの場合、必要なデータをプルできるように、ビューに変更を通知するための別のオブザーバーパターンメカニズムが必要になると思います。

2
Alan Todtenkopf

あなたの言うことSince ViewModel has a link to Adapterこれが問題です。ViewModelにはビューへの参照がないはずです。アダプターにはビューがあるので、これを行うと、MVVMにまったく従わなくなります。

データバインディングなしでMVVMを使用することはできますが、データの変更についてビューに通知する方法が必要です。LiveData(推奨される方法)、Java Observable、Rx、またはカスタム実装ですらあります。ビューは変更と更新自体について通知を受け取ります。あなたの場合、ビューはアダプターを更新します。

例については、ここで私の答えを参照してください MVVMでアクションは許可されていますか?Android

1