C# 很早就有了MVVM的開發模式,Android手機中的MVVM一直到去年Google的I\O大會上才推出,姍姍來遲。MVVM這中開發模式的優勢自沒必要多說,能夠實現視圖和邏輯代碼的解耦,並且,按照Google的說法,使用了MVVM的開發模式,還能夠提升佈局文件的解析速度,我的以爲這一點很是重要。咱們在安卓開發中常常須要寫不少個findViewById,讓人心煩,不少人不想寫這個因而用了一些註解框架,但是註解框架不管性能多好,效率老是要低於findViewById的,所以,Android中的MVVM也即databinding能夠幫助咱們完全解決這個問題。OK,廢話很少說,咱們來看看具體要怎麼在Android開發中使用MVVM。android
在低版本的AndroidStudio中使用DataBinding稍微有點麻煩,這裏不作介紹。我這裏以AndroidStuido2.1爲例來介紹DataBinding。本文主要包含如下幾方面內容:json
1.基本使用api
2.綁定ImageView網絡
3.綁定ListViewapp
4.點擊事件處理框架
5.數據更新處理ide
好了,那就開始吧!佈局
1.基本使用建立好一個Android Project以後,在gradle文件中添加以下幾行代碼,表示開啓databinding:性能
android { ... ... ... dataBinding{ enabled true } }
要使用數據綁定,咱們得首先建立一個實體類,好比User實體類,以下:gradle
/** * Created by 王鬆 on 2016/7/31. */ public class UserEntity { private String username; private String nickname; private int age; public UserEntity() { } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public UserEntity(int age, String nickname, String username) { this.age = age; this.nickname = nickname; this.username = username; } }
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" > <data> <variable name="user" type="org.lenve.databinding1.UserEntity"/> </data> <LinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="org.lenve.databinding1.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.username}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.nickname}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{String.valueOf(user.age)}"/> </LinearLayout> </layout>
<data> <import type="org.lenve.databinding1.UserEntity"/> <variable name="user" type="UserEntity"/> </data>
<data> <import type="org.lenve.databinding1.UserEntity" alias="Lenve"/> <variable name="user" type="Lenve"/> </data>
看完data節點咱們再來看看佈局文件,TextView的text屬性被我直接設置爲了@{user.username},這樣,該TextView一會直接將UserEntity實體類的username屬性的值顯示出來,對於顯示age的TextView,我用了String.valueOf來顯示,由於你們知道TextView並不能直接顯示int型數據,因此須要一個簡單的轉換,事實上,咱們還能夠在{}裏邊進行一些簡單的運算,這些我一會再說。
最後,咱們來看看Activity中該怎麼寫,setContentView方法不可以再像之前那樣來寫了,換成下面的方式:
DataBindingUtil.setContentView(this, R.layout.activity_main)
ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); UserEntity user = new UserEntity(); user.setAge(34); user.setUsername("zhangsan"); user.setNickname("張三"); activityMainBinding.setUser(user); }
運行,顯示效果以下:
OK,那咱們剛纔還說到能夠在@{}進行簡單的計算,都有哪些計算呢?咱們來看看:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.username??user.nickname}"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{`username is :`+user.username}"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@{user.age < 30 ? 0xFF0000FF:0xFFFF0000}" android:text="@{String.valueOf(user.age)}"/>我在這裏給TextView設置背景的時候,作了一個簡單的判斷,若是用戶的年齡小於30,背景就顯示爲藍色,不然背景就顯示爲紅色,DataBinding裏支持小於號可是不支持大於號,索性,大於小於號我都用轉義字符來表示。
另外,DataBinding對於基本的四則運算、邏輯與、邏輯或、取反位移等都是支持的,我這裏再也不舉例。
2.綁定ImageView
OK,上文只是一個簡單的綁定文本,下面咱們來看看怎麼樣綁定圖片,這裏咱們還得介紹DataBinding的另外一項新功能,就是關於DataBinding自定義屬性的問題,事實上,在咱們使用DataBinding的時候,能夠給一個控件自定義一個屬性,好比咱們下面即將說的這個綁定ImageView的案例。假設我如今想要經過Picasso顯示一張網絡圖片,正常狀況下這個顯示很簡單,但是若是我要經過DataBinding來實現,該怎麼作呢?咱們可使用
@BindingAdapter
/** * Created by 王鬆 on 2016/7/31. */ public class User { private String username; private String userface; public User() { } public User(String userface, String username) { this.userface = userface; this.username = username; } @BindingAdapter("bind:userface") public static void getInternetImage(ImageView iv, String userface) { Picasso.with(iv.getContext()).load(userface).into(iv); } public String getUserface() { return userface; } public void setUserface(String userface) { this.userface = userface; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <data> <variable name="user" type="org.lenve.databinding2.User"/> </data> <LinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="org.lenve.databinding2.MainActivity"> <ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" app:userface="@{user.userface}"></ImageView> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.username}"/> </LinearLayout> </layout>
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); dataBinding.setUser(new User("http://img2.cache.netease.com/auto/2016/7/28/201607282215432cd8a.jpg", "張三")); }
3.綁定ListView
好了,看完了簡單使用以後,不知道你有沒有喜歡上DataBinding,若是尚未,那就再來看看使用DataBinding來給ListView綁定數據吧,這個你必定會喜歡上的。由於使用這中方式來綁定太簡單了。
先來看看咱們要作的效果吧:
就是一個ListView,左邊顯示圖片,右邊顯示文本,這樣一個效果。OK,那就一步一步來吧,先是主佈局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="org.lenve.databinding3.MainActivity"> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent"></ListView> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <data> <variable name="food" type="org.lenve.databinding3.Food"/> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="96dp" android:orientation="vertical"> <ImageView android:id="@+id/iv" android:layout_width="96dp" android:layout_height="96dp" android:padding="6dp" app:img="@{food.img}"/> <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:text="@{food.description}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_toRightOf="@id/iv" android:layout_alignParentBottom="true" android:layout_marginBottom="2dp" android:text="@{food.keywords}" android:textStyle="bold"/> </RelativeLayout> </layout>
/** * Created by 王鬆 on 2016/7/31. */ public class Food { private String description; private String img; private String keywords; private String summary; public Food() { } public Food(String description, String img, String keywords, String summary) { this.description = description; this.img = img; this.keywords = keywords; this.summary = summary; } @BindingAdapter("bind:img") public static void loadInternetImage(ImageView iv, String img) { Picasso.with(iv.getContext()).load(img).into(iv); } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getImg() { return img; } public void setImg(String img) { this.img = img; } public String getKeywords() { return keywords; } public void setKeywords(String keywords) { this.keywords = keywords; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } }
/** * Created by 王鬆 on 2016/7/31. */ public class MyBaseAdapter<T> extends BaseAdapter { private Context context; private LayoutInflater inflater; private int layoutId; private int variableId; private List<T> list; public MyBaseAdapter(Context context, int layoutId, List<T> list, int resId) { this.context = context; this.layoutId = layoutId; this.list = list; this.variableId = resId; inflater = LayoutInflater.from(context); } @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 = DataBindingUtil.getBinding(convertView); } dataBinding.setVariable(variableId, list.get(position)); return dataBinding.getRoot(); } }
public class MainActivity extends AppCompatActivity { private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { MyBaseAdapter<Food> adapter = new MyBaseAdapter<>(MainActivity.this, R.layout.listview_item, foods, org.lenve.databinding3.BR.food); lv.setAdapter(adapter); } }; private List<Food> foods; private ListView lv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = ((ListView) findViewById(R.id.lv)); initData(); } private void initData() { OkHttpClient client = new OkHttpClient.Builder().build(); Request request = new Request.Builder().url("http://www.tngou.net/api/food/list?id=1").build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()) { parseJson(response.body().string()); } } }); } private void parseJson(String jsonStr) { foods = new ArrayList<>(); try { JSONObject jo = new JSONObject(jsonStr); JSONArray tngou = jo.getJSONArray("tngou"); for (int i = 0; i < tngou.length(); i++) { JSONObject item = tngou.getJSONObject(i); String description = item.getString("description"); String img = "http://tnfs.tngou.net/image"+item.getString("img"); String keywords = "【關鍵詞】 "+item.getString("keywords"); String summary = item.getString("summary"); foods.add(new Food(description, img, keywords, summary)); } mHandler.sendEmptyMessage(0); } catch (JSONException e) { e.printStackTrace(); } } }
至此,咱們使用DataBinding的方式來給ListView加載數據就算完成了。so easy~~~
4.點擊事件處理若是你使用DataBinding,咱們的點擊事件也會有新的處理方式,首先以ListView爲例來講說如何綁定點擊事件,在listview_item佈局文件中每個item的根節點添加以下代碼:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > .... .... <RelativeLayout android:layout_width="match_parent" android:layout_height="96dp" android:onClick="@{food.onItemClick}" android:orientation="vertical"> <ImageView android:id="@+id/iv" android:layout_width="96dp" android:layout_height="96dp" android:padding="6dp" app:img="@{food.img}"/> .... .... .... </RelativeLayout> </layout>
public void onItemClick(View view) { Toast.makeText(view.getContext(), getDescription(), Toast.LENGTH_SHORT).show(); }
5. 數據更新處理
單純的更新Food對象並不能改變ListView的UI顯示效果,那該怎麼作呢?Google給咱們提供了三種解決方案,分別以下:
讓實體類繼承自BaseObservable,而後給須要改變的字段的get方法添加上@Bindable註解,而後給須要改變的字段的set方法加上notifyPropertyChanged(org.lenve.databinding3.BR.description);一句便可,好比我想點擊item的時候把description字段的數據所有改成111,我能夠修改Food類變爲下面的樣子:
public class Food extends BaseObservable { private String description; private String img; private String keywords; private String summary; public Food() { } public Food(String description, String img, String keywords, String summary) { this.description = description; this.img = img; this.keywords = keywords; this.summary = summary; } @BindingAdapter("bind:img") public static void loadInternetImage(ImageView iv, String img) { Picasso.with(iv.getContext()).load(img).into(iv); } public void onItemClick(View view) { // Toast.makeText(view.getContext(), getDescription(), Toast.LENGTH_SHORT).show(); setDescription("111"); } public void clickKeywords(View view) { Toast.makeText(view.getContext(), getKeywords(), Toast.LENGTH_SHORT).show(); } @Bindable public String getDescription() { return description; } public void setDescription(String description) { this.description = description; notifyPropertyChanged(org.lenve.databinding3.BR.description); } public String getImg() { return img; } public void setImg(String img) { this.img = img; } public String getKeywords() { return keywords; } public void setKeywords(String keywords) { this.keywords = keywords; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } }
OK,這是第一種解決方案,也是比較簡單經常使用的一種。
這種方式使用起來略微麻煩,除了繼承BaseObservable以外,建立屬性的方式也變成下面這種:
private final ObservableField<String> description = new ObservableField<>();
description.get()
this.description.set(description);
/** * Created by 王鬆 on 2016/7/31. */ public class Food extends BaseObservable { private final ObservableField<String> description = new ObservableField<>(); private final ObservableField<String> img = new ObservableField<>(); private final ObservableField<String> keywords = new ObservableField<>(); private final ObservableField<String> summary = new ObservableField<>(); public Food() { } public Food(String description, String img, String keywords, String summary) { this.description.set(description); this.keywords.set(keywords); this.img.set(img); this.summary.set(summary); } @BindingAdapter("bind:img") public static void loadInternetImage(ImageView iv, String img) { Picasso.with(iv.getContext()).load(img).into(iv); } public void onItemClick(View view) { // Toast.makeText(view.getContext(), getDescription(), Toast.LENGTH_SHORT).show(); setDescription("111"); } public void clickKeywords(View view) { Toast.makeText(view.getContext(), getKeywords(), Toast.LENGTH_SHORT).show(); } @Bindable public String getDescription() { return description.get(); } public void setDescription(String description) { this.description.set(description); notifyPropertyChanged(org.lenve.databinding3.BR.description); } public String getImg() { return img.get(); } public void setImg(String img) { this.img.set(img); } public String getKeywords() { return keywords.get(); } public void setKeywords(String keywords) { this.keywords.set(keywords); } public String getSummary() { return summary.get(); } public void setSummary(String summary) { this.summary.set(summary); } }
DataBinding中給咱們提供了一些現成的集合,用來存儲數據,好比ObservableArrayList,ObservableArrayMap,由於這些用的少,我這裏就不作介紹了。
以上。