- android MVVM框架学习总结
- 1.简单的数据绑定示例
- 2.事件处理
- 3. Layout细节
android MVVM框架学习总结
1.简单的数据绑定示例
(1)布局文件这样写
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>
在标签中 描述了一个变量user,它对应的类型为”com.example.User”
布局中使用“@ { }”语法来书写表达式。如为TextView的文本设置成user. firstName.
(2) 数据对象这样写
public class User {
public final String firstName;
public final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
在数据绑定中,访问@{user.firstName},那么默认就会访问同名的属性firstName,或对应的getFirstName方法。
(3)数据绑定
默认情况下,绑定类将基于布局文件的名称来产生
如布局文件为main_activity.xml,这样生成的类是 MainActivityBinding
如下设置Activity的contentView:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
User user = new User("Test", "User");
binding.setUser(user);
}
上文说,在xml中用表达式可以获取数据,那么同样的,在代码中也可以设置数据,如这里的:binding.setUser(user);
还可以使用inflate的方式来获取生成数据绑定类:
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
如果是在一个adapter中来使用,那么可以如下:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
2.事件处理
可以在xml中编写表达式处理事件。比如 View.OnLongClickListener有一个方法为onLongClick(),那么它对应的事件就是android:onLongClick。当绑定事件后,会自动将该listener绑定在View上。处理事件有两种方法,
1. 方法引用:可以引用符合listener 任意方法签名规则的方法。
2. 监听器绑定:使用lambda表达式。
1.方法引用
比如定义一个符合OnClickListener#onClick(View view)方法参数签名规则的方法:
public class MyHandlers {
public void onClickFriend(View view) { ... }
}
如下在xml中绑定并使用:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.Handlers"/>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"
android:onClick="@{handlers::onClickFriend}"/>
</LinearLayout>
</layout>
2.监听器绑定
表达式使用的方法就可以定义的比较随意了,不用像”方法引用”那样遵循特定规则,如一个方法
public class Presenter {
public void onSaveClick(Task task){}
}
xml中使用它
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="task" type="com.android.example.Task" />
<variable name="presenter" type="com.android.example.Presenter" />
</data>
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:onClick="@{() -> presenter.onSaveClick(task)}" />
</LinearLayout>
</layout>
像上面,我们并没有定义onClick(android.view.View) 参数view。监听器绑定,允许我们忽略所有的参数。如果想要使用参数,可以忽略参数类型,随意定义一个名字。监听器绑定还有很多灵活的写法,可以参考官方文档。
3. Layout细节
(1) import
1) 在标签中,import 一些class,就像在java 代码中一样
如: <import type="android.view.View"/>
2) 当类名过长或者有类名称冲突,其中一个类可以重命名一个别名
<import type="com.grandstream.gsmarket.R.string" alias="Strings"/>
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="EsView"/>
3) 也可以在表达式中运用静态属性或方法,使用MyStringUtils.capitalize():
<data>
<import type="com.example.MyStringUtils"/>
<variable name="user" type="com.example.User"/>
</data>
…
<TextView
android:text="@{MyStringUtils.capitalize(user.lastName)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
示例代码
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
<variable
name="viewmodel"
type="com.grandstream.gsmarket.appdetail.AppDetailItemViewModel" />
<import type="android.view.View"/>
<import type="com.grandstream.gsmarket.R.string" alias="Strings"/>
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.AppCompatTextView
android:id="@+id/download_count_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:singleLine="true"
android:textColor="#999999"
android:textSize="36px"
android:text='@{viewmodel.getContext().getString(Strings.download) + viewmodel.downloadCount}'/>
</LinearLayout>
(2)可以定义多个标签
这些variable变量在编译时会进行类型检查,如果一个variable实现了Observable接口或是一个Observable的collection,那就需要在type中声明;如果不是一个Observable的实现,那么该variable就不会被观察到.
<data>
<import type="android.view.View" />
<variable
name="view"
type="com.grandstream.gsmarket.apps.ApkListFragment" />
<variable
name="viewmodel"
type="com.grandstream.gsmarket.apps.AppsViewModel" />
</data>
(3)自定义绑定 class names
默认情况下,基于布局文件的名称而生成绑定类,开头大写,然后移除下划线,再加上后缀”Binding”。这个类将被放置在一个数据绑定包中,该包位于moudle包下。例如, 布局文件contact_item.xml将生成ContactItemBinding。如果moudle的包名为com.example.my.app, 那么它将被放置在com.example.my.app.databinding下,通过调整data元素的class属性,绑定类可以重命名或放置在不同的包。
<data class="ContactItem">
...
</data>
这会生成在moudle下的databinding包下,如果要生成moudle包下,可以使用”.”前缀:
<data class=".ContactItem">
...
</data>
在下面这种情况下,ContactItem直接生成在模块包下。可以使用任何完整的包路径:
<data class="com.example.ContactItem">
...
</data>
(4)include
variables可以传递给使用了include标签的子布局中,如
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</LinearLayout>
</layout>
如上,在name.xml和contract.xml中都可以使用变量user了
不支持在merge下直接使用include的情形。
(5)表达式语言
1)共同的特征
表达式语言看起来很像Java表达式。如下这些都是相同的:
Mathematical + - / * %
String concatenation +
Logical && ||
Binary & | ^
Unary + - ! ~
Shift >> >>> <<
Comparison == > < >= <=
instanceof
Grouping ()
Literals - character, String, numeric, null
Cast
Method calls
Field access
Array access []
Ternary operator ?:
例:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
2)不支持的操作符
this、new、super 如这些,还是只能在java类中使用
3)合并null操作符
如下使用 “??” 操作符
android:text="@{user.displayName ?? user.lastName}"
等价于
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
4)属性引用
如下,user有getLastName(),可以简单引用:
android:text="@{user.lastName}"
5)NullPointerException处理
数据绑定代码会自动检查null值,避免NullPointerException。例如String类型默认为null, Int类型默认0.
6)集合
一般的集合都可以使用”[]”操作符: arrays, lists, sparse lists, and maps。例
<data>
<import type="android.util.SparseArray"/>
<import type="java.util.Map"/>
<import type="java.util.List"/>
<variable name="list" type="List<String>"/>
<variable name="sparse" type="SparseArray<String>"/>
<variable name="map" type="Map<String, String>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"
7)字符串引用
如下,可以使用单引号在属性值上,用双引号在字符串字面值上:
android:text='@{map["firstName"]}'
也可以反过来
android:text="@{map[`firstName`}"
android:text="@{map['firstName']}"
8)资源
可以使用正常的访问资源的表达式语法:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
可以为格式字符串和复数提供参数:
android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"
一些资源需要显示的引用
Type | Normal Reference | Expression Reference |
String[] | @array | @stringArray |
int[] | @array | @intArray |
TypedArray | @array | @typedArray |
Animator | @animator | @animator |
StateListAnimator | @animator | @stateListAnimator |
color int | @color | @color |
ColorStateList | @color | @colorStateList |