todo-databinding 是基于todo-mvp ,但是使用了Data Binding 去展示数据并且绑定操作,这不是一个严格的Model-View-ViewModel,它同时使用了View Models 和 Presenter.
Data Binding 库可以节省很多样板代码,它允许UI元素和data model中的属性绑定起来
- 布局文件用于将数据元素(如姓名name,年龄age)绑定到UI元素(TextView,EditText等)
- 事件也用action handler绑定起来
- 数据可以被观察在需要的时候自动更新数据
Databinding 简介
Databinding是一个实现数据和UI绑定的库,可以省掉很多我们以前经常写的末班方法,比如findViewById,setText,setVisibility等方法,直接在layout文件中将具体的值与控件绑定比如
将任务名与显示的textView绑定
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:text="@{task.titleForList}" />
如果还不了解databinding的参考Data Binding Library,这里不多说啦
Activity
以任务列表页为例,这个架构中Activity充当各个组件的创建容器,在TasksActivity
的onCreate
中创建TasksFragment
,TasksPresenter
和TasksViewModel
protected void onCreate(Bundle savedInstanceState) {
...
...
TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// 创建fragment,即MVP中的View角色
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
// 创建presenter,即mvp中的p层,同时创建数据仓库(model),presenter同时持有view和model
mTasksPresenter = new TasksPresenter(Injection.provideTasksRepository(
getApplicationContext()), tasksFragment);
//创建ViewModel
TasksViewModel tasksViewModel = new TasksViewModel(getApplicationContext(), mTasksPresenter);
tasksFragment.setViewModel(tasksViewModel);
}
Fragment
实现TasksContract.View接口,mvp中的view层,根据计算结果展示界面,将TasksViewModel
通过Databinding设置给layout,Fragment同时也持有presenter,通过presenter去得到用户操作的结果
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TasksFragBinding tasksFragBinding = TasksFragBinding.inflate(inflater, container, false);
tasksFragBinding.setTasks(mTasksViewModel); 将mTasksViewModel绑定到布局
tasksFragBinding.setActionHandler(mPresenter); 将presenter绑定到布局,这样可以直接在布局中将presenter中的方法与点击事件绑定
...
...
}
布局中将presenter中的方法与点击事件绑定
<TextView
android:id="@+id/noTasksAdd"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center"
android:background="@drawable/touch_feedback"
android:gravity="center"
android:text="@string/no_tasks_add"
android:onClick="@{() -> actionHandler.addNewTask()}"
android:visibility="@{tasks.tasksAddViewVisible ? View.VISIBLE : View.GONE}" />
TasksPresenter.java
@Override
public void addNewTask() {
mTasksView.showAddTask();
}
ViewModel
相当于布局要绑定的数据的一个映射类,即要展示的数据全部由ViewModel来提供,将ViewModel设置给layout
tasksFragBinding.setTasks(mTasksViewModel);
layout接收TasksViewModel并命名为tasks
tasks_frag.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable
name="tasks"
type="com.example.android.architecture.blueprints.todoapp.tasks.TasksViewModel" />
<variable
name="actionHandler"
type="com.example.android.architecture.blueprints.todoapp.tasks.TasksContract.Presenter" />
</data>
根据TasksViewModel中的数据设置界面
tasks_frag.xml
<LinearLayout
android:id="@+id/tasksLL"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="@{tasks.notEmpty ? View.VISIBLE : View.GONE}">
<TextView
android:id="@+id/filteringLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:gravity="center_vertical"
android:layout_marginLeft="@dimen/list_item_padding"
android:layout_marginRight="@dimen/list_item_padding"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:text="@{tasks.currentFilteringLabel}" />
<ListView
android:id="@+id/tasks_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
这部分意思是如果tasks不是空的就显示列表,notempty的值是从哪里来的呢
TasksViewModel.java
@Bindable
public boolean isNotEmpty() {
return mTaskListSize > 0;
}
@Bindable是DataBinding的注解,可以生成一个notEmpty的属性,这个demo中的属性都是这样生成的
Presenter
mvp中的p层,负责解析view的请求并计算,最后将计算结果返回给View,刚才绑定生命Tasks的地方同时也声明了actionHandler,他是一个presenter的对象
<variable
name="actionHandler"
type="com.example.android.architecture.blueprints.todoapp.tasks.TasksContract.Presenter" />
当view接收到请求的时候,actionHandler触发相应的计算方法,比如点击了添加按钮
TextView
android:id="@+id/noTasksAdd"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center"
android:background="@drawable/touch_feedback"
android:gravity="center"
android:text="@string/no_tasks_add"
android:onClick="@{() -> actionHandler.addNewTask()}"
android:visibility="@{tasks.tasksAddViewVisible ? View.VISIBLE : View.GONE}" />
因为onclick与actionHandler的addnewTask()方法绑定,点击的时候触发方法
TasksPresenter.java
public void addNewTask() {
mTasksView.showAddTask();
}
这里计算结果就是现实添加界面,所以通知View去打开添加界面
TasksFragment.java
public void showAddTask() {
Intent intent = new Intent(getContext(), AddEditTaskActivity.class);
startActivityForResult(intent, AddEditTaskActivity.REQUEST_ADD_TASK);
}
这个架构的大体执行流程就是这样啦