本系列目录

为什么要使用自定义Interface

  • 我们平常在Android的开发中,比如如果要设置一个View的点击事件,通常通过view.setOnClickListener()来实现的,这种方式略显繁琐,而且要通过findViewById()来获取到此View的引用。使用了Data Binding技术以后,我们无需这样做,可以直接通过在xml布局文件中设置一个Interface的实现来直接调用某个方法,非常方便。

  • 除了上面说的方便之外,当两个Fragment之间需要通信时,Android是强烈不建议两个Fragment之间直接通信的,它们之间的通信只能通过他们所在的Activity来进行中转。使用Data Binding之后,这种情况处理起来就简单了很多,通过将一个Interface的实现设置到两个Fragment的xml布局文件中就可以实现。

下面我们来举例说明这种方式的用法。

使用自定义接口

  1. 首先定义一个接口

    1
    2
    3
    public interface IMainActivity {
    void clickedSomething();
    }
  2. 实现这个接口

    1
    2
    3
    4
    5
    6
    public class MainActivity extends AppCompatActivity implements IMainActivity{
    @Override
    public void clickedSomething() {

    }
    }
  3. 在布局文件的标签中定义该接口的变量

    1
    2
    3
    <variable
    name="iMainActivity"
    type="cn.examplecode.androiddatabinding.IMainActivity"/>
  4. 通过binding设置这个变量

    1
    mBinding.setIMainActivity(this);

    如果发现并没有这个方法,可能因为IDE并没有自动生成这个方法,可以参考这篇文章进行解决。

  5. 布局中调用接口的方法

    1
    2
    3
    4
    5
    6
    <TextView
    android:id="@+id/tv_example"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{()->iMainActivity.clickedSomething()}"
    android:textSize="16sp" />

    注意:android:onClick()中的写法是一种lambda式的写法

总结

通过简单的几步就可以在布局中直接调用Activity(或任意对象)中的方法了,本文以简单的点击事件及简单的事件进行用法的举例,大家可以根据自己的业务应用到更多的场景中。

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

本系列目录

什么是BindingAdapter

BindingAdapter用来设置布局中View的自定义属性,当使用该属性时,可以自定义其行为。

下面是一个简单的例子:

1
2
3
4
@BindingAdapter("android:bufferType")
public static void setBufferType(TextView view, TextView.BufferType bufferType) {
view.setText(view.getText(), bufferType);
}

当一个方法加上@BindingAdapter注解后,就定义了一个BindingAdapter,注意方法的第一个参数是需要绑定到的View,第二个参数是绑定的属性值。

当定义完成后,此时我们就可以在布局的View中使用该属性,举例如下:

1
2
3
4
5
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:bufferType="normal"
/>

当TextView中加入了android:bufferType=”normal”后,setBufferType()方法就会被调用。

当自定义其它一些属性时,也遵循一样的规则。

自定义图片加载的BindingAdapter

由于BindingAdapter的特性,我们就可以为ImageView自定义一个BindingAdapter,从而大幅简化图片加载的过程。

第一步,我们先新建一个ImageBindingAdapter的类,图片相关的BindingAdapter可以都定义在这个类里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ImageBindingAdapter {

@BindingAdapter("imageUrl")
public static void bindImageUrl(ImageView view, String imageUrl){
RequestOptions options =
new RequestOptions()
.centerCrop()
.dontAnimate();

Glide.with(view)
.load(imageUrl)
.apply(options)
.into(view);
}

}

定义好后,我们就可以直接在布局中使用这个属性了:

1
2
3
4
5
<ImageView
android:layout_width="180dp"
android:layout_height="180dp"
app:imageUrl="@{user.photo}"
/>

仅仅简单的一行代码,就可以进行网络图片的加载了,是不是感觉这个世界简单了很多?

除了这种单个参数的BindingAdapter,它也支持多个参数,这也是BindingAdapter强大的地方。

总结

使用BindingAdapter可以大大简化一些重复代码,本文主要介绍了加载图片上的使用,你可以举一反三,用在更多的场景中使用,比如加载列表的数据等,这样做以后也可以使您的代码更加清晰高效。

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

本系列目录

前面我们讲到了Data Binding在Activity中的使用在Fragment中的使用,除此之外,Data Binding也可以使用在RecyclerView/AdapterView的Adapter中,本文以RecyclerView为例。

获取Binding对象

按照通常的做法,我们在Adapter中会定义一个ViewHolder,在此ViewHolder中取得一些布局View的引用。

使用Data Binding后同样可以简化Adapter中的操作。

比如我们新建布局文件item_binding.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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>
</data>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
>

<TextView
android:id="@+id/tv_example"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp" />

</LinearLayout>
</layout>

新建后我们会发现IDE会为我们生成一个绑定类ItemBinding.java,此时我们就可以把它定义在ViewHolder中:

如果没有生成,依然参考 这篇文章

1
2
3
4
5
6
7
8
class ItemsHolder extends RecyclerView.ViewHolder {
ItemBinding binding;

ItemsHolder(View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
}

覆写父类方法onCreateViewHolder(),创建ViewHolder

定义好上面的ViewHolder后,我们就覆写RecyclerView.Adapter的onCreateViewHolder()方法来创建一个ViewHolder:

1
2
3
4
5
6
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new ItemsHolder(v);
}

在onBindViewHolder()方法中使用ItemBinding

上面我们创建好ViewHolder后,我们就可以在这个方法中使用它了,用法就跟之前我们在Activity中和Fragment中一样了。

1
2
3
4
5
6
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ItemsHolder vh = (ItemsHolder)holder;
Item item = getItem(position);
vh.binding.tvExample.setText(item.getName());
}

总结

下一篇我们将会介绍BindingAdapter的使用,它应用的场景也很多,我们将以ImageView来举例,大大简化我们开发中加载网络/本地图片的重复代码(仅需一行代码)。

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

本系列目录

前面我们讲到了Data Binding在Activity中的使用在Fragment中的使用,在两者的布局文件中,都可以绑定数据(对象),本篇文章我们就来介绍数据的绑定。

<data>标签

在这个标签中,我们通常用来做下面的事情:

  • 定义所绑定的数据的名称(变量名)及对应类型
  • 引入页面所需的类

示例如下:

1
2
3
4
5
6
7
8
<data>
<import type="android.view.View" />
<import type="android.text.TextUtils" />

<variable name="visible" type="boolean"/>
<variable name="title" type="String"/>
<variable name="user" type="cn.examplecode.androiddatabinding.User"/>
</data>

其中”<import/>”标签表示引入一个类,比如上例中引入了View类和一个工具类TextUtils,当然也可以引入你自己的类,比如常量类或者工具类。

下面”<variable/>”标签定义了本页面所需要的各种数据名称或类型,其类型可以是java中的基础类型,或者自定义的类。

设置数据

上面定义了页面中所需要的数据后,下面就需要通过获取到的Binding对象设置这些数据:

1
2
3
4
5
mBinding.setVisible(true);
mBinding.setTitle("用户信息");
User user = new User();
user.setName("Steve Jobs");
mBinding.setUser(user);

这里的setXXX()方法也是IDE自动根据<data>标签中的定义自动生成的。

如果写代码过程中发现IDE并没有自动正确生成对应的setXXX()方法,则参考这篇文章:Android Data Binding没有正常生成相关类/方法的解决办法,仅需几步操作即可使IDE正常生成。

在布局中使用这些数据

数据设置完毕以后就可以在页面中使用这些数据了,使用起来也非常方便,比起在java代码中操作,可以省去不少代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{title}"
android:visibility="@{visible ? View.VISIBLE : View.GONE}"
/>

<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
/>

请注意上面的代码:

1
android:text="@{title}"

这一行是设置变量”title”的值到TextView中。

1
android:visibility="@{visible ? View.VISIBLE : View.GONE}"

这一行是根据变量”visible”的值来控制该TextView的显示与否,注意这里需要在<data>标签中引入android.view.View类,来使用View中的VISIBLE和GONE常量,这里可以使用问号表达式来判断。

1
android:text="@{user.name}"

user是一个自定义对象,user.name意思是取user对象中的name的值。

User定义如下,注意必须设置getter/setter:

1
2
3
4
5
6
7
8
9
10
11
12
public class User {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

根据View获取Binding

经过前面的学习,我们可以很方便地从数据绑定对象(Binding) 中获取到其绑定的View,但是我们会碰到一些应用场景,比如我们在操作View的时候,需要取得其绑定的Binding对象,以获取到当前View中的子View的引用,从而避免类似findViewById()这样的操作,并且也可以获取到当前View所绑定的数据。

那么该如何从View获取当前绑定的Binding对象呢?

其实很简单,DataBindingUtil就提供了这样的方法,举例如下:

1
UserBinding userBinding = DataBindingUtil.getBinding(userView);

##总结
通过本篇文章相信你已经了解如何将数据与View进行绑定,这是Data Binding的核心,也是MVVM架构模式的基础,这里介绍的是比较简单的绑定,后面我们将介绍更加深入的使用,使用这些技术后就会发现Data Binding的强大和高效率。

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

本系列目录

修改fragment的布局

上一篇:在Activity中的使用中一样,在Fragment中使用Data Binding同样需要修改布局,修改方式也跟Activity一样,在最外层加上<layout>标签:

1
2
3
4
5
6
7
8
9
10
11
<?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>

</data>

<页面布局.../>

</layout>

同样,为避免重复讲解, <data>中的数据绑定我们会在这一篇文章中讲到。

在Fragment中进行绑定

在Activity中绑定中创建绑定的方式有些不同,但是目的都是获得绑定对象的引用。

比如我们Fragment的布局文件为:frag_main.xml,具体的方式如下:

  1. 定义成员变量
1
private FragMainBinding mBinding;
  1. 在onCreateView()中初始化mBinding,并返回View
1
2
3
4
5
6
7
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mBinding = FragMainBinding.inflate(inflater);
mBinding.tvExample.setText("Binding Text");
return mBinding.getRoot();
}

此时就可以正常操作Binding对象了。

总结

Activity中获取Data Binding对象类似,只是方法稍微不同。

除了在Activity和Fragment中使用Data Binding之外,另一个常用的场景是在列表的Adapter中使用Data Binding,这一篇我们将讲到。

下一篇我们将先讲解一下布局中<data>标签的作用,即如何将数据绑定到布局文件中。

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

本系列目录

修改activity布局

如果使Activity支持Data Binding,在布局的最外层加入”<layout>”标签即可,由于是加在最外层,所以即使重构现有工程,所做的修改也非常简单,并不会影响现有的布局结构。

以下以MainActivity进行举例。

修改前activty_main.xml的布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
>

<TextView
android:id="@+id/tv_example"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
/>

</LinearLayout>

修改后activty_main.xml的布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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>

</data>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
>

<TextView
android:id="@+id/tv_example"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp" />

</LinearLayout>
</layout>

这里的<data>标签中的元素放的是页面所需的数据,后面的文章我们会讲到,也可以直接点击这里查看,这里我们暂且放一下,重点讲Activity中Data Binding的使用。

在Activity中进行绑定

上面我们修改了布局后,下面我们就可以在代码中进行数据绑定了。此时工程中会自动生成该布局对应的java绑定文件:ActivityMainBinding。仔细观察就会发现,这个文件名就是将布局的下划线形式转换成java规范的驼峰形式,后面加上Binding。

下面进入MainActivity.java中下面我们进行数据绑定操作。

如果写代码过程中发现IDE并没有自动正确生成对应的Binding类,则参考这篇文章:Android Studio不能正常生成相关类/方法的解决办法,仅需几步操作即可使IDE正常生成:

  1. 定义成员变量:
1
private ActivityMainBinding mBinding;
  1. 在onCreate()中初始化mBinding
1
2
3
4
5
6
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mBinding.tvExample.setText("Binding Text");
}

此时就可以从mBinding中获取到布局中的所有View了,比如上面的mBinding.tvExample。

总结

Activity中使用Data Binding很简单,省去了模版化的代码findViewById(),也避免了使用ButterKnife等第三方库,省时省力。

除了获取布局中的元素,后续的文章我们会讲到如何设置布局中的数据,可以点击这里查看。

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

本系列目录

安装依赖库,配置工程

Data Binding安装和配置都非常简单,仅需简单的两步即可完成。

更新SDK

打开SDK管理工具,下载最新的Android Support库。

配置工程的Gradle

1
2
3
4
5
6
android {

dataBinding {
enabled = true
}
}

加入完成后,然后点击Sync Now,完成后就可以使用Data Binding强大的功能了。

总结

这一篇我们介绍了Data Binding的配置,下一篇我们将介绍在Activity中使用Data Binding的内容,请点击:使用Data Binding(三)在Activity中的使用

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

本系列目录

引言

Data Binding(数据绑定)发布于2015年的Google I/O大会,旨在减少Android开发中的大量模板代码(比如findViewById()),增加代码及逻辑清晰度,提高开发效率和维护效率。

什么是Data Binding

Data Binding,即数据绑定,是Android团队实现MVVM架构的一种方法,使得数据(对象)可以直接绑定到布局的xml中,数据的变化直接反映到View上。

同时,Data Binding也支持双向绑定。

有什么好处

  • 省去大量模板代码,比如findViewById(),setOnClickListener(), setText(),等等。
  • 使得View与逻辑彻底解耦(MVVM)成为可能,不像MVC那样逻辑与View操作混在一起难以维护,也不像MVP那样定义大量接口,费时费力。
  • 由于数据(对象)与View进行双向绑定,所以开发时只需要关注数据(对象)即可,无需关心View的各种繁杂操作(如setVisibility(),setText()等)
  • 功能强大,xml中即可完成简单的逻辑(xml中支持表达式语言,逻辑/数学运算等)

使用简单吗

集成非常简单,使用起来也非常方便,使你的工程支持Data Binding,仅需一项配置即可。

1
2
3
4
5
6
android {

dataBinding {
enabled = true
}
}

具体的使用步骤,可以点击下一篇:Data Binding的集成与配置

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

使用Python的requests库时,默认是没有失败时重试请求的,通过下面的方式可以支持重试请求

设置请求时的重试规则

1
2
3
4
5
6
7
8
9
import requests
from requests.adapters import HTTPAdapter

s = requests.Session()
a = HTTPAdapter(max_retries=3)
b = HTTPAdapter(max_retries=3)
#将重试规则挂载到http和https请求
s.mount('http://', a)
s.mount('https://', b)

请求Url

上面设置完毕后,通过改Session的请求就可以支持失败重试

1
2
3
4
5
6
7
8
9
10
11
r = s.get('http://api.map.baidu.com/geocoder?location=39.90733345,116.391244079988&output=json')
# 返回的状态码
r.status_code
# 响应内容,中文为utf8编码
r.content
# 响应的字符串形式,中文为unicode编码
r.text
# 响应头中的编码
r.encoding
# 响应头信息
r.headers

通过经纬度获取具体省市区位置地址的Api

本文介绍通过几种方式免费通过经纬度获取到具体位置地址信息的方法

方法一:百度Api(推荐)

经过数个Api的比较,发现百度Api获取到的地理位置数据比较规范,包括省市区等字段,方便实用

http://api.map.baidu.com/geocoder?location=39.90733345,116.391244079988&output=json

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"status":"OK",
"result":{
"location":{
"lng":116.391244,
"lat":39.907334
},
"formatted_address":"北京市西城区东松树胡同28号",
"business":"和平门,前门,宣武门",
"addressComponent":{
"city":"北京市",
"direction":"near",
"distance":"6",
"district":"西城区",
"province":"北京市",
"street":"东松树胡同",
"street_number":"28号"
},
"cityCode":131
}
}

方法二:阿里云Api

阿里云获取到的数据较不规范,省市区都在一个字段里了,不易使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
"queryLocation": [
39.90733345,
116.391244079988
],
"addrList": [
{
"type": "street",
"status": 1,
"name": "西长安街",
"admCode": "110102",
"admName": "北京市,西城区",
"addr": "",
"nearestPoint": [
116.39173,
39.90742
],
"distance": 7.453
},
{
"type": "poi",
"status": 1,
"name": "升平署旧址",
"id": "ANB000A84ZLK",
"admCode": "110102",
"admName": "北京市,北京市,西城区,",
"addr": "南长街南口路西",
"nearestPoint": [
116.39117,
39.90829
],
"distance": 96.255
},
{
"type": "doorPlate",
"status": 0,
"name": "",
"admCode": "",
"admName": "",
"nearestPoint": [],
"distance": -1
}
]
}

方法三:Python的第三方库(geopy)

这种方式获取到的地址信息与阿里云Api获取的到数据存在一样的问题,但是有一个优点,就是可以获取全世界范围内的地理位置信息。

具体使用请参考我的另一篇文章,点击这里