大概有一个月的时间,我都在反思移动端应用的架构,尝试过mvc,这大概是大部分人刚开始入门的时候使用的模式了。然而细思一下,在android里面这种架构恐怕行不通,比如,我们的activity,要获得系统服务的话都要通过getService(*)之类的代码,这无意中就让activity的责任乱了起来,按理说activity应该就是一个view,它不应该拥有类似controller的职责。
于是,我开始尝试Mvp。它的确是个好东西,一般我们都是通过dagger2,rxjava,etc等来实现mvp,但是如果,数据改变了,我们的presenter就要提醒view视图改变了,然而,这似乎又增加了控制层和视图层的耦合度。所以,在google 的 I/O大会上,google提出了data binding (可用于实现mvvm,其中ViewModel可以理解成是View的数据模型和Presenter的合体,ViewModel和View之间的交互通过Data Binding完成,而Data Binding可以实现双向的交互,这就使得视图和控制层之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力), 三者之间的差别:

我摘一个来自知乎的图片来解释android 架构之间的区别:

OK!

在这里,我假设所有的读者都是使用的android studio,如果要使用data binding的话在build.gradle里面添加:

android {    compileSdkVersion 23    buildToolsVersion "23.0.2"    dataBinding {        enabled = true    }    defaultConfig {        applicationId "com.chan.databindingdemo"        minSdkVersion 14        targetSdkVersion 23        versionCode 1        versionName "1.0"    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }}

dataBinding {
enable = true
}
便可以了

在官网上,google介绍了data binding如何结合ListView 和 recycler view,这里我们只讲解recycler view
我们假设,我们要做一个标签应用,这个应用非常简单,就是一个recycler view,它其中的项只有各种label,于是我们自定义一个类:

public class DemoItem {    private String m_label;    public DemoItem(String label) {        m_label = label;    }    public String getLabel() {        return m_label;    }    public void setLabel(String label) {        m_label = label;    }}

代码非常简单,就一个label field,和一些getter & setter
对于recycler中的子项,我们显然是要自定义布局的,比如:

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable  name="data" type="com.chan.databindingdemo.DemoItem"/>    </data>    <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">        <TextView  android:id="@+id/label" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{data.label}"/>    </LinearLayout></layout>

我们可以看到,这个layout的xml文件根节点是layout 并且,还定义了名为data类型为com.chan.databindingdemo.DemoItem的数据。它有一个data节点包裹,然后以variable的属性给出
往下阅读便是recycler view的内容视图了,很简单 就一个TextView,我们可以看到对于TextView的android:text属性,我们给出了它的值@{data.label}。也就是说这个text view的内容由data得label属性给出。非常简单吧,这时候,编译器便给我们生成了一个ViewDataBinding类,这个类所在的包名是以我们工程包+binding决定的,比如我现在的应用包名是com.chan.databindingdemo,那么这个编译器生成的代码便在com.chan.databindingdemo.databinding下,对于这个类的名字呢是以上文的Layout文件名决定的,比如:我的layout文件名是item_layout.xml,那么生成的类就为ItemLayoutBinding。

我假设读者在读本文的时候,已经通晓recycler view的使用方法,因为本文并不对基本的内容做讲解

好的,然后我们开始定义view holder了
demo:

/** * Created by chan on 16/3/12. */public class RecyclerHolder extends RecyclerView.ViewHolder {    ItemLayoutBinding m_itemLayoutBinding;    public RecyclerHolder(View itemView, ViewDataBinding viewDataBinding) {        super(itemView);        m_itemLayoutBinding = (ItemLayoutBinding) viewDataBinding;    }    public void bind(DemoItem demoItem) {        m_itemLayoutBinding.setData(demoItem);    }}

我们的RecyclerHolder的构造函数多了一个view data binding,并且这个类型的真实类型为ItemLayouBinding,就是刚刚编译器自动生成的那个类。一个ViewModel要和view进行绑定,需要调用binding的set方法,具体的set方法名根据你在上文的layout文件的variable的name属性决定,所以我们这里是m_itemLayoutBinding.setData(demoItem);

好了现在我们开始实现adapter

/** * Created by chan on 16/3/12. */public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerHolder> {    private LayoutInflater m_layoutInflater;    private List<DemoItem> m_demoItems;    public RecyclerAdapter(Context context, List<DemoItem> demoItems) {        m_layoutInflater = LayoutInflater.from(context);        m_demoItems = demoItems;    }    @Override    public RecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {        ViewDataBinding viewDataBinding = DataBindingUtil.inflate(m_layoutInflater, R.layout.item_layout, parent, false);        return new RecyclerHolder(viewDataBinding.getRoot(), viewDataBinding);    }    @Override    public void onBindViewHolder(RecyclerHolder holder, int position) {        final int currentPosition = holder.getLayoutPosition();        holder.bind(m_demoItems.get(currentPosition));    }    @Override    public int getItemCount() {        return m_demoItems.size();    }}

值得注意的是,如果我们要inflate视图,必须要通过DataBindingUtil来实现!!否则会抛出一个非捕获异常!!
也就是这里的:

   ViewDataBinding viewDataBinding = DataBindingUtil.inflate(m_layoutInflater, R.layout.item_layout, parent, false);

一个简单的demo出现了 ,不过我们刚刚讲过,如果数据改变了,view能自动刷新那就好了,哎呦,我们可以引入观察者模式哦(最近这个比较火!!!)

还记得刚刚的model吗?我们只需要一个简单的修改就可以完成了

/** * Created by chan on 16/3/12. */public class DemoItem extends BaseObservable {    private String m_label;    public DemoItem(String label) {        m_label = label;    }    @Bindable    public String getLabel() {        return m_label;    }    public void setLabel(String label) {        m_label = label;        notifyPropertyChanged(com.chan.databindingdemo.BR.label);    }}

如果一个应用要实现可观察,只要实现 Observable这个借口就好了,原文:A class implementing the Observable interface will allow the binding to attach a single listener to a bound object to listen for changes of all properties on that object.
不过库给我们实现了一个简单的对象BaseObservable,它可以满足我们的简单需求。
我们在getter上加了一个bindable注解,并且在setter里面,我们手动提醒了变化

有一些谜团,比如 com.chan.databindingdemo.BR.label是啥,我引入一下原文:
The Bindable annotation generates an entry in the BR class file during compilation. The BR class file will be generated in the module package. If the base class for data classes cannot be changed, the Observable interface may be implemented using the convenient PropertyChangeRegistry to store and notify listeners efficiently.

在编译时期,bindable注解会在BR里面生成一个项,这个项就是针对上文的getter。并且这个br类生成在项目的包下。

我们看下完整的activity代码:

public class MainActivity extends AppCompatActivity {    private RecyclerView m_recyclerView;    private List<DemoItem> m_demoItems = new ArrayList<>();    private int m_index = 0;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(toolbar);        m_recyclerView = (RecyclerView) findViewById(R.id.id_recycler);        m_recyclerView.setLayoutManager(new LinearLayoutManager(this));        for(int i = 0; i < 10; ++i) {            m_demoItems.add(new DemoItem("标签" + i));        }        RecyclerAdapter adapter = new RecyclerAdapter(this, m_demoItems);        m_recyclerView.setAdapter(adapter);        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);        fab.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                m_demoItems.get(m_index).setLabel(System.currentTimeMillis() + "");                m_index = (m_index + 1) % m_demoItems.size();            }        });    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }}

效果图:

然而 csdn为啥不让我上传代码?

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. Android:初窥手势识别
  3. Android(安卓)编译基础知识
  4. android 自动换行FlowLayout
  5. Android之——史上最简单图片轮播广告效果实现
  6. Android获取屏幕分辨率及DisplayMetrics简介 和java获得屏幕宽度
  7. Android开发实践:JNI层线程回调Java函数示例
  8. 一种Android客户端架构设计分享
  9. Winow&WindowManager

随机推荐

  1. unity AssetBundle 加载资源 笔记
  2. Android的内存泄漏和调试
  3. Android 读取excel (支持 xls和xlsx)
  4. Android 仿IOS搜索框
  5. Android 休眠与唤醒
  6. android 工具类 数据库管理
  7. Android中字体颜色大全-146种(完整版)
  8. android按键模拟测试
  9. android 里面的测量单位
  10. android四种不同的事件实现