對(duì)Repository的解釋
http://m.itdecent.cn/p/4679c384acae //初探Android中Repository模式
在應(yīng)用需要加載數(shù)據(jù)或者保存數(shù)據(jù)的時(shí)候,建議創(chuàng)建一個(gè)Repository的存儲(chǔ)區(qū)類,里面放置存儲(chǔ)與加載應(yīng)用數(shù)據(jù)的API
注意:
一,anroid 的文件夾一般都是小寫字母開頭(尤其包名一定要小寫字母開頭),類都是大寫字母開頭.
二,用dataBinding可以不用給findById 這要找控件賦值,可以不用設(shè)置Id直接在xml種賦值,這樣就不用設(shè)置了Id了
三,AndroidViewModel
使用ViewModel的時(shí)候,需要注意的是ViewModel不能夠持有View、Lifecycle、Acitivity引用,而且不能夠包含任何包含前面內(nèi)容的類。因?yàn)檫@樣很有可能會(huì)造成內(nèi)存泄漏。
那如果需要使用Context對(duì)象改怎么辦。這時(shí)候我們可以給ViewModel一個(gè)Application。Application是一個(gè)Context,而且一個(gè)應(yīng)用也只會(huì)有Application。
我們自己添加Application?其實(shí)沒必要Google還有一個(gè)AndroidViewModel。這是一個(gè)包含Application的ViewModel。
dataBinding {
enabled = true
}
}
ViewModel類是用來(lái)保存UI數(shù)據(jù)的類,它會(huì)在配置變更(即 Configuration Change,例如手機(jī)屏幕的旋轉(zhuǎn))之后繼續(xù)存在



android 編譯以后會(huì)有dataBinding,dataBinding是出現(xiàn)在這個(gè)目錄下







dataBinding:
DataBinding 是谷歌官方發(fā)布的一個(gè)框架,顧名思義即為數(shù)據(jù)綁定,是 MVVM 模式在 Android 上的一種實(shí)現(xiàn),用于降低布局和邏輯的耦合性,使代碼邏輯更加清晰。MVVM 相對(duì)于 MVP,其實(shí)就是將 Presenter 層替換成了 ViewModel 層。DataBinding 能夠省去我們一直以來(lái)的 findViewById() 步驟,大量減少 Activity 內(nèi)的代碼,數(shù)據(jù)能夠單向或雙向綁定到 layout 文件中,有助于防止內(nèi)存泄漏,而且能自動(dòng)進(jìn)行空檢測(cè)以避免空指針異常
啟用 DataBinding 的方法是在對(duì)應(yīng) Model 的 build.gradle 文件里加入以下代碼,同步后就能引入對(duì) DataBinding 的支持
android {
dataBinding {
enabled = true
}
}
一、基礎(chǔ)入門
啟用 DataBinding 后,這里先來(lái)看下如何在布局文件中綁定指定的變量
打開布局文件,選中根布局的 ViewGroup,按住 Alt + 回車鍵,點(diǎn)擊 “Convert to data binding layout”,就可以生成 DataBinding 需要的布局規(guī)則

<?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"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</android.support.constraint.ConstraintLayout>
</layout>
和原始布局的區(qū)別在于多出了一個(gè) layout 標(biāo)簽將原布局包裹了起來(lái),data 標(biāo)簽用于聲明要用到的變量以及變量類型,要實(shí)現(xiàn) MVVM 的 ViewModel 就需要把數(shù)據(jù)(Model)與 UI(View)進(jìn)行綁定,data 標(biāo)簽的作用就像一個(gè)橋梁搭建了 View 和 Model 之間的通道
這里先來(lái)聲明一個(gè) Modle
package com.leavesc.databinding_demo.model;
/**
* 作者:葉應(yīng)是葉
* 時(shí)間:2018/5/16 20:20
* 描述:https://github.com/leavesC
*/
public class User {
private String name;
private String password;
···
}
在 data 標(biāo)簽里聲明要使用到的變量名、類的全路徑
<data>
<variable
name="userInfo"
type="com.leavesc.databinding_demo.model.User" />
</data>
如果 User 類型要多處用到,也可以直接將之 import 進(jìn)來(lái),這樣就不用每次都指明整個(gè)包名路徑了,而 java.lang.* 包中的類會(huì)被自動(dòng)導(dǎo)入,所以可以直接使用
<data>
<import type="com.leavesc.databinding_demo.model.User"/>
<variable
name="userInfo"
type="User"/>
</data>
如果存在 import 的類名相同的情況,可以使用 alias 指定別名
<data>
<import type="com.leavesc.databinding_demo.model.User" />
<import
alias="TempUser"
type="com.leavesc.databinding_demo.model2.User" />
<variable
name="userInfo"
type="User" />
<variable
name="tempUserInfo"
type="TempUser" />
</data>
這里聲明了一個(gè) User 類型的變量 userInfo,我們要做的就是使這個(gè)變量與兩個(gè) TextView 控件掛鉤,通過(guò)設(shè)置 userInfo 的變量值同時(shí)使 TextView 顯示相應(yīng)的文本
完整的布局代碼如下所示
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.leavesc.databinding_demo.model.User" />
<variable
name="userInfo"
type="User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical"
tools:context="com.leavesc.databinding_demo.Main2Activity">
<TextView
android:id="@+id/tv_userName"
···
android:text="@{userInfo.name}" />
<TextView
···
android:text="@{userInfo.password}" />
</LinearLayout>
</layout>
通過(guò) @{userInfo.name} 使 TextView 引用到相關(guān)的變量,DataBinding 會(huì)將之映射到相應(yīng)的 getter 方法
之后可以在 Activity 中通過(guò) DataBindingUtil 設(shè)置布局文件,省略原先 Activity 的 setContentView() 方法,并為變量 userInfo 賦值
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMain2Binding activityMain2Binding = DataBindingUtil.setContentView(this, R.layout.activity_main2);
user = new User("leavesC", "123456");
activityMain2Binding.setUserInfo(user);
}

由于 @{userInfo.name}在布局文件中并沒有明確的值,所以在預(yù)覽視圖中什么都不會(huì)顯示,不便于觀察文本的大小和字體顏色等屬性,此時(shí)可以為之設(shè)定默認(rèn)值(文本內(nèi)容或者是字體大小等屬性都適用),默認(rèn)值將只在預(yù)覽視圖中顯示,且默認(rèn)值不能包含引號(hào)
android:text="@{userInfo.name,default=defaultValue}"
此外,也可以通過(guò) ActivityMain2Binding 直接獲取到指定 ID 的控件
activityMain2Binding.tvUserName.setText("leavesC");
每個(gè)數(shù)據(jù)綁定布局文件都會(huì)生成一個(gè)綁定類,ViewDataBinding 的實(shí)例名是根據(jù)布局文件名來(lái)生成,將之改為首字母大寫的駝峰命名法來(lái)命名,并省略布局文件名包含的下劃線??丶墨@取方式類似,但首字母小寫
也可以通過(guò)如下方式自定義 ViewDataBinding 的實(shí)例名
<data class="CustomBinding">
</data>
此外,在綁定表達(dá)式中會(huì)根據(jù)需要生成一個(gè)名為context的特殊變量,context的值是根 View 的getContext()方法返回的Context對(duì)象, context變量會(huì)被具有該名稱的顯式變量聲明所覆蓋
Databinding 同樣是支持在 Fragment 和 RecyclerView 中使用 。例如,可以看 Databinding 在 Fragment 中的使用
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
FragmentBlankBinding fragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false);
fragmentBlankBinding.setHint("Hello");
return fragmentBlankBinding.getRoot();
}
**以上實(shí)現(xiàn)數(shù)據(jù)綁定的方式,每當(dāng)綁定的變量發(fā)生變化的時(shí)候,都需要重新向 ViewDataBinding 傳遞新的變量值才能刷新 UI 。接下來(lái)看如何實(shí)現(xiàn)自動(dòng)刷新 UI **
二、單向數(shù)據(jù)綁定
實(shí)現(xiàn)數(shù)據(jù)變化自動(dòng)驅(qū)動(dòng) UI 刷新的方式有三
種:BaseObservable、ObservableField、ObservableCollection
BaseObservable
一個(gè)純凈的 ViewModel 類被更新后,并不會(huì)讓 UI 自動(dòng)更新。而數(shù)據(jù)綁定后,我們自然會(huì)希望數(shù)據(jù)變更后 UI 會(huì)即時(shí)刷新,Observable 就是為此而生的概念
BaseObservable 提供了 notifyChange() 和 notifyPropertyChanged() 兩個(gè)方法,前者會(huì)刷新所有的值域,后者則只更新對(duì)應(yīng) BR 的 flag,該 BR 的生成通過(guò)注釋@Bindable 生成,可以通過(guò) BR notify 特定屬性關(guān)聯(lián)的視圖
public class Goods extends BaseObservable {
//如果是 public 修飾符,則可以直接在成員變量上方加上 @Bindable 注解
@Bindable
public String name;
//如果是 private 修飾符,則在成員變量的 get 方法上添加 @Bindable 注解
private String details;
private float price;
public Goods(String name, String details, float price) {
this.name = name;
this.details = details;
this.price = price;
}
public void setName(String name) {
this.name = name;
//只更新本字段
notifyPropertyChanged(com.leavesc.databinding_demo.BR.name);
}
@Bindable
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
//更新所有字段
notifyChange();
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
在** setName()** 方法中更新的只是本字段,而 setDetails() 方法中更新的是所有字段
添加兩個(gè)按鈕用于改變 goods 變量的三個(gè)屬性值,由此可以看出兩個(gè) notify 方法的區(qū)別。當(dāng)中涉及的按鈕點(diǎn)擊事件綁定,在下面也會(huì)講到
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.leavesc.databinding_demo.model.Goods" />
<import type="com.leavesc.databinding_demo.Main3Activity.GoodsHandler" />
<variable
name="goods"
type="Goods" />
<variable
name="goodsHandler"
type="GoodsHandler" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp"
tools:context=".Main3Activity">
<TextView
···
android:text="@{goods.name}" />
<TextView
···
android:text="@{goods.details}" />
<TextView
···
android:text="@{String.valueOf(goods.price)}" />
<Button
···
android:onClick="@{()->goodsHandler.changeGoodsName()}"
android:text="改變屬性 name 和 price"
android:textAllCaps="false" />
<Button
···
android:onClick="@{()->goodsHandler.changeGoodsDetails()}"
android:text="改變屬性 details 和 price"
android:textAllCaps="false" />
</LinearLayout>
</layout>
public class Main3Activity extends AppCompatActivity {
private Goods goods;
private ActivityMain3Binding activityMain3Binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
activityMain3Binding = DataBindingUtil.setContentView(this, R.layout.activity_main3);
goods = new Goods("code", "hi", 24);
activityMain3Binding.setGoods(goods);
activityMain3Binding.setGoodsHandler(new GoodsHandler());
}
public class GoodsHandler {
public void changeGoodsName() {
goods.setName("code" + new Random().nextInt(100));
goods.setPrice(new Random().nextInt(100));
}
public void changeGoodsDetails() {
goods.setDetails("hi" + new Random().nextInt(100));
goods.setPrice(new Random().nextInt(100));
}
}
}

可以看到,name 視圖的刷新沒有同時(shí)刷新 price 視圖,而 details 視圖刷新的同時(shí)也刷新了 price 視圖
實(shí)現(xiàn)了Observable 接口的類允許注冊(cè)一個(gè)監(jiān)聽器,當(dāng)可觀察對(duì)象的屬性更改時(shí)就會(huì)通知這個(gè)監(jiān)聽器,此時(shí)就需要用到 OnPropertyChangedCallback
當(dāng)中 propertyId 就用于標(biāo)識(shí)特定的字段
goods.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
if (propertyId == com.leavesc.databinding_demo.BR.name) {
Log.e(TAG, "BR.name");
} else if (propertyId == com.leavesc.databinding_demo.BR.details) {
Log.e(TAG, "BR.details");
} else if (propertyId == com.leavesc.databinding_demo.BR._all) {
Log.e(TAG, "BR._all");
} else {
Log.e(TAG, "未知");
}
}
});
ObservableField
繼承于 Observable 類相對(duì)來(lái)說(shuō)限制有點(diǎn)高,且也需要進(jìn)行 notify 操作,因此為了簡(jiǎn)單起見可以選擇使用 ObservableField。ObservableField 可以理解為官方對(duì) BaseObservable 中字段的注解和刷新等操作的封裝,官方原生提供了對(duì)基本數(shù)據(jù)類型的封裝,例如 ObservableBoolean、ObservableByte、ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble 以及 ObservableParcelable ,也可通過(guò) ObservableField 泛型來(lái)申明其他類型
public class ObservableGoods {
private ObservableField<String> name;
private ObservableFloat price;
private ObservableField<String> details;
public ObservableGoods(String name, float price, String details) {
this.name = new ObservableField<>(name);
this.price = new ObservableFloat(price);
this.details = new ObservableField<>(details);
}
```
}
對(duì) ObservableGoods 屬性值的改變都會(huì)立即觸發(fā) UI 刷新,概念上與 Observable 區(qū)別不大,具體效果可看下面提供的源代碼,這里不再贅述
ObservableCollection
dataBinding 也提供了包裝類用于替代原生的 List和 Map,分別是 ObservableList 和 ObservableMap,當(dāng)其包含的數(shù)據(jù)發(fā)生變化時(shí),綁定的視圖也會(huì)隨之進(jìn)行刷新
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.databinding.ObservableList"/>
<import type="android.databinding.ObservableMap"/>
<variable
name="list"
type="ObservableList<String>"/>
<variable
name="map"
type="ObservableMap<String,String>"/>
<variable
name="index"
type="int"/>
<variable
name="key"
type="String"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.leavesc.databinding_demo.Main12Activity">
<TextView
···
android:padding="20dp"
android:text="@{list[index],default=xx}"/>
<TextView
···
android:layout_marginTop="20dp"
android:padding="20dp"
android:text="@{map[key],default=yy}"/>
<Button
···
android:onClick="onClick"
android:text="改變數(shù)據(jù)"/>
</LinearLayout>
</layout>
private ObservableMap<String, String> map;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMain12Binding activityMain12Binding = DataBindingUtil.setContentView(this, R.layout.activity_main12);
map = new ObservableArrayMap<>();
map.put("name", "leavesC");
map.put("age", "24");
activityMain12Binding.setMap(map);
ObservableList<String> list = new ObservableArrayList<>();
list.add("Ye");
list.add("leavesC");
activityMain12Binding.setList(list);
activityMain12Binding.setIndex(0);
activityMain12Binding.setKey("name");
}
public void onClick(View view) {
map.put("name", "leavesC,hi" + new Random().nextInt(100));
}
DataBindingUtil
獲取dataBinding的方法
val binding: ItemRdGoodItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.item_ed_good_item, parent, false)
var binding:ItemProductBinding
init {
binding = DataBindingUtil.bind(view)!!
DataBindingUtil.setContentView(this,R.layout.activity_main);
BindingAdapter的使用
我們之前用的都是Android自帶的監(jiān)聽或是屬性,比如text、onClick,但是如果項(xiàng)目中需要?jiǎng)討B(tài)改變ImageView的內(nèi)容,那我們應(yīng)該怎么辦呢?dataBinding給我們提供了BindingAdapter這個(gè)注解,方便我們定義自定義的屬性。
假如我們有個(gè)需求,點(diǎn)擊按鈕更換圖片,這個(gè)時(shí)候我們需要定義靜態(tài)的方法:
@BindingAdapter({"url", "name"})
public static void loadImageView(ImageView view, String url, String name) {
Log.i("xwz--->", url + "\t" + name);
Glide.with(view.getContext())
.load(url)
.into(view);
}
在XML中使用
<ImageView
android:layout_width="160dp"
android:layout_height="160dp"
bind:name="@{student.name}"
bind:url="@{student.imgUrl}"/>
這里有必要解釋一下,靜態(tài)方法loadImageView里第一個(gè)參數(shù)為作用的View,這里是ImageView;后面的參數(shù)即分別對(duì)應(yīng)于@BindingAdapter里面的參數(shù)。那這里是怎么跟View聯(lián)系在一塊呢?我們發(fā)現(xiàn)XML中有這樣一行代碼bind:name="@{student.name}這里的name對(duì)應(yīng)的的@BindingAdapter注解里的參數(shù)name,并映射于ViewModel中的student.name。當(dāng)student.name值改變,就會(huì)觸發(fā)loadImageView方法,從而執(zhí)行里面的方法。
bind名稱是任意的定義的,不過(guò)要定義對(duì)應(yīng)的命名空間xmlns:bind="http://schemas.android.com/apk/res-auto"。
實(shí)現(xiàn)的效果就很簡(jiǎn)單了:

更強(qiáng)大的在于可以覆蓋Android原生的元素設(shè)置屬性,比如android:text最常見不過(guò)了
@BindingAdapter ("android:text")
public static void setText(TextView view, String text) {
view.setText(text + "xiaweizi");
Log.i("xwz--->", text);
}
XML:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{"測(cè)試"}'/>
這個(gè)時(shí)候所有設(shè)置text的地方后綴全部加上了xiaweizi.
在java中用static的方法
在kotlin中用文件方法
一. @BindingAdapter 介紹
我們?cè)趚ml中會(huì)給view添加各種屬性,比如 textSize = xxx。databinding庫(kù)為我們提供了一種方法,就是擴(kuò)展view控件的xml屬性,而且使用起來(lái)非常簡(jiǎn)單,就是在你的項(xiàng)目里寫一個(gè)類,然后根據(jù)每一個(gè)屬性寫一個(gè)方法,使用@BindingAdapter告訴databinding框架這個(gè)方法就是用來(lái)自定義屬性的。
1。簡(jiǎn)單應(yīng)用,view動(dòng)態(tài)控制:

如上圖,我們寫了一個(gè)方法,public static void imageSrc(ImageView view, int resId) 用這個(gè)方法來(lái)給ImageView控件添加一個(gè)xml屬性:imageSrc(通過(guò)注解@BindingAdapter來(lái)指定這個(gè)屬性在xml里應(yīng)該叫什么)。 這樣在xml里我們就可以利用這個(gè)屬性來(lái)給imageView來(lái)添加圖片。
xml中使用方式如下:

我們來(lái)看imageView的最后一行, android:imageSrc="@{menuBean.src()}" 就是我們上面自定義的方法。這個(gè)menuBean 是我們?cè)诋?dāng)前xml頁(yè)面綁定的一個(gè)數(shù)據(jù)對(duì)象。這樣一來(lái),我們就可以調(diào)用這個(gè)對(duì)象的一個(gè)方法:src() 來(lái)給iageView指定圖片。
如果你要問(wèn),這樣做的意義何在?imageView自己就有設(shè)置圖片的方法啊。
我們考慮如下情況,一個(gè)imageView,圖片不是固定的,比如根據(jù)用戶級(jí)別來(lái)顯示不同圖片。這樣一來(lái),我們自定義這個(gè)屬性就派上用場(chǎng)了,從服務(wù)端獲取到用戶數(shù)據(jù)后,我們?cè)趍enuBean內(nèi)部就可以計(jì)算判斷出當(dāng)前應(yīng)該顯示的圖片是什么,并且賦值給這個(gè)成員變量。這樣進(jìn)入頁(yè)面的時(shí)imageview就會(huì)顯示我們想要的圖片。
而我們不在需要代碼中寫mageView的resource控制,而通常要控制一個(gè)view,我們都需要在activity都代碼中來(lái)寫,這就意味這這段代碼既要引用到view,又要引用到數(shù)據(jù)data對(duì)象,不符合代碼低耦合的觀點(diǎn)。而用這種方式以后,data對(duì)象里不需要知道view的存在,activity里也不需要去代碼讓這2者關(guān)聯(lián),而是成功的轉(zhuǎn)到了xml中的一條屬性。

三、雙向數(shù)據(jù)綁定
雙向綁定的意思即為當(dāng)數(shù)據(jù)改變時(shí)同時(shí)使視圖刷新,而視圖改變時(shí)也可以同時(shí)改變數(shù)據(jù)
看以下例子,當(dāng) EditText 的輸入內(nèi)容改變時(shí),會(huì)同時(shí)同步到變量 goods,綁定變量的方式比單向綁定多了一個(gè)等號(hào):android:text="@={goods.name}"
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.leavesc.databinding_demo.model.ObservableGoods"/>
<variable
name="goods"
type="ObservableGoods" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main10Activity">
<TextView
···
android:text="@{goods.name}" />
<EditText
···
android:text="@={goods.name}" />
</LinearLayout>
</layout>
public class Main10Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMain10Binding activityMain10Binding = DataBindingUtil.setContentView(this, R.layout.activity_main10);
ObservableGoods goods = new ObservableGoods("code", "hi", 23);
activityMain10Binding.setGoods(goods);
}
}

四、事件綁定
3.1.4 導(dǎo)入的類后,就可以在表達(dá)式中使用類的靜態(tài)屬性/方法:
<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"/>
嚴(yán)格意義上來(lái)說(shuō),事件綁定也是一種變量綁定,只不過(guò)設(shè)置的變量是回調(diào)接口而已
事件綁定可用于以下多種回調(diào)事件
- android:onClick
- android:onLongClick
- android:afterTextChanged
- android:onTextChanged
在 Activity 內(nèi)部新建一個(gè) UserPresenter 類來(lái)聲明 onClick() 和 afterTextChanged() 事件相應(yīng)的回調(diào)方法
public class UserPresenter {
public void onUserNameClick(User user) {
Toast.makeText(Main5Activity.this, "用戶名:" + user.getName(), Toast.LENGTH_SHORT).show();
}
public void afterTextChanged(Editable s) {
user.setName(s.toString());
activityMain5Binding.setUserInfo(user);
}
public void afterUserPasswordChanged(Editable s) {
user.setPassword(s.toString());
activityMain5Binding.setUserInfo(user);
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.leavesc.databinding_demo.model.User" />
<import type="com.leavesc.databinding_demo.MainActivity.UserPresenter" />
<variable
name="userInfo"
type="User" />
<variable
name="userPresenter"
type="UserPresenter" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical"
tools:context="com.leavesc.databinding_demo.MainActivity">
<TextView
···
android:onClick="@{()->userPresenter.onUserNameClick(userInfo)}"
android:text="@{userInfo.name}" />
<TextView
···
android:text="@{userInfo.password}" />
<EditText
···
android:afterTextChanged="@{userPresenter.afterTextChanged}"
android:hint="用戶名" />
<EditText
···
android:afterTextChanged="@{userPresenter.afterUserPasswordChanged}"
android:hint="密碼" />
</LinearLayout>
</layout>
方法引用的方式與調(diào)用函數(shù)的方式類似,既可以選擇保持事件回調(diào)方法的簽名一致:@{userPresenter.afterTextChanged},此時(shí)方法名可以不一樣,但方法參數(shù)和返回值必須和原始的回調(diào)函數(shù)保持一致。也可以引用不遵循默認(rèn)簽名的函數(shù):@{()->userPresenter.onUserNameClick(userInfo)},這里用到了 Lambda 表達(dá)式,這樣就可以不遵循默認(rèn)的方法簽名,將userInfo對(duì)象直接傳回點(diǎn)擊方法中。此外,也可以使用方法引用 :: 的形式來(lái)進(jìn)行事件綁定

高級(jí)綁定

RecyclerView使用databinding出現(xiàn)數(shù)據(jù)閃爍問(wèn)題
@Override
public void onBindViewHolder(TestDBViewHolder holder, int position) {
holder.binding.setData(list.get(position));
holder.binding.executePendingBindings();//加一行,問(wèn)題解決
}
在cycleView中添加dataBinding的問(wèn)題
http://m.itdecent.cn/p/62525ff0caac(這種是用LayoutInflater.from( parent.getContext())來(lái)生成HolderView)
http://m.itdecent.cn/p/4d30efa6b500 (這種是用DataBindingUtil.inflate(inflater, R.layout.item_fruit來(lái)生成HolderView)
兩種方式在cycleView種使用dataBinding
拼接字符串的效率問(wèn)題
JDK1.8
代碼示例
public void test() {
String a = "a" + "b";
// 單行+
String c = "c" + a + a;
// 單行append
StringBuilder builder = new StringBuilder("c");
builder.append(a).append(a);
// 多行+
c += a;
c += a;
// for循環(huán)append
for (int i = 1; i <= 100000; i++) {
builder.append(a);
}
// for循環(huán)+
for (int i = 1; i <= 100000; i++) {
String x = c + a;
}
for (int i = 1; i <= 100000; i++) {
c += a;
}
}
字節(jié)碼
// class version 52.0 (52)
// access flags 0x21
public class com/zhangyue/momr/api/service/Test {
// compiled from: Test.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 7 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lcom/zhangyue/momr/api/service/Test; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x1
public test()V
L0
LINENUMBER 9 L0
LDC "ab"
ASTORE 1
L1
LINENUMBER 12 L1
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
LDC "c"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 2
L2
LINENUMBER 15 L2
NEW java/lang/StringBuilder
DUP
LDC "c"
INVOKESPECIAL java/lang/StringBuilder.<init> (Ljava/lang/String;)V
ASTORE 3
L3
LINENUMBER 16 L3
ALOAD 3
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
POP
L4
LINENUMBER 19 L4
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 2
L5
LINENUMBER 20 L5
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 2
L6
LINENUMBER 23 L6
ICONST_1
ISTORE 4
L7
FRAME FULL [com/zhangyue/momr/api/service/Test java/lang/String java/lang/String java/lang/StringBuilder I] []
ILOAD 4
LDC 100000
IF_ICMPGT L8
L9
LINENUMBER 24 L9
ALOAD 3
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
POP
L10
LINENUMBER 23 L10
IINC 4 1
GOTO L7
L8
LINENUMBER 28 L8
FRAME CHOP 1
ICONST_1
ISTORE 4
L11
FRAME APPEND [I]
ILOAD 4
LDC 100000
IF_ICMPGT L12
L13
LINENUMBER 29 L13
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 5
L14
LINENUMBER 28 L14
IINC 4 1
GOTO L11
L12
LINENUMBER 31 L12
FRAME CHOP 1
ICONST_1
ISTORE 4
L15
FRAME APPEND [I]
ILOAD 4
LDC 100000
IF_ICMPGT L16
L17
LINENUMBER 32 L17
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 2
L18
LINENUMBER 31 L18
IINC 4 1
GOTO L15
L16
LINENUMBER 34 L16
FRAME CHOP 1
RETURN
L19
LOCALVARIABLE i I L7 L8 4
LOCALVARIABLE i I L11 L12 4
LOCALVARIABLE i I L15 L16 4
LOCALVARIABLE this Lcom/zhangyue/momr/api/service/Test; L0 L19 0
LOCALVARIABLE a Ljava/lang/String; L1 L19 1
LOCALVARIABLE c Ljava/lang/String; L2 L19 2
LOCALVARIABLE builder Ljava/lang/StringBuilder; L3 L19 3
MAXSTACK = 3
MAXLOCALS = 6
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 37 L0
NEW com/zhangyue/momr/api/service/Test
DUP
INVOKESPECIAL com/zhangyue/momr/api/service/Test.<init> ()V
INVOKEVIRTUAL com/zhangyue/momr/api/service/Test.test ()V
L1
LINENUMBER 38 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
}
首先String a = "a" + "b";可以看到LDC "ab",證明編譯器在編譯階段就直接做了優(yōu)化
String c = "c" + a + a;可以看到優(yōu)化成了一個(gè)StringBuilder,然后做了3次append
當(dāng)你把多個(gè)+寫在同一行拼接多次時(shí),是一個(gè)StringBuilder做多次append(加號(hào)的數(shù)量+1);當(dāng)寫成多行的時(shí)候,會(huì)new多個(gè)StringBuilder
-
在for循環(huán)中使用+時(shí),每次循環(huán)都會(huì)new一個(gè)StringBuilder
for (int i = 1; i <= 100000; i++) { builder.append(a); } for (int i = 1; i <= 100000; i++) { String x = c + a; } for (int i = 1; i <= 100000; i++) { c += a; }
第三個(gè)for循環(huán)跟前兩個(gè)相比性能差距巨大,但是看字節(jié)碼長(zhǎng)得都差不多,這是為什么呢?
第二個(gè)和第三個(gè)for循環(huán)里的代碼都等價(jià)于new StringBuilder().append(c).append(a);但是最后一個(gè)for循環(huán)的append(c)里的c是在不斷變大的,append底層調(diào)用的是System.arraycopy,在每次循環(huán)都append一個(gè)比較大的字符串,性能是很差的
測(cè)試單行StringBuilder和+拼接的運(yùn)行時(shí)間
public void test() {
String a = "a" + "b";
String c = "c" + a;
long t3 = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
StringBuilder builder = new StringBuilder(c);
builder.append(a).append(a);
}
long t4 = System.currentTimeMillis();
System.out.println(t4 - t3);
long t1 = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
String x = c + a + a;
}
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
結(jié)果可以看到基本是一樣的
24
19
測(cè)試for循環(huán)中append和+的運(yùn)行時(shí)間
public void test() {
String a = "a" + "b";
String c = "c" + a;
StringBuilder builder = new StringBuilder(c);
long t3 = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
builder.append(a);
}
long t4 = System.currentTimeMillis();
System.out.println(t4 - t3);
long t1 = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
c += a;
}
long t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
結(jié)果可以看到+的運(yùn)行時(shí)間特別的長(zhǎng)
9
7722