DataBinding


一、DataBinding

1.1 在Module的build.gradle android模塊中添加如下配置

代碼地址 https://github.com/MichealPan9999/DataBinding-MVVM

android {
 dataBinding {
    enabled = true
 }
}
  Android Studio中是依靠gradle來管理項目的,在創建一個項目時,從開始創建一直到創建完畢,整個過程是需要執行很多個gradle task的,這些task有很多是系統預先幫我們定義好的,比如build task,clean task等,DataBinding相關的task也是系統預先幫我們定義好的,但是默認情況下,DataBinding相關的task在task列表中是沒有的,因為我們沒有開啟dataBinding,但是一旦我們通過 dataBinding{enabled = true}的方式開啟DataBinding之后,DataBinding相關的task就會出現在task列表中,每當我們執行編譯之類的操作時,就會執行這些dataBinding Task, 這些task的作用就是檢查並生成相關dataBinding代碼,比如dataBindingExportBuildInfoDebug這個task就是用來導出debug模式下的build信息的。

1.2 創建一個簡單的JavaBean對象

public class UserBean {
    private String name; //姓名
    private int age; //年齡

    public UserBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

1.3 使用了DataBinding之后的Activity的布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.zx.databindingdemo.bean.UserBean" />
    </data>

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

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

        <!--注意:這里age是int類型,必須轉化為String,否則會運行時異常-->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(user.age)}" />
    </LinearLayout>
</layout>

1.4 MainActivity 

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        UserBean userBean = new UserBean ("張三", "25");
        binding.setUser(userBean );
    }
}
  這個activity很簡潔,沒有了控件的初始化的findViewById或者butterknife的那一堆注解,也沒有了TextView的setText(),也就2行代碼而已。大家應該已經看見了,這里用DataBindingUtil.setContentView代替了setContentView,然后創建一個 UserBean 對象,通過 binding.setUser(userBean) 與 variable 進行綁定。注意:這個ActivityMainBinding 是如何生成的呢?他是繼承ViewDataBinding,這個類的生成是有規則的,它是根據對應的布局文件的名字生成的,比如:activity_main-->ActivityMainBinding 、fragment-->FragmentBinding即:第一個單詞首字母大寫,第二個單詞首字母大寫,最后都會拼上Binding就是生成的Binding類。
  ActivityMainBinding這個類其實是系統幫我們自動生成的。
  但是如果你在實際編寫代碼的過程中,你會發現並沒有執行編譯、運行之類等操作,ActivityMainBinding這個類就直接能用了,竟然還有這種操作?其實是Android Studio 這個IDE自動幫我們做了這一步,在默認情況下,系統會使用Android Studio為我們自動生成databinding相關的代碼,但是這種方式生成的代碼不能調試,如果你想通過點擊ActivityMainBinding跳轉到它的源碼中,你會發現並不能如你所願,而是會跳轉到對應的布局文件中。那么如果我們確實要查看ActivityMainBinding的源碼並且還想調試,我們就需要通過另外一種方式:手動編譯代碼。這兩種方式可以通過Android Studio的設置面板修改。

1.5 引入一些高級變量variable

1.5.1 import導包及xml中添加運算

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <import type="java.util.List" />

        <import type="java.util.Map" />

        <import type="com.example.panzq.mvvm.bean.UserBean" />
        <import type="android.view.View" />

        <!--泛型的支持會在編譯時期報紅線,但是是可以直接運行的
       但是需要通過轉義字符才行,如:<號用&lt表示;>號用&gt表示;-->
        <variable
            name="list"
            type="List&lt;String&gt;" />

        <variable
            name="map"
            type="Map&lt;String,Object&gt;" />

        <variable
            name="array"
            type="String[]" />

        <variable
            name="muser"
            type="UserBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list[0]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{list.get(1)}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{map[`key0`]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{map.get(`key1`)}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="@{array[0]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{array[1]}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{muser.name}"
            android:visibility="@{((muser.age > 18) ? View.GONE:View.VISIBLE)}" />

    </LinearLayout>
</layout>
ActivityMain2Binding binding = DataBindingUtil.setContentView(this, R.layout.activity_main2);
        List<String> list = new ArrayList<>();
        list.add("List1");
        list.add("List2");
        binding.setList(list);
        HashMap<String, Object> map = new HashMap<>();
        map.put("key0", "map_value0");
        map.put("key1", "map_value1");
        binding.setMap(map);

        String[] array = {"字符串1", "字符串2"};
        binding.setArray(array);

        UserBean userBean = new UserBean("張三",18);
        binding.setMuser(userBean);

 

list和map這里我沒有用List<String>和Map<String,Object>,而是用的List<String>和Map<String,Object>原因是在data中,有些字符是必須用轉義字符才能編譯通過,上面把<>換成轉義字符的寫法雖然會在編譯時是紅色的,但是不用擔心,會編譯通過的,下面給出常用的轉義字符。

附:常用的轉義字符

顯示結果 描述 轉義字符 十進制
  空格 &nbsp; &#160;
< 小於號 &lt; &#60;
> 大於號 &gt; &#62;
& 與號 &amp; &#38;
" 引號 &quot; &#34;
撇號 &apos; &#39;
× 乘號 &times; &#215;
÷ 除號 &divide; &#247;

1.5.2 別名

  如果我們import了兩個不同路徑,但名稱相同的類,可以借助於別名來解決,別名借助alias字段來標識。

<import type="com.example.panzq.mvvm.bean.UserBean" />
<import type="com.example.panzq.mvvm.bean.UserBean" alias="UserBean2"/>
...
<variable
    name="muser"
    type="UserBean" />
<variable
    name="muser2"
    type="UserBean2" />
...
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{muser.name}"
    android:visibility="@{((muser.age > 18) ? View.GONE:View.VISIBLE)}" />
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{muser2.name}"
    android:visibility="@{((muser2.age > 18) ? View.GONE:View.VISIBLE)}" />
UserBean userBean = new UserBean("張三",18);
binding.setMuser(userBean);
UserBean userBean2 = new UserBean("李四",17);
binding.setMuser2(userBean2);

1.5.3 android:onClick事件處理

下面給出幾種實現方式:

  • 布局中引入OnClickListener的變量
  • 方法調用
<variable
            name="clickListener"
            type="android.view.View.OnClickListener" />
...
<Button
            android:id="@+id/click_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{clickListener}"
            android:text="button" />
binding.setClickListener(this);
...
@Override
    public void onClick(View view) {

        switch (view.getId()) {
            case R.id.click_btn:
                Toast.makeText(Main2Activity.this, "點擊了按鈕", Toast.LENGTH_SHORT).show();
                break;
        }
    }

二 DataBinding & RecyclerView

2.1 處理找不到符號  RecyclerView問題 

 import android.support.v7.widget.RecyclerView;時提示找不到符號RecyclerView

2.1.1 配置aar方法

1. 找到sdk目錄下的recyclerview-v7-****.aar文件,如:

D:\Program Files\androidstudio3\sdk2\extras\android\m2repository\com\android\support\recyclerview-v7\24.0.0\recyclerview-v7-24.0.0.aar

2. 將aar文件拷貝到項目的libs目錄下build.gradle中加載aar文件

apply plugin: 'com.android.application'

android {
compileSdkVersion 28


dataBinding {
enabled = true
}
defaultConfig {
applicationId "com.example.recyclerview"
minSdkVersion 22
targetSdkVersion 28
versionCode 1
versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

}
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'com.android.support:appcompat-v7:28.+'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
compile(name: 'recyclerview-v7-24.0.0', ext: 'aar')
}

執行完Sync Now以后便可以正常導包

import android.support.v7.widget.RecyclerView;

2.2 Adapter 配置

BaseBindRecyclerViewAdapter

public abstract class BaseBindRecyclerViewAdapter<T> extends RecyclerView.Adapter {
    public List<T> mList; //數據源
    public LayoutInflater inflater;

    public BaseBindRecyclerViewAdapter(Context context, List<T> mList) {
        this.mList = mList;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        return onCreateMyViewHolder(viewGroup, i);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
        onBindMyViewHolder(viewHolder, i);
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    //獲取Item布局
    public abstract RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType);

    //綁定數據
    public abstract void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position);
}

MutiItemAdapter

public class MultiItemAdapter extends BaseBindRecyclerViewAdapter<IBaseBindingAdapterItem> {
    public MultiItemAdapter(Context context, List<IBaseBindingAdapterItem> mList) {
        super(context, mList);
    }

    @Override
    public int getItemViewType(int position) {
        return mList.get(position).getItemViewType();
    }

    @Override
    public RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case R.layout.item_fruit:
                ItemFruitBinding itemFruitBinding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(itemFruitBinding);
            case R.layout.item_text:
                ItemTextBinding itemTextBinding = DataBindingUtil.inflate(inflater, R.layout.item_text, parent, false);
                return new TextViewHolder(itemTextBinding);
            default:
                ItemFruitBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(binding);
        }
    }

    @Override
    public void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof FruitViewHolder) {
            FruitItem fruitBean = (FruitItem) mList.get(position);
            ((FruitViewHolder) holder).getBinding().setItem(fruitBean);
            ((FruitViewHolder) holder).getBinding().executePendingBindings(); //解決databinding閃爍問題
        } else if (holder instanceof TextViewHolder) {
            TextItem textBean = (TextItem) mList.get(position);
            ((TextViewHolder) holder).getBinding().setItem(textBean);
            ((TextViewHolder) holder).getBinding().executePendingBindings(); //解決databinding閃爍問題
        }
    }


}

但是用了DataBinding以后,主要有3個地方發生了變化,ViewHolder, onCreateViewHolder,onBindMyViewHolder。

2.2.1 編寫ViewHolder

這里我的RecyclerView有兩種布局,並且布局全部是用databinding編寫的
class FruitViewHolder extends RecyclerView.ViewHolder {
    private ItemFruitBinding binding;

    public ItemFruitBinding getBinding() {
        return binding;
    }

    public FruitViewHolder(ItemFruitBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }
}
...
class TextViewHolder extends RecyclerView.ViewHolder {
    private ItemTextBinding binding;

    public ItemTextBinding getBinding() {
        return binding;
    }

    public TextViewHolder(ItemTextBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }
}
這里發生了一點變化,1.構造函數的參數不再是你item布局對應的View對象了,而是變成了你item布局對應的ViewDataBinding對象,這個名字是和你item布局的名字相關的,我這里的item是item_fruit.xml。2.里面寫了一個getBinding的方法,方便獲取binding對象。ViewHolder里面不需要其他操作,以前的那些findViewById去獲取控件對象都不用了,因為item布局里面已經和數據綁定了。
 

2.2.2 重寫onCreateViewHodler(ViewGroup parent,int viewType)

這里根據返回的viewType布局類型來創建不同的ViewHolder,viewType為getItemViewType(int position)方法返回的值:
@Override
    public RecyclerView.ViewHolder onCreateMyViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case R.layout.item_fruit:
                ItemFruitBinding itemFruitBinding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(itemFruitBinding);
            case R.layout.item_text:
                ItemTextBinding itemTextBinding = DataBindingUtil.inflate(inflater, R.layout.item_text, parent, false);
                return new TextViewHolder(itemTextBinding);
            default:
                ItemFruitBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_fruit, parent, false);
                return new FruitViewHolder(binding);
        }
    }

2.2.3.onBindViewHolder(RecyclerView.ViewHolder holder,int position)方法

@Override
    public void onBindMyViewHolder(RecyclerView.ViewHolder holder, int position) {//綁定數據
        if (holder instanceof FruitViewHolder) {
            FruitItem fruitBean = (FruitItem) mList.get(position);
            ((FruitViewHolder) holder).getBinding().setItem(fruitBean);
            ((FruitViewHolder) holder).getBinding().executePendingBindings(); //解決databinding閃爍問題
        } else if (holder instanceof TextViewHolder) {
            TextItem textBean = (TextItem) mList.get(position);
            ((TextViewHolder) holder).getBinding().setItem(textBean);
            ((TextViewHolder) holder).getBinding().executePendingBindings(); //解決databinding閃爍問題
        }
    }

2.3 MainAcitivity 填充數據

public class MainActivity extends AppCompatActivity {

    private MultiItemAdapter multiItemAdapter;
    private List<IBaseBindingAdapterItem> mList = new ArrayList<>(); //數據源
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
        initData();
        multiItemAdapter = new MultiItemAdapter(this,mList);//獲取填充的數據
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, OrientationHelper.VERTICAL,false);
        binding.recyclerView.setLayoutManager(layoutManager);
        binding.recyclerView.setAdapter(multiItemAdapter);//填充數據到R.id.recyclerView
    }
    private void initData() {
        mList.add(new TextItem("標題1"));
        mList.add(new FruitItem(R.mipmap.fruit, "蘋果"));
        mList.add(new FruitItem(R.mipmap.fruit, "香蕉"));
        mList.add(new TextItem("標題2"));
        mList.add(new TextItem("標題3"));
        mList.add(new FruitItem(R.mipmap.fruit, "桃子"));
        mList.add(new TextItem("標題4"));
        mList.add(new FruitItem(R.mipmap.fruit, "梨"));
        mList.add(new TextItem("標題5"));
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM