C# 很早就有了MVVM的开发模式,Android手机中的MVVM一直到去年Google的I\O大会上才推出,姗姗来迟。MVVM这中开发模式的优势自没必要多说,能够实现视图和逻辑代码的解耦,并且,按照Google的说法,使用了MVVM的开发模式,还能够提升布局文件的解析速度,我的以为这一点很是重要。咱们在安卓开发中常常须要写不少个findViewById,让人心烦,不少人不想写这个因而用了一些注解框架,但是注解框架不管性能多好,效率老是要低于findViewById的,所以,Android中的MVVM也即databinding能够帮助咱们完全解决这个问题。OK,废话很少说,咱们来看看具体要怎么在Android开发中使用MVVM。java
在低版本的AndroidStudio中使用DataBinding稍微有点麻烦,这里不作介绍。我这里以AndroidStuido2.1为例来介绍DataBinding。本文主要包含如下几方面内容:android
1.基本使用json
2.绑定ImageView
api
3.绑定ListView网络
4.点击事件处理app
5.数据更新处理框架
好了,那就开始吧!
ide
建立好一个Android Project以后,在gradle文件中添加以下几行代码,表示开启databinding:布局
android { ... ... ... dataBinding{ enabled true } }
要使用数据绑定,咱们得首先建立一个实体类,好比User实体类,以下:性能
/** * 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对于基本的四则运算、逻辑与、逻辑或、取反位移等都是支持的,我这里再也不举例。
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", "张三")); }
好了,看完了简单使用以后,不知道你有没有喜欢上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~~~
若是你使用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(); }
单纯的更新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,由于这些用的少,我这里就不作介绍了。
本文共涉及到三个Demo,因为CSDN对上传文件大小的限制,我分三次上传,下载地址以下:
1.http://download.csdn.net/detail/u012702547/9591142
2.http://download.csdn.net/detail/u012702547/9591150
3.http://download.csdn.net/detail/u012702547/9591160
以上。