MVVM:Android MVVMモードのDataBinding
16961 ワード
GoogleのDataBindingのリリースは長い間、今も成熟して安定しています.私の前のプロジェクトはずっとMVPを使って、実はずっとMVVMモードに変えたいと思っています.結局、それはデータ駆動を使って、MVPモードの中のいくつかの欠陥を解決することができますが、ずっとあまり时間がありません.今日はAndroidのMVVMモードを整理してどのように使うかを整理します.この文章は実用的に偏っています.MVVMの概念、メリットとデメリットは紹介しません.皆さんはそれぞれ各フォーラムで見てみましょう.文章がたくさんあります.
DataBinding機能の有効化
Android sudioにプロジェクトを新規作成し、Moduleのbuild.gradleファイルandroidノードにdatabinding{enable true}を追加してDataBindingを有効にします.構成されたbuild.gradleファイルの内容は次のとおりです.
MVVMコンポーネント
私のプロジェクトでは、MVVMはmodel、view、viewmodelの3つの部分から構成されています. model:データモデルは、ビジネスエンティティのデータを格納する一方で、データの取得、更新などの操作も担当します. view:ビュー、ビューはデータ表示のみを担当し、ここでビュー上のイベント処理もviewから剥離し、イベント処理は独立してEventHandleに書くこともできるし、ViewModelに定義することもできる. viewmodel:viewとmodelを持ち、業務論理処理、modelとviewの協力を担当しているが、実際にmodelデータが更新された後、viewmodelは何か動作をしてviewを更新する必要はない.この点はmvpと大きく異なり、modelが更新された後databindingフレームワークでviewに直接更新された. binding:実は以上の3つの部分のほかに、一部の内容が重要ですが、この部分の内容はフレームワークが自動的に生成してくれたのです.それはフレームワークが生成した様々なViewDataBindingです.ここではbindingと略称し、view層データの縛り問題を解決します.
MVVMを使いましょう!
ここでは、フレームワークによってviewレイヤにデータをバインドする最も簡単なアプリケーションについて説明します.データモデルAuthorを新規作成します.コードは次の です. activityのレイアウトファイルを新規作成し、layoutノードにdata定義を追加します.Rebuild Project、フレームワークは自動的にAuthorBindingクラスを生成します. AuthorView Modelを新規作成し、xmlファイルで属性バインドAuthorView Model を設定します.
xml:
activity:
イベントバインド
mvvmを使用した後、Activityでfindviewを使用しないで、各イベントを設定します.イベントをViewModeまたは他のクラスに定義し、xmlファイルで対応する変数を定義し、イベントバインド処理方法を指定します.たとえば、xml:
ここで定義したメソッドパラメータは、従来の構造と一致しなければならないことに注意してください.そうしないと、エラーが発生します.
Fragment、およびカスタムViewの使用
FragmentにはsetContentの方法がありません.タブレットや携帯電話のニーズに適応するために、いくつかの業務を1つのViewに抽象化し、設備の大きさに応じてViewをインタフェースに組み込む場合もあります.そのため、私たちのMVVMはView層でViewをサポートし、特にView Groupを継承してlayoutからinflateから出てきた1つのViewを追加します.この2つの状況も同様に使用できます.方法は次のとおりです. fragment fragment fragmentでは、xmlファイル、model、modelviewを定義するのは前述の内容と同じであり、異なる場合はdatabindingをインスタンス化する方法が異なる可能性があります.fragmentでは、次の文でdatabinding をインスタンス化します. view定義xmlファイル、model、modelviewは前述の内容と一致し、viewの構築関数でdatabinding をインスタンス化するために以下の方法を呼び出す.
属性の双方向バインド
@=
属性バインド時に@=を用いて双方向バインドし,双方向バインドを用いてEditTextに内容を入力すると自動的に対応するフィールドに格納され,他のViewがこの値を一方向バインドしている場合,その表示も変化する.
Observable
純粋なJava Modelクラスは、変更された後もUIは更新されません.データバインディング後にデータモデルの値を変更したい場合、UIを更新するにはどうすればいいですか?ここではObsservableを使用します.現在@BindableとnotifyPropertyChangedを組み合わせて使用しています.次のデータモデルがUIにバインドされた後、値を変更すると、UIが自動的に更新されます.
ObservableField
Modelのフィールドが変更されてUIが自動的に更新される場合、Obsservableを使用するとより多くのコードが生成されるに違いありません.誰もそんなに多くのコードを書いていませんよね?では、フィールドをObservableFieldと定義すればOKです.ObservableFieldが提供するタイプは、ObservableFile、ObservableBoolean、ObservableByte、ObservableInt、ObservableChar、ObservableLong、ObservableFloat、ObserbableDouble、ObservableParcelableであり、これらは基本的にすべてのデータ型のニーズを満たすことができます.
xmlでのバインドは、以前のフィールドバインドと同じですが、javaで値を変更したり取得したりするのは少し異なります.javaでは次のようにアクセスします.
カスタムプロパティー
前にViewにカスタム属性を追加するのはいつも面倒で、styleで定義し、xmlで使用し、viewの構造で解析し、Databindingを使用してカスタム属性を実現するのは便利です.xmlファイルで直接属性を設定すると、フレームワークはフルネームスペースに基づいて対応するsetメソッドを検索しますが、対応するsetメソッドが見つからないとエラーになるので注意してください.たとえば、次にViewがButtonに継承されていることを書き直し、colorのカスタム属性を追加します.
xml呼び出し方式
ExtButton Javaファイル
属性に変更リスナーを追加
データモデルのいくつかの属性値が変化した場合、ビジネス処理を行う可能性がありますが、Model属性の変化をキャプチャするにはどうすればいいのでしょうか.addOnPropertyChangedCallbackでは、プロパティの変更をキャプチャできます.
アトリビュート値が変化したときのアニメーション
モデルのプロパティ値が変更され、UIに更新されると、アニメーションが必要になります.bindingにOnRebindCallbackリスニングを追加し、対応する方法でアニメーション効果を追加することができます.
いくつかの暗黙的な表現繰り返し発現 隠しUI更新 RecyclerViewの使用
ここではRecyclerViewの使用について説明します.他のRecyclerViewはアダプタモードViewを使用する代表的なもので、その使用について説明します.ViewPager、Spinerなどのコントロールの使用は基本的に一致しています. itemのlayoutファイル を作成 Adapter を書く
いくつかの役に立つ注釈
@BindingAdapter
BindingAdapterでは、いくつかのカスタムプロパティ設定を実行できます.
もし私たちが1つのImageViewに1枚のネットワークピクチャを表示するならば、オリジナルの方法はandroid:srcしかありませんが、この方法は1つのネットワークピクチャをロードして表示することはできませんが、私たちもImageViewを書き換えたくありません.このときBindAdapterメソッドは不思議な役割を果たします.まず、ピクチャ処理のクラスを定義し、ピクチャのロード方法を定義します.注意するには、メソッドは静的メソッドでなければなりません.No側でエラーが発生します.
この方法では自分のピクチャロード方法を書きますが、選択したピクチャロードライブラリによって違います.ImageLoader、Glideなどです.
xmlファイルでapp:imageプロパティを使用してピクチャアドレスを設定し、layoutノードにxmlns:app="を追加することを忘れないでください.http://schemas.android.com/apk/res-auto"
この方法の有用性は大きいが,前述したRecyclerViewのデータロードは単純にRecyclerViewでデータ動的更新UI表示に基づいているだけであり,もし私がActivityにRecyclerViewと他のコントロールを持っていて,インタフェース全体のデータとサーバがhttpリクエストで完了した場合,サービスリクエストが完了した後にインタフェース全体のデータを更新するにはどうすればよいのだろうか.私の解決策はBindingAdapterを使用して、RecyclerViewにdata属性を設定し、data属性はデータモデル全体のリストフィールド値をバインドし、BindingAdaterメソッドでAdapterデータソースを更新してRecyclerViewのデータ内容を更新することです.
@Bindable
@Bindableで注記されたgetメソッドに対応する変数はBRリソースにレコードを生成します.notifyPropertyChanged(BR.*)と組み合わせて、データモデル値の変更後にUIに自動的に更新されます.
内容はこれだけ書きましょう.みんなに少し助けてほしいです.
DataBinding機能の有効化
Android sudioにプロジェクトを新規作成し、Moduleのbuild.gradleファイルandroidノードにdatabinding{enable true}を追加してDataBindingを有効にします.構成されたbuild.gradleファイルの内容は次のとおりです.
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "leix.lebean.android.mvvm"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dataBinding{
enabled true
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile project(':lib')
}
MVVMコンポーネント
私のプロジェクトでは、MVVMはmodel、view、viewmodelの3つの部分から構成されています.
MVVMを使いましょう!
ここでは、フレームワークによってviewレイヤにデータをバインドする最も簡単なアプリケーションについて説明します.
/**
* Name:Author
* Description:
* Author:leix
* Time: 2017/4/5 14:57
*/
public class Author extends BaseObservable {
private String name;
private Date birthdate;
private boolean male;
@Bindable
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Bindable
public Date getBirthdate() {return birthdate;}
public void setBirthdate(Date birthdate) {this.birthdate = birthdate;}
@Bindable
public boolean isMale() { return male;}
public void setMale(boolean male) {this.male = male;}
/**
* Name:AuthorViewModel
* Description:
* Author:leix
* Time: 2017/4/5 15:01
*/
public class AuthorViewModel {
private Activity activity;
private ViewDataBinding binding;
public Author author;
public AuthorViewModel(Activity activity, ViewDataBinding binding) {
author = new Author();
author.setBirthdate(new Date());
author.setMale(true);
author.setName("Jim");
this.activity = activity;
this.binding = binding;
this.binding.setVariable(BR.vMode,this);
}
}
xml:
activity:
public class AuthorActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewDataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_author);
AuthorViewModel viewModel = new AuthorViewModel(this, binding);
}
}
イベントバインド
mvvmを使用した後、Activityでfindviewを使用しないで、各イベントを設定します.イベントをViewModeまたは他のクラスに定義し、xmlファイルで対応する変数を定義し、イベントバインド処理方法を指定します.たとえば、xml:
viewmodel:
public void buttonClick(View view) {
Toast.makeText(activity, " ", Toast.LENGTH_SHORT).show();
}
ここで定義したメソッドパラメータは、従来の構造と一致しなければならないことに注意してください.そうしないと、エラーが発生します.
Fragment、およびカスタムViewの使用
FragmentにはsetContentの方法がありません.タブレットや携帯電話のニーズに適応するために、いくつかの業務を1つのViewに抽象化し、設備の大きさに応じてViewをインタフェースに組み込む場合もあります.そのため、私たちのMVVMはView層でViewをサポートし、特にView Groupを継承してlayoutからinflateから出てきた1つのViewを追加します.この2つの状況も同様に使用できます.方法は次のとおりです.
/**
* Name:AuthorFragment
* Description:
* Author:leix
* Time: 2017/4/5 16:01
*/
public class AuthorFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.activity_author, container, false);
AuthorViewModel viewModel=new AuthorViewModel(getActivity(),binding);
return binding.getRoot();
}
}
/**
* Name:AuthorView
* Description:
* Author:leix
* Time: 2017/4/5 16:08
*/
public class AuthorView extends FrameLayout {
public AuthorView(@NonNull Context context) {
this(context, null);
}
public AuthorView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, null, -1);
}
public AuthorView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(getContext()), R.layout.activity_author, this, true);
AuthorViewModel viewModel = new AuthorViewModel(context, binding);
}
}
属性の双方向バインド
@=
属性バインド時に@=を用いて双方向バインドし,双方向バインドを用いてEditTextに内容を入力すると自動的に対応するフィールドに格納され,他のViewがこの値を一方向バインドしている場合,その表示も変化する.
Observable
純粋なJava Modelクラスは、変更された後もUIは更新されません.データバインディング後にデータモデルの値を変更したい場合、UIを更新するにはどうすればいいですか?ここではObsservableを使用します.現在@BindableとnotifyPropertyChangedを組み合わせて使用しています.次のデータモデルがUIにバインドされた後、値を変更すると、UIが自動的に更新されます.
/**
* Name:Author
* Description:
* Author:leix
* Time: 2017/4/5 14:57
*/
public class Author extends BaseObservable {
private String name;
private Date birthdate;
private boolean male;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthdate) {
this.birthdate = birthdate;
notifyPropertyChanged(BR.birthdate);
}
@Bindable
public boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
notifyPropertyChanged(BR.male);
}
}
ObservableField
Modelのフィールドが変更されてUIが自動的に更新される場合、Obsservableを使用するとより多くのコードが生成されるに違いありません.誰もそんなに多くのコードを書いていませんよね?では、フィールドをObservableFieldと定義すればOKです.ObservableFieldが提供するタイプは、ObservableFile、ObservableBoolean、ObservableByte、ObservableInt、ObservableChar、ObservableLong、ObservableFloat、ObserbableDouble、ObservableParcelableであり、これらは基本的にすべてのデータ型のニーズを満たすことができます.
/**
* Name:Book
* Description:
* Author:leix
* Time: 2017/4/5 10:32
*/
public class Book {
public final ObservableField name = new ObservableField<>();
public final ObservableField description = new ObservableField<>();
public final ObservableInt price = new ObservableInt();
public final ObservableField publishDate = new ObservableField<>();
}
xmlでのバインドは、以前のフィールドバインドと同じですが、javaで値を変更したり取得したりするのは少し異なります.javaでは次のようにアクセスします.
book.name.get();// name
book.name.set("Thinking in java");// name
カスタムプロパティー
前にViewにカスタム属性を追加するのはいつも面倒で、styleで定義し、xmlで使用し、viewの構造で解析し、Databindingを使用してカスタム属性を実現するのは便利です.xmlファイルで直接属性を設定すると、フレームワークはフルネームスペースに基づいて対応するsetメソッドを検索しますが、対応するsetメソッドが見つからないとエラーになるので注意してください.たとえば、次にViewがButtonに継承されていることを書き直し、colorのカスタム属性を追加します.
xml呼び出し方式
ExtButton Javaファイル
**
* Name:ExtButton
* Description:
* Author:leix
* Time: 2017/4/7 09:48
*/
public class ExtButton extends Button {
public ExtButton(Context context) {
super(context);
}
public ExtButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExtButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setColor(@ColorInt int color) {
setTextColor(color);
}
}
属性に変更リスナーを追加
データモデルのいくつかの属性値が変化した場合、ビジネス処理を行う可能性がありますが、Model属性の変化をキャプチャするにはどうすればいいのでしょうか.addOnPropertyChangedCallbackでは、プロパティの変更をキャプチャできます.
author.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
if (i == BR.name) {
}
}
});
アトリビュート値が変化したときのアニメーション
モデルのプロパティ値が変更され、UIに更新されると、アニメーションが必要になります.bindingにOnRebindCallbackリスニングを追加し、対応する方法でアニメーション効果を追加することができます.
binding.addOnRebindCallback(new OnRebindCallback() {
@Override
public boolean onPreBind(ViewDataBinding binding) {
return super.onPreBind(binding);
}
@Override
public void onCanceled(ViewDataBinding binding) {
super.onCanceled(binding);
}
@Override
public void onBound(ViewDataBinding binding) {
super.onBound(binding);
}
});
いくつかの暗黙的な表現
ここではRecyclerViewの使用について説明します.他のRecyclerViewはアダプタモードViewを使用する代表的なもので、その使用について説明します.ViewPager、Spinerなどのコントロールの使用は基本的に一致しています.
/**
* Name:AuthorAdapter
* Description:
* Author:leix
* Time: 2017/4/5 16:51
*/
public class AuthorAdapter extends RecyclerView.Adapter {
List dataSet;
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.activity_author, null, false);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.getBinding().setVariable(BR.author,dataSet.get(position));
}
@Override
public int getItemCount() {
return dataSet == null ? 0 : dataSet.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
protected T binding;
public ViewHolder(T binding) {
super(binding.getRoot());
this.binding = binding;
}
public T getBinding() {
return binding;
}
}
}
いくつかの役に立つ注釈
@BindingAdapter
BindingAdapterでは、いくつかのカスタムプロパティ設定を実行できます.
もし私たちが1つのImageViewに1枚のネットワークピクチャを表示するならば、オリジナルの方法はandroid:srcしかありませんが、この方法は1つのネットワークピクチャをロードして表示することはできませんが、私たちもImageViewを書き換えたくありません.このときBindAdapterメソッドは不思議な役割を果たします.まず、ピクチャ処理のクラスを定義し、ピクチャのロード方法を定義します.注意するには、メソッドは静的メソッドでなければなりません.No側でエラーが発生します.
@BindingAdapter({"image"})
public static void loadImage(ImageView view, String url) {
}
この方法では自分のピクチャロード方法を書きますが、選択したピクチャロードライブラリによって違います.ImageLoader、Glideなどです.
xmlファイルでapp:imageプロパティを使用してピクチャアドレスを設定し、layoutノードにxmlns:app="を追加することを忘れないでください.http://schemas.android.com/apk/res-auto"
この方法の有用性は大きいが,前述したRecyclerViewのデータロードは単純にRecyclerViewでデータ動的更新UI表示に基づいているだけであり,もし私がActivityにRecyclerViewと他のコントロールを持っていて,インタフェース全体のデータとサーバがhttpリクエストで完了した場合,サービスリクエストが完了した後にインタフェース全体のデータを更新するにはどうすればよいのだろうか.私の解決策はBindingAdapterを使用して、RecyclerViewにdata属性を設定し、data属性はデータモデル全体のリストフィールド値をバインドし、BindingAdaterメソッドでAdapterデータソースを更新してRecyclerViewのデータ内容を更新することです.
@Bindable
@Bindableで注記されたgetメソッドに対応する変数はBRリソースにレコードを生成します.notifyPropertyChanged(BR.*)と組み合わせて、データモデル値の変更後にUIに自動的に更新されます.
/**
* Name:Author
* Description:
* Author:leix
* Time: 2017/4/5 14:57
*/
public class Author extends BaseObservable {
private String name;
private Date birthdate;
private boolean male;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthdate) {
this.birthdate = birthdate;
notifyPropertyChanged(BR.birthdate);
}
@Bindable
public boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
notifyPropertyChanged(BR.male);
}
}
内容はこれだけ書きましょう.みんなに少し助けてほしいです.