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ファイルの内容は次のとおりです.
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つの部分から構成されています.
  • 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を新規作成します.コードは次の
  • です.
    /**
     * 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;}
    
  • activityのレイアウトファイルを新規作成し、layoutノードにdata定義を追加します.Rebuild Project、フレームワークは自動的にAuthorBindingクラスを生成します.
  • 
    
        
        
    
    
  • AuthorView Modelを新規作成し、xmlファイルで属性バインドAuthorView Model
  • を設定します.
    /**
     * 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つの状況も同様に使用できます.方法は次のとおりです.
  • fragment fragment fragmentでは、xmlファイル、model、modelviewを定義するのは前述の内容と同じであり、異なる場合はdatabindingをインスタンス化する方法が異なる可能性があります.fragmentでは、次の文でdatabinding
  • をインスタンス化します.
    /**
     * 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();
        }
    }
    
  • view定義xmlファイル、model、modelviewは前述の内容と一致し、viewの構築関数でdatabinding
  • をインスタンス化するために以下の方法を呼び出す.
       /**
        * 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);
               }
           });
    

    いくつかの暗黙的な表現
  • 繰り返し発現
      
            
            
    
    
    
    
    
  • 隠しUI更新
    
    
    
  • RecyclerViewの使用
    ここではRecyclerViewの使用について説明します.他のRecyclerViewはアダプタモードViewを使用する代表的なもので、その使用について説明します.ViewPager、Spinerなどのコントロールの使用は基本的に一致しています.
  • itemのlayoutファイル
  • を作成
          
          
              
                  
              
              
                  
                  
              
         
    
  • Adapter
  • を書く
          /**
           * 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);
        }
    }
    

    内容はこれだけ書きましょう.みんなに少し助けてほしいです.