Android新推出了一个官方的数据绑定框架Data Binding Library,既然是官方推出的新玩意,我们就有必要了解一下Android新带来的数据绑定框架,等到该框架推出正式版的时候,我们就可以快速地运用到项目中去。数据绑定框架给我们带来了很大的方便性,以前我们可能需要在Activity里写很多的findViewById,烦人的代码也增加了我们代码的耦合性,现在我们马上就可以抛弃那些findViewById。说到这里,有人可能会问:我使用的一些注解框架也可以不用findViewById啊,是的,但是注解的缺点是拖累代码的效率,Data Binding则不会,Android官方文档说还会提高解析XML的速度,最主要的是Data Binding并不是单单减少我们的findViewById,更多的好处我们接下来一起探寻。

1.环境

使用最新的Android Studio,并更新你的Suport Repository到最新的版本,确保Android Studio的Gradle插件不低于1.5.0

classpath 'com.android.tools.build:gradle:1.5.0'

然后修改对应模块(Module)的build.gradle,添加如下脚本代码:

android {        //添加DataBinding Library      dataBinding {      enabled true      }  }

最后,点击Sync同步一下Gradle即可完成环境配置

2.Data Binding示例

首先,我们需要新建一个Java Bean,一个简单的学生类。

package com.example.iaiai.databinding;    /**  * Created by iaiai on 2016/2/16.  */  public class Student {        private String name;      private String addr;        public Student() {        }        public Student(String name,String addr) {          this.addr = addr;          this.name = name;      }        public String getName() {          return name;      }        public void setName(String name) {          this.name = name;      }        public String getAddr() {          return addr;      }        public void setAddr(String addr) {          this.addr = addr;      }  }

其次,编写布局文件data_binding.xml:

<?xml version="1.0" encoding="utf-8"?>                                                    //也可以是android:text="@{stu.getName()}"             //也可以是android:text="@{stu.getAddr()}"          

最后,实现MainActivity,为变量赋值

import com.example.iaiai.databinding.databinding.DataBindingBinding;    public class MainActivity extends AppCompatActivity {          @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);            DataBindingBinding binding =  DataBindingUtil.setContentView(this, R.layout.data_binding);          binding.setStu(new Student("lee", "Shenzhen"));        }

由上面可以看出,MainActivity的代码非常简单,就添加了两行代码,需要注意的是我们并没有findViewById然后再去setText。

3.Data Binding详解

上面的示例仅仅是带领我们进入了Data Binding的世界,接下来我们解释一下Data Binding的开发步骤。先看看上面的布局文件。

<?xml version="1.0" encoding="utf-8"?>                                      .....

根节点使用的是layout,在layout中分成两部分,第一部分是data节点,第二部分才是我们布局的根节点,在data节点下我们定义了一个variable,它是一个变量,变量名称是stu,类型是com.example.iaiai.databinding.Student,这类似我们在java文件中的定义:

com.example.iaiai.databinding.Student stu;

不过这里要写Student完整的包名,如果这里我们需要多个Student呢?我们可以像写java文件那样导入类包

                                                        .....    

这样就类似于java中的

import com.example.iaiai.databinding.Student;    Student stu1,stu2,...

既然变量定义好了,那该怎么使用呢?我们仍然看上面的xml文件

        ....                                          

由上面可以看出,两个TextView的android:text,它的值是以@开始,以{}包裹的形式出现,而值呢?是stu.name,stu就是上面定义的variable,name就是Student类中的成员变量,其实这里就会去调用stu.getName()方法。

最后,我们看看如何给变量赋值呢?如下代码:

import com.example.iaiai.databinding.databinding.DataBindingBinding;    public class MainActivity extends AppCompatActivity {          @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);            DataBindingBinding binding =  DataBindingUtil.setContentView(this, R.layout.data_binding);          binding.setStu(new Student("lee", "Shenzhen")); //赋值        }

大部分情况,我们会在Activity中去使用它,以前我们都是在OnCreate方法中通过setContextView去设置布局。但现在不一样了,现在我们是通过DataBindingUtil类的一个静态方法setContentView设置布局,同时该方法会返回一个对象,这个对象时一个自动生成的类的对象,如DataBindingBinding?那么它的命名规则是什么呢?将我们布局文件的首字母大写,并且去掉下划线,将下划线后面的字母大写,加上后缀Binding组成。最后,我们通过这个对象来给变量赋值。

通过以上分析,我们了解Data Binding的具体开发步骤,下面让我们定义不同的几个变量看看

                                                                                                                                      

由上面代码可以看出,String类型的变量没有导入包,这是因为Data Binding和Java一样,java.lang包里的类,我们是可以不用导入包的,再往下一个boolean和int类型的变量,都是java基本类型,也不用导入包。

再来看看几个TextView,第二个TextView,我们直接使用@{str}来为android:text设置文本内容;接下来注意第三个TextView,我们使用android:text="@{String.valueOf(num)}"来设置一个int类型的变量,因为在给android:text设置int类型的值一定要转化为String类型,不然系统会认为是资源文件id。此外,我们还学习到了一点,在Xml中,我们不仅可以使用变量,而且还可以调用方法

  1. 变量定义的高级部分

在上面,我们学会了如何在xml中定义变量,但是我们并没有定义像List、Map等这样的集合变量。那么到底能不能定义呢?答案是肯定的,而且定义的方式和我们上面的基本一致,区别就在于我们还需要为它定义key的变量,例如:

<?xml version="1.0" encoding="utf-8"?>                                                                                                                                                                                                                                                                                            

然后在java代码中为变量赋值

public class MainActivity extends AppCompatActivity {          @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);              DataBindingBinding binding =  DataBindingUtil.setContentView(this, R.layout.data_binding);          binding.setStu(new Student("lee", "Shenzhen"));          binding.setStr("just do it");          binding.setNum(10);            ArrayList list = new ArrayList();          list.add("list1");          list.add("list2");            binding.setList(list);          binding.setListKey(0);            HashMap map = new HashMap();          map.put("name","liu");          map.put("sex","male");            binding.setMap(map);          binding.setMapKey("sex");            String[] array = new String[2];          array[0] = "array0";          array[1] = "array1";            binding.setArray(array);          binding.setArrayKey(1);        }

5.表达式

xml中还支持表达式

如上所示,android:text后是一个三元表达式,如果error是true,则text就是error,否则是OK。

除此外还支持null合并操作,??--左边的对象如果它不是null,选择左边的对象;或者如果它是null,选择右边的对象

还支持以下表达式:

  • 数学 + - / * %
  • 字符串连接 +
  • 逻辑 && ||
  • 二进制 & | ^
  • 一元运算 + - ! ~
  • 移位 >> >>> <<
  • 比较 == > < >= <=
  • instanceof
  • 分组 ()
  • null
  • Cast
  • 方法调用
  • 数据访问 []
  • 三元运算 ?:

示例:

android:text="@{String.valueOf(index + 1)}"  android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"  android:transitionName='@{"image_" + id}'

但是它不支持一下表达式:

  • this
  • super
  • new
  • 显式泛型调用
  1. 其他知识点

6.1 设置别名

假如我们import了两个相同名称的类咋办?我们可以借助于别名来解决,别名借助alias字段来标识,例如:

                        

6.2 自定义Binding类名称

默认情况下,Binding类的命名是基于所述layout文件的名称,用大写开头,除去下划线()以及()后的第一个字母大写,然后添加“Binding”后缀。这个类将被放置在一个模块封装包里的databinding封装包下。例如,所述layout文件contact_item.xml将生成ContactItemBinding。如果模块包是com.example.my.app,那么它将被放置在com.example.my.app.databinding。

Binding类可通过调整data元素中的class属性来重命名或放置在不同的包中。例如:

      ...  

在模块封装包的databinding包中会生成名为ContactItem的Binding类。如果要想让该类生成在不同的包中,你需要添加前缀.,如下:

      ...  

在这个情况下,ContactItem类直接在模块包中生成。或者你可以提供整个包名:

      ...  

6.3 字符串

当使用单引号包含属性值时,在表达式中使用双引号很容易:

android:text='@{map["firstName"]}'

使用双引号来包含属性值也是可以的。字符串前后需要使用"":

android:text="@{map[`firstName`]}"

6.4 Resources

使用正常的表达式来访问resources也是可行的:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

6.5 include

通过使用application namespace以及在属性中的Variable名字从容器layout中传递Variables到一个被包含的layout:

<?xml version="1.0" encoding="utf-8"?>                                                   

注意:在name.xml以及contact.xml两个layout文件中必需要有user variable

7.事件绑定

大家都知道,在xml中我们可以给button设置一个onClick来达到事件的绑定,现在DataBinding也提供了事件绑定,而且不仅仅是button。首先定义一个对象处理点击事件,如下:

/**  * Created by iaiai on 2016/2/16.  */  public class EventHandler {        public void handleClick(View view) {            Toast.makeText(view.getContext(),"click",Toast.LENGTH_SHORT).show();      }  }

其次看布局:

<?xml version="1.0" encoding="utf-8"?>                                                          

最后,实现事件绑定

public class MainActivity extends AppCompatActivity {          @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);            ClickBindingBinding binding =  DataBindingUtil.setContentView(this, R.layout.click_binding);          binding.setHandler(new EventHandler());        }

8.Data对象

我们学会了通过binding为我们的变量设置数据,但是不知道你有没有发现一个问题,当我们数据改变的时候会怎样?数据是跟随着改变呢?还是原来的数据呢?这里告诉你答案:很不幸,显示的还是原来的数据?那有没有办法让数据源发生变化后显示的数据也随之发生变化?先来想想ListView是怎么做的, ListView的数据是通过Adapter提供的,当数据发生改变时,我们通过notifyDatasetChanged通过UI去改变数据,这里面的原理其实就是内容观察者,庆幸的是DataBinding也支持内容观察者,而且使用起来也相当方便!

8.1 Observable

我们可以通过Observable的方式去通知UI数据已经改变了,当然了,官方为我们提供了更加简便的方式BaseObservable,我们的实体类只需要继承该类,稍做几个操作,就能轻松实现数据变化的通知。如何使用呢? 首先我们的实体类要继承BaseObservale类,第二步在Getter上使用注解@Bindable,第三步,在Setter里调用方法notifyPropertyChanged,第四步,完成。就是这么简单,下面我们来实际操作一下。

首先定义一个实体类,并继承BaseObservable

/**  * Created by iaiai on 2016/2/16.  */  public class Student extends BaseObservable{        private String name;      private String addr;        public Student() {        }        public Student(String name,String addr) {          this.addr = addr;          this.name = name;      }        @Bindable      public String getName() {          return name;      }        public void setName(String name) {          this.name = name;          notifyPropertyChanged(com.example.iaiai.databinding.BR.name);      }        @Bindable      public String getAddr() {          return addr;      }        public void setAddr(String addr) {          this.addr = addr;          notifyPropertyChanged(com.example.iaiai.databinding.BR.addr);      }  }

观察getName方法,我们使用了@Bindable注解,观察setName,我们调用了notifyPropertyChanged方法,这个方法还需要一个参数,这里参数类似于R.java,保存了我们所有变量的引用地址,这里我们使用了name。

其次,看看布局文件

<?xml version="1.0" encoding="utf-8"?>                                                                                

最后,java实现

public class MainActivity extends AppCompatActivity {        private Student mStu;        @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);            ClickBindingBinding binding =  DataBindingUtil.setContentView(this, R.layout.click_binding);          mStu = new Student("lau","Shenzhen");            binding.setStu(mStu); //设置初始显示数据          binding.setClick(this); //设置点击事件        }          public void click(View view) {          //点击时数据发生改变          mStu.setName("lee");          mStu.setAddr("Beijing");      }  }

8.2 ObservableFields

上面使用BaseObservable已经非常容易了,但是google工程师还不满足,继续给我们封装了一系列的ObservableFields,这里有ObservableField,ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,ObservableInt,ObservableLong,ObservableFloat,ObservableDouble,ObservableParcelable

ObservableFields的使用方法就更加简单了,例如下面代码:

public class People {        public ObservableField name = new ObservableField<>();        public ObservableInt age = new ObservableInt();        public ObservableBoolean isMan = new ObservableBoolean();    }

很简单,只有三个ObservableField变量,并且没有getter和setter,因为我们不需要getter和setter。
在xml中怎么使用呢?

                                                                                        

也很简单,直接使用变量,那怎么赋值和取值呢?这些ObservableField都会有一对get和set方法,所以使用起来也很方便了:

mPeople = new People();    binding.setPeople(mPeople);    mPeople.name.set("people");    mPeople.age.set(19);    mPeople.isMan.set(true);

8.3 Observable Collections

既然普通的变量我们有了ObservableFields的分装,那集合呢?当然也有啦,来看着两个:ObservableArrayMap,ObservableArrayList。使用和普通的Map、List基本相同,直接看代码:

                                                                                    

在来看java文件,怎么设置数据

ObservableArrayMap map = new ObservableArrayMap<>();    ObservableArrayList list = new ObservableArrayList<>();    map.put("name", "loader or qibin");    list.add("loader!!!");    binding.setMap(map);    binding.setList(list);

9.Inflate

上面的代码我们都是在activity中通过DataBindingUtil.setContentView来加载的布局的,现在有个问题了,如果我们是在Fragment中使用呢?Fragment没有setContentView怎么办?不要着急,Data Binding也提供了inflate的支持!

使用方法如下,大家肯定会觉得非常眼熟。

MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);    MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);

接下来,我们就尝试着在Fragment中使用一下Data Binding吧。

首先还是那个学生类,Student

public class Student extends BaseObservable {        private String name;        private int age;          public Student() {        }          public Student(int age, String name) {            this.age = age;            this.name = name;        }          @Bindable        public int getAge() {            return age;        }          public void setAge(int age) {            this.age = age;            notifyPropertyChanged(org.loader.app5.BR.age);        }          @Bindable        public String getName() {            return name;        }          public void setName(String name) {            this.name = name;            notifyPropertyChanged(org.loader.app5.BR.name);        }    }

其次,activity的布局

                

Activity的实现

public class MainActivity extends AppCompatActivity {          @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            getSupportFragmentManager().beginTransaction()                    .replace(R.id.container, new MyFragment()).commit();        }    }

重点来了,我们这里data binding的操作都放在了fragment里,那么我们先来看看fragment的布局。

                                                                                                    

两个TextView分别绑定了Student的name和age字段,而且给name添加了一个点击事件,点击后会调用Fragment的click方法。我们来迫不及待的看一下Fragment怎么写:

public class MyFragment extends Fragment {          private Student mStu;          @Nullable        @Override        public View onCreateView(LayoutInflater inflater,                                 ViewGroup container, Bundle savedInstanceState) {            org.loader.app5.Custom binding = DataBindingUtil.inflate(inflater,                    R.layout.frag_layout, container, false);            mStu = new Student(20, "loader");            binding.setStu(mStu);            binding.setFrag(this);            return binding.getRoot();        }          public void click(View view) {            mStu.setName("qibin");            mStu.setAge(18);        }    }

在onCreateView中,不同于在Activity中,这里我们使用了DataBindingUtil.inflate方法,接受4个参数,第一个参数是一个LayoutInflater对象,正好,我们这里可以使用onCreateView的第一个参数,第二个参数是我们的布局文件,第三个参数是一个ViewGroup,第四个参数是一个boolean类型的,和在LayoutInflater.inflate一样,后两个参数决定了是否想container中添加我们加载进来的布局。

下面的代码和我们之前写的并无差别,但是有一点,onCreateView方法需要返回一个View对象,我们从哪获取呢?ViewDataBinding有一个方法getRoot可以获取我们加载的布局,是不是很简单?

来看一下效果:


1.gif

10.Data Binding VS RecyclerView

有了上面的思路,大家是不是也会在ListView和RecyclerView中使用了?我们仅以一个RecyclerView来学习一下。

首先来看看item的布局,

                                                                                  

可以看到,还是用了那个Student实体,这样得代码,相信你也已经看烦了吧。 那我们来看看activity的。

private RecyclerView mRecyclerView;    private ArrayList mData = new ArrayList() {        {            for (int i=0;i<10;i++) add(new Student("loader" + i, 18 + i));        }    };      @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);          mRecyclerView = (RecyclerView) findViewById(R.id.recycler);        mRecyclerView.setLayoutManager(new LinearLayoutManager(this,                LinearLayoutManager.VERTICAL, false));        mRecyclerView.setAdapter(new MyAdapter(mData));    }

这里给RecyclerView设置了一个Adapter,相信最主要的代码就在这个Adapter里。

private class MyAdapter extends RecyclerView.Adapter {          private ArrayList mData = new ArrayList<>();          private MyAdapter(ArrayList data) {            mData.addAll(data);        }          @Override        public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {            ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater                    .from(viewGroup.getContext()), R.layout.item, viewGroup, false);            ViewHolder holder = new ViewHolder(binding.getRoot());            holder.setBinding(binding);            return holder;        }          @Override        public void onBindViewHolder(ViewHolder viewHolder, int i) {            viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));            viewHolder.getBinding().executePendingBindings();        }          @Override        public int getItemCount() {            return mData.size();        }          class ViewHolder extends RecyclerView.ViewHolder {              private ViewDataBinding binding;              public ViewHolder(View itemView) {                super(itemView);            }              public void setBinding(ViewDataBinding binding) {                this.binding = binding;            }              public ViewDataBinding getBinding() {                return this.binding;            }        }

果然,这个adapter的写法和我们之前的写法不太一样,首先看看ViewHolder,在这个holder里,我们保存了一个ViewDataBinding对象,并给它提供了Getter和Setter方法, 这个ViewDataBinding是干嘛的?我们稍后去讲。继续看看onCreateViewHolder,在这里面,我们首先调用DataBindingUtil.inflate方法返回了一个ViewDataBinding的对象,这个ViewDataBinding是个啥?我们以前没见过啊,这里告诉大家我们之前返回的那些都是ViewDataBinding的子类!继续看代码,我们new了一个holder,参数是肯定是我们的item布局了,继续看,接着我们又把binding设置给了holder,最后返回holder。这时候,我们的holder里就保存了刚刚返回的ViewDataBinding对象,干嘛用呢?继续看onBindViewHolder就知道了。

@Override    public void onBindViewHolder(ViewHolder viewHolder, int i) {        viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));        viewHolder.getBinding().executePendingBindings();    }

只有两行代码,但是都是我们没有见过的,首先第一行,我们以前都是使用类似binding.setStu这样方法去设置变量,那这个setVariable呢? 为什么没有setStu,这里要记住,ViewDataBinding是我们之前用的那些binding的父类,只有自动生成的那些子类才会有setXXX方法,那现在我们需要在ViewDataBinding中设置变量咋办?这个类为我们提供了setVariable去设置变量,第一个参数是我们的变量名的引用,第二个是我们要设置的值。

第二行代码,executePendingBindings的作用是干嘛的?

官方的回答是:
当数据改变时,binding会在下一帧去改变数据,如果我们需要立即改变,就去调用executePendingBindings方法。 所以这里的作用就是去让数据的改变立即执行。

ok,现在看起来,我们的代码更加简洁了,而且不需要保存控件的实例,是不是很爽? 来看看效果:


2.png

11.View with ID

在使用Data Binding的过程中,我们发现并没有保存View的实例,但是现在我们有需求需要这个View的实例咋办?难道走老路findViewById?当然不是啦,当我们需要某个view的实例时,我们只要给该view一个id,然后Data Binding框架就会给我们自动生成该view的实例,放哪了?当然是ViewDataBinding里面。

上代码:

                                                      

xml中代码没有什么好说的,都是之前的代码。需要注意的是, 我们给TextView设定了一个id-textView。

activity代码如下:

public class MainActivity extends AppCompatActivity {          private org.loader.app7.Custom mBinding;        private ObservableField mString;          @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            mBinding = DataBindingUtil.setContentView(this,                    R.layout.activity_main);            mString = new ObservableField();            mString.set("loader");            mBinding.setStr(mString);            mBinding.setHandler(this);        }          public void click(View view) {            mString.set("qibin");            mBinding.textView.setTextColor(Color.GREEN);  //找到控件       }    }

通过ViewDataBinding类的实例直接去获取的。只要我们给了view一个id,那么框架就会在ViewDataBinding中自动帮我们保存这个view的实例,变量名就是我们设置的id。

12.自定义setter(BindingAdapter)

想想这样的一种情景,一个ImageView需要通过网络去加载图片,那我们怎么办?看似好像使用DataBinding不行,恩,我们上面所学到东西确实不能够解决这个问题,但是DataBinding框架给我们提供了很好的扩展,允许我们自定义setter,那该怎么做呢?这里就要引出另一个知识点——BindingAdapter,这是一个注解,参数是一个数组,数组中存放的是我们自定义的’属性’。接下来就以一个例子学习一下BindingAdapter的使用。

                                          

这里我们增加了一个命名空间app,并且注意ImageView的app:image属性,这里和我们自定义view时自定义的属性一样,但是这里并不需要我们去重写ImageView,这条属性的值是我们上面定义的String类型的imageUrl,从名称中看到这里我们可能会塞给他一个url。

activity代码如下:

public class MainActivity extends AppCompatActivity {          @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            org.loader.app8.Custom binding = DataBindingUtil.setContentView(this,                    R.layout.activity_main);            binding.setImageUrl("http://images.csdn.net/20150810/Blog-Image%E5%89%AF%E6%9C%AC.jpg");        }    }

果然在这里我们set了一个url,那图片怎么加载呢?这里就要使用到我们刚才说的BindingAdapter注解了。

public class Utils {        @BindingAdapter({"bind:image"})        public static void imageLoader(ImageView imageView, String url) {            ImageLoaderUtils.getInstance().displayImage(url, imageView);        }    }

我们定义了一个Utils类,这个类你可以随便起名,该类中只有一个静态的方法imageLoader,该方法有两个参数,一个是需要设置数据的view, 一个是我们需要的url。值得注意的是那个BindingAdapter注解,看看他的参数,是一个数组,内容只有一个bind:image,仅仅几行代码,我们不需要 手工调用Utils.imageLoader,也不需要知道imageLoader方法定义到哪了,一个网络图片加载就搞定了,是不是很神奇,这里面起关键作用的就是BindingAdapter 注解了,来看看它的参数怎么定义的吧,难道是乱写?当然不是,这里要遵循一定的规则,

以bind:开头,接着书写你在控件中使用的自定义属性名称。

这里就是image了,不信来看。

13.Converters

Converter是什么呢?举个例子吧:假如你的控件需要一个格式化好的时间,但是你只有一个Date类型额变量咋办?肯定有人会说这个简单,转化完成后在设置,恩,这也是一种办法,但是DataBinding还给我们提供了另外一种方式,虽然原理一样,但是这种方式使用的场景更多,那就是——Converter。和上面的BindingAdapter使用方法一样,这也是一个注解。下面还是以一段代码的形式进行学习。

                                          

看TextView的text属性,我们需要一个String类型的值,但是这里确给了一个Date类型的,这就需要我们去定义Converter去转换它,

activity代码如下:

public class MainActivity extends AppCompatActivity {          @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            org.loader.app9.Custom binding = DataBindingUtil.setContentView(this,                    R.layout.activity_main);            binding.setTime(new Date());        }    }

去给这个Date类型的变量设置值。怎么去定义Converter呢? 看代码:

public class Utils {          @BindingConversion        public static String convertDate(Date date) {            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");            return sdf.format(date);        }    }

和上面一样,我们不需要关心这个convertDate在哪个类中,重要的是他的@BindingConversion注解,这个方法接受一个Date类型的变量,正好我们的android:text设置的就是一个Date类型的值,在方法内部我们将这个Date类型的变量转换成String类型的日期并且返回。这样UI上就显示出我们转化好的字符串。


3.png

欢迎加入QQ群:104286694

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. 浅谈Androi安全问题和Android(安卓)P安全新特性
  3. Android事件分发机制及源码分析
  4. Android的五大基本组件
  5. Android中使用广播机制退出多个Activity
  6. Android开场屏 (Splash Screen)
  7. android 的Java代码中的布局相关方法LayoutParams
  8. Android添加用户组及自定义App权限的方法
  9. android sqlite遇到的一个吐血的问题

随机推荐

  1. android九宫格实现
  2. Android横屏竖屏切换的问题
  3. Android学习入门
  4. 淘宝Android客户端技术分析
  5. android中Intent简介
  6. 程序设置横屏后,锁屏时会被销毁一遍,解锁时
  7. 自定义tabhost实现
  8. (翻译)又一个Android Sqlite库: Cupboard
  9. Android高手进阶教程(三)之----Android
  10. Android编译系统makefile(Android.mk)说