9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[android-architecture] MVVM & DataBindingを読んでみる

Last updated at Posted at 2018-12-18

はじめに

この記事は、以前の記事の続きです。
GoogleSamples(android-architecture)を斜め読みして筆者が印象に残った内容を書きます。

バックナンバー

1. Model-View-Presenter(MVP)+αを読んでみる
2. Clean Architectureを読んでみる
3. MVVM & DataBindingを読んでみる
4. MVVM & LiveDataを読んでみる

Model‑View‑ViewModel (MVVM) - DataBinding

##ViewModel
ObservableFieldを使ってUIとViewModelをバインドしています。
(ちなみに、BaseObservable#notifyPropertyChanged を使ってもできます)
ViewModelはRepositoryも持っていて、必要な情報は自分で用意します。

ViewModel
public abstract class XxxViewModel extends BaseObservable {
    ObservableField<Xxx> xxx = new ObservableField<>();
    Repository repository;
}

##BindingAdapter
xmlの要素とViewModelの変数を変換します。
また、イベントハンドラとしても使えます。
便利な反面、乱用すると追いにくい不具合の温床にもなりかねない諸刃の剣の側面も。

BindingAdapter

// xml の記載
<ListView app:items="@{viewmodel.items}"/>

// ソース側
public class ListBindings {
    @BindingAdapter("app:items")
    public static void setItems(ListView listView, List<xxx> items) {
        XxxAdapter adapter = (XxxAdapter) listView.getAdapter();
        if (adapter != null) adapter.method(items);
    }
}

// xml の記載
<SwipeRefreshLayout android:onRefresh="@{viewmodel}" />

// ソース側
public class XxxBinding {
    @BindingAdapter("android:onRefresh")
    public static void onSwipeRefresh(SwipeRefreshLayout v, XxxViewModel vm) {
        v.setOnRefreshListener(() -> {vm.xxxx()});
    }
}

##ViewModelの管理方法
このサンプルコードでは、UI無しFragmentを定義し、FragmentがViewModelの参照を管理しています。
メリットは、例えば画面回転をした際に毎度ViewModelをインスタンス化すると効率が悪いですが
Fragmentのフィールドとして保持しグローバル変数的に参照すれば、再度インスタンス化せずに済みます。
(実はこの辺り、AACで解決されるので自作不要です。AACのViewModel管理はこちら

ソースがちょっと長いので、意味が通じる程度に要約(圧縮、改変した)コードを記載します。

ViewModelHolder
public class ViewModelHolder<VM> extends Fragment {
    private VM vm; // setter, getterも定義してね
    public ViewModelHolder() {...}

    public static <M> ViewModelHolder create(M vm) {
        ViewModelHolder<M> holder = new ViewModelHolder<>();
        holder.vm = vm;
        return holder;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }
}
Activity
public class XxxActivity extends AppCompatActivity {
    public static final String TAG = "TAG";
    private TasksViewModel vm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        XxxFragment fragment = findOrCreateViewFragment();
        ...
        vm = findOrCreateViewModel();
        ...
        fragment.setViewModel(vm);
    }
    private XxxViewModel findOrCreateViewModel() {
        ViewModelHolder<XxxViewModel> holder = (ViewModelHolder<XxxViewModel>) getSupportFragmentManager().findFragmentByTag(TAG);
        if (holder == null || holder.getViewmodel() == null) {
            hodler = ViewModelHolder.create(new XxxViewModel(xxx));
            /* add ViewHodler(fragment) */
            /* fragment manager で add してね*/
        }
        return holder.getViewmodel();
    }
    private XxxFragment findOrCreateViewFragment() {
        XxxFragment fragment = (XxxFragment) getSupportFragmentManager().findFragmentById(xxx);
        if (fragment == null) {
            fragment = XxxFragment.newInstance();
            /* fragment manager で add してね*/
        }
        return fragment;
    }
}
Fragment
public class XxxFragment extends Fragment {
    private XxxViewModel vm;
    private XxxFragBinding binding;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        binding = XxxFragBinding.inflate(inflater, container, false);
        binding.setView(this);
        binding.setViewmodel(vm);
        return binding.getRoot();
    }
    public void setViewModel(TasksViewModel vm) {this.vm = vm;}
}
Fragment(xxx_frag.xml)
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <import type="android.view.View" />
        <variable name="view" type="XxxFragment"/>
        <variable name="vm" type="XxxViewModel" />
    </data>
    ...
        <LinearLayout
            android:visibility="@{vm.xxx ? View.GONE : View.VISIBLE}" />
    ...
</layout>

いかがでしたでしょうか

xml側の記載量が増えますが、ソースコード側はいちいちViewを検索してイベントハンドラを登録して〜といった、面倒なView操作がググッと減りそうですね。ビジネスロジックの変更に集中できる様になれば、生産性も上がるのかもしれません。

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?