1. 程式人生 > >MVVM的使用和原理

MVVM的使用和原理

一、MVC、MVP、MVVM的區別。Android以往的架構都類似於MVC,在MVC中檢視層是非常弱化的,造成C層程式碼量非常大。所以MVC架構模式可以理解成M-VC,是一個二層架構。幾乎所有的檢視邏輯都要寫在Activity中,一個Activity可能有上千行。MVP改善了MVC的這種問題,將檢視層抽象成一個IView介面,將業務邏輯和資料處理丟給Presenter,Presenter持有Iview的引用,解決了MVC中Activity程式碼過於臃腫的問題。而這也造成了一個問題,當業務過多時,IView的數量也會增多。如果一個Presenter只操作一個IView則違反了單一原則,粒度太大,解耦效果不好;如果將不同的功能區分成不同的IView介面,粒度太小。程式碼則太過碎片化。而且資料變化之後UI不會自動變化,資料不會直接映視到UI中。MVVM解決了上述的缺點,MVVM最大的亮點就是資料和View的雙向繫結,MVVM通過DataBinding將View和Model進行繫結,當Model發生改變時VIew也會改變,不需要在Activity中再新增程式碼。

二、MVVM的使用。

1、在build.gradle中新增DataBinding,DataBinding是實現MVVM的一個工具,Android studio中已經整合,新增起來非常方便。
 dataBinding{
        enabled true
    }

Data Binding 外掛需要Gradle 1.3以上及Android Studio 1.3.

2、引入DataBinding之後,layout的寫法會發生些改變。要以layout標籤作為根節點。通過data標籤來繫結model。寫法如下

<layout  xmlns:android="http://schemas.android.com/apk/res/android"    >   
         <data>    
                <!--此處定義該佈局要用到的資料的名稱及型別-->
         </data>
         <!--此處按照常規方式定義要使用的佈局,其中可以使用binding表示式代表屬性值,所謂binding表示式,指形如"@{user.firstName}"的表示式-->
</layout>

在對應的VIew中新增Model,寫法是@{內容},比如@{user.name},除此之外DataBinding還支援其他運算子如字串拼接(@{`名字`:·+user.name}),數學表示式(+-/%等),三元操作符android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"支援dimen,還支援color、string、drawable、anim等

3、在設定檢視的時候,不能再通過setContentView來設定檢視,要通過DataBindingUtil.setContentView來設定檢視,在設定DataBindingUtil.setContentView時會返回一個Binding物件,這個Binding物件的命名原則是佈局名稱(首字母大寫)+Binding,例如我的佈局名稱叫做activity_main,我的Binding物件就叫做ActivityMainBinding。
ActivityMainBinding binding=DataBindingUtil.setContentView(this,R.layout.activity_main)
這個Binding類所在包為apppackage/databinding中。

然後通過這個BInding來設定資料

  binding.setUser(user);

4、載入自定義屬性。當我們載入圖片時,需要新增自定義的屬性。自定義屬性需要在實體中寫好。

 @BindingAdapter("bind:header")
    public static void getImage(ImageView view,String url)
    {
        Picasso.with(view.getContext()).load(url).into(view);
    }
這個方法一定要是靜態的。

然後在layout中呼叫。

 <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:header="@{user.header}"
            />
5、當modle發生改變時,改變VIew。兩種寫法,第一個直接讓實體類繼承BaseObservable,第二種寫法是將實體抽離出來單獨放入一個類。
public class UserField {
    public ObservableField<String> name=new ObservableField<>();

    public ObservableField<String> password=new ObservableField<>();
}

然後在佈局中引入UserField。

<variable
        name="field"
        type="com.example.administrator.mvvmframwork.UserField"/>
在layout應用model中用field代替user
  <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="@{`    `+field.name}"
            />
別忘了在Activity中新增
binding.setField(userField);
無論哪種寫法,都需要在對應的屬性的set方法上新增@Bindable
 @Bindable
    public String getPassword() {

        return password;
    }

6、listVIew和DataBinding

listview或者RecyclerView和DataBinding搭配使用時很方便,因為adapter只需要寫一個就好了。

我們利用item和model繫結,繫結過程和上面差不多,主要是adapter 的寫法有了些變化

public class ComonAdapter<T> extends BaseAdapter {
    private Context context;
    private LayoutInflater inflater;
    private int layoutId;
    private int variableId;
    //List <Food> User
    private List<T> list;

    public ComonAdapter(Context context, LayoutInflater inflater, int layoutId, int variableId, List<T> list) {
        this.context = context;
        this.inflater = inflater;
        this.layoutId = layoutId;
        this.variableId = variableId;
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewDataBinding dataBinding;
        if(convertView==null)
        {
            dataBinding= DataBindingUtil.inflate(inflater,layoutId,parent,false);
        }else
        {
            //重用DataBinding
            dataBinding=DataBindingUtil.getBinding(convertView);
        }
            dataBinding.setVariable(variableId,list.get(position));
        //返回item檢視
        return dataBinding.getRoot().getRootView();
    }
}

variableId是一個Model,是資料和View繫結的型別,和上面的DataBinding一樣是在編譯時生成的。我上面使用的variableID是BR.food。點進去它的內容很像我們的R檔案。
public class BR {
        public static final int _all = 0;
        public static final int field = 1;
        public static final int food = 2;
        public static final int name = 3;
        public static final int password = 4;
        public static final int user = 5;
}

7、新增點選事件。我們直接去model裡面去寫

 public  void click(View view)
    {
        Toast.makeText(view.getContext(),getDescription(),Toast.LENGTH_SHORT).show();
    }
在layout中去呼叫
  <TextView
            android:id="@+id/description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:layout_toRightOf="@id/iv"
            android:ellipsize="end"
            android:maxLines="3"
            android:onClick="@{food.click}"
            android:text="@{food.description}"/>


三、原理。

採用了MVVM之後,編譯之後的class類會很多。每個layout中的一個data標籤都會單獨生成一個Binding。一個activity_main.xml檔案編譯後會生成一個activity_main.xml和一個activity_main-layout.xml,在activity_main中去掉了<layout>標籤,<layout>這部分內容單獨編譯進activity_main-layout.xml中 未完待續...