├── settings.gradle
├── .gitignore
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── app
├── src
│ ├── main
│ │ ├── assets
│ │ │ └── fonts
│ │ │ │ └── zhuanti.ttf
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── strings.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ ├── card.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── include.xml
│ │ │ │ ├── recycler_item.xml
│ │ │ │ ├── activity_view_stub.xml
│ │ │ │ ├── activity_normal_object.xml
│ │ │ │ ├── activity_observer.xml
│ │ │ │ ├── activity_observer_field.xml
│ │ │ │ ├── list_item.xml
│ │ │ │ ├── activity_attribute_setters.xml
│ │ │ │ ├── activity_converters.xml
│ │ │ │ ├── activity_event.xml
│ │ │ │ ├── activity_observer_collection.xml
│ │ │ │ ├── activity_demo.xml
│ │ │ │ ├── activity_two_way.xml
│ │ │ │ └── activity_combine.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── connorlin
│ │ │ │ └── databinding
│ │ │ │ ├── task
│ │ │ │ └── Task.java
│ │ │ │ ├── listener
│ │ │ │ └── ColorChangeListener.java
│ │ │ │ ├── handler
│ │ │ │ ├── ContextHandler.java
│ │ │ │ ├── Presenter.java
│ │ │ │ ├── EventHandler.java
│ │ │ │ └── DemoHandler.java
│ │ │ │ ├── utils
│ │ │ │ ├── MyStringUtils.java
│ │ │ │ └── Utils.java
│ │ │ │ ├── model
│ │ │ │ ├── ObservableFieldContact.java
│ │ │ │ ├── Contact.java
│ │ │ │ ├── RecyclerItem.java
│ │ │ │ ├── TwoWayModel.java
│ │ │ │ ├── User.java
│ │ │ │ ├── ObservableContact.java
│ │ │ │ └── FontFamily.java
│ │ │ │ ├── context
│ │ │ │ ├── EventActivity.java
│ │ │ │ ├── AttributeSettersActivity.java
│ │ │ │ ├── BaseActivity.java
│ │ │ │ ├── ObserverActivity.java
│ │ │ │ ├── NormalObjectActivity.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── ObserverFieldActivity.java
│ │ │ │ ├── DemoActivity.java
│ │ │ │ ├── TwoWayActivity.java
│ │ │ │ ├── CombineActivity.java
│ │ │ │ ├── ViewStubActivity.java
│ │ │ │ ├── ConvertersActivity.java
│ │ │ │ └── ObserverCollectionActivity.java
│ │ │ │ ├── view
│ │ │ │ ├── Card.java
│ │ │ │ └── TwoWayView.java
│ │ │ │ └── adapter
│ │ │ │ ├── ListAdapter.java
│ │ │ │ └── RecyclerAdapter.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── connorlin
│ │ │ └── databinding
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── connorlin
│ │ └── databinding
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | .idea
5 | .DS_Store
6 | /build
7 | /captures
8 | /local.properties
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/assets/fonts/zhuanti.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/app/src/main/assets/fonts/zhuanti.ttf
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/app/src/main/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ConnorLin/DataBindingDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/task/Task.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.task;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * ClassName: Task
7 | * Description:
8 | * Author: connorlin
9 | * Date: Created on 2016-7-1.
10 | */
11 | public class Task implements Runnable {
12 |
13 | @Override
14 | public void run() {
15 | Log.i("connor", "Task run");
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/listener/ColorChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.listener;
2 |
3 | import com.connorlin.databinding.view.TwoWayView;
4 |
5 | /**
6 | * ClassName: ColorChangeListener
7 | * Description:
8 | * Author: connorlin
9 | * Date: Created on 2016/7/2.
10 | */
11 | public interface ColorChangeListener {
12 | void onColorChange(TwoWayView view, int color);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/test/java/com/connorlin/databinding/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/connorlin/databinding/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/handler/ContextHandler.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.handler;
2 |
3 | import android.content.Context;
4 |
5 | import com.connorlin.databinding.R;
6 |
7 | /**
8 | * ClassName: ContextHandler
9 | * Description:
10 | * Author: connorlin
11 | * Date: Created on 2016-6-30.
12 | */
13 | public class ContextHandler {
14 |
15 | public String loadString(Context context) {
16 | return context.getResources().getString(R.string.string_from_context);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/utils/MyStringUtils.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.utils;
2 |
3 | /**
4 | * ClassName: MyStringUtils
5 | * Description:
6 | * Author: connorlin
7 | * Date: Created on 2016-6-29.
8 | */
9 | public class MyStringUtils {
10 |
11 | // 首字母大写
12 | public static String capitalize(final String word) {
13 | if (word.length() > 1) {
14 | return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);
15 | }
16 | return word;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/handler/Presenter.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.handler;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import com.connorlin.databinding.model.RecyclerItem;
7 |
8 | /**
9 | * ClassName: Presentr
10 | * Description:
11 | * Author: connorlin
12 | * Date: Created on 2016-7-1.
13 | */
14 | public class Presenter {
15 |
16 | public void onTypeClick(Context context, RecyclerItem recyclerItem) {
17 | Intent intent = new Intent(recyclerItem.getAction());
18 | context.startActivity(intent);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/model/ObservableFieldContact.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.model;
2 |
3 | import android.databinding.ObservableField;
4 |
5 | /**
6 | * ClassName: PlainContact
7 | * Description:
8 | * Author: connorlin
9 | * Date: Created on 2016-6-29.
10 | */
11 | public class ObservableFieldContact {
12 | public ObservableField mName = new ObservableField<>();
13 | public ObservableField mPhone = new ObservableField<>();
14 |
15 | public ObservableFieldContact(String name, String phone) {
16 | mName.set(name);
17 | mPhone.set(phone);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/card.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
17 |
21 |
22 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/connorlin/Program/SDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/model/Contact.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.model;
2 |
3 | /**
4 | * ClassName: BaseContact
5 | * Description:
6 | * Author: connorlin
7 | * Date: Created on 2016-6-29.
8 | */
9 | public class Contact {
10 |
11 | private String mName;
12 | private String mPhone;
13 |
14 | public Contact(String name, String phone) {
15 | mName = name;
16 | mPhone = phone;
17 | }
18 |
19 | public String getName() {
20 | return mName;
21 | }
22 |
23 | public void setName(String name) {
24 | mName = name;
25 | }
26 |
27 | public String getPhone() {
28 | return mPhone;
29 | }
30 |
31 | public void setPhone(String phone) {
32 | mPhone = phone;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/model/RecyclerItem.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.model;
2 |
3 | /**
4 | * ClassName: Item
5 | * Description:
6 | * Author: connorlin
7 | * Date: Created on 2016-7-1.
8 | */
9 | public class RecyclerItem {
10 |
11 | private String mType;
12 | private String mAction;
13 |
14 | public RecyclerItem(String type, String action) {
15 | mType = type;
16 | mAction = action;
17 | }
18 |
19 | public String getType() {
20 | return mType;
21 | }
22 |
23 | public void setType(String type) {
24 | mType = type;
25 | }
26 |
27 | public String getAction() {
28 | return mAction;
29 | }
30 |
31 | public void setAction(String action) {
32 | mAction = action;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/include.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
15 |
16 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/model/TwoWayModel.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.model;
2 |
3 | import android.databinding.ObservableInt;
4 | import android.graphics.Color;
5 |
6 | import com.connorlin.databinding.listener.ColorChangeListener;
7 | import com.connorlin.databinding.view.TwoWayView;
8 |
9 | /**
10 | * ClassName: TwoWayHandler
11 | * Description:
12 | * Author: connorlin
13 | * Date: Created on 2016/7/2.
14 | */
15 | public class TwoWayModel {
16 |
17 | public ObservableInt mColor = new ObservableInt(Color.GRAY);
18 |
19 | public ColorChangeListener onColorChangeListener() {
20 | return new ColorChangeListener() {
21 | @Override
22 | public void onColorChange(TwoWayView view, int color) {
23 | view.setBackgroundColor(Color.YELLOW);
24 | }
25 | };
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/EventActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 |
6 | import com.connorlin.databinding.R;
7 | import com.connorlin.databinding.databinding.ActivityEventBinding;
8 | import com.connorlin.databinding.handler.EventHandler;
9 | import com.connorlin.databinding.task.Task;
10 |
11 | public class EventActivity extends BaseActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | // setContentView(R.layout.activity_event);
17 | ActivityEventBinding binding =
18 | DataBindingUtil.setContentView(this, R.layout.activity_event);
19 |
20 | binding.setHandler(new EventHandler(this));
21 | binding.setTask(new Task());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.connorlin.databinding"
9 | minSdkVersion 17
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 | dataBinding {
22 | enabled true
23 | }
24 | }
25 |
26 | dependencies {
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 | testCompile 'junit:junit:4.12'
29 | compile 'com.android.support:appcompat-v7:23.4.0'
30 | compile 'com.android.support:recyclerview-v7:24.0.0'
31 | compile 'com.github.bumptech.glide:glide:3.7.0'
32 | }
33 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.utils;
2 |
3 | import android.databinding.BindingAdapter;
4 | import android.graphics.drawable.Drawable;
5 | import android.widget.ImageView;
6 | import android.widget.TextView;
7 |
8 | import com.bumptech.glide.Glide;
9 | import com.connorlin.databinding.model.FontFamily;
10 |
11 | /**
12 | * ClassName: Utils
13 | * Description:
14 | * Author: connorlin
15 | * Date: Created on 2016/7/2.
16 | */
17 | public class Utils {
18 |
19 | // 使用注解,无需手动调用此函数
20 | @BindingAdapter({"imageUrl", "error"})
21 | public static void loadImage(ImageView view, String url, Drawable error) {
22 | Glide.with(view.getContext()).load(url).error(error).into(view);
23 | }
24 |
25 | @BindingAdapter({"font"})
26 | public static void setFont(TextView textView, String fontName) {
27 | textView.setTypeface(FontFamily.getInstance().getFont(fontName, textView.getContext()));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/AttributeSettersActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 |
6 | import com.connorlin.databinding.R;
7 | import com.connorlin.databinding.databinding.ActivityAttributeSettersBinding;
8 | import com.connorlin.databinding.model.User;
9 |
10 | public class AttributeSettersActivity extends BaseActivity {
11 |
12 | private ActivityAttributeSettersBinding mBinding;
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | // setContentView(R.layout.activity_attribute_setters);
18 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_attribute_setters);
19 |
20 | User user = new User("Connor", "Lin", 28);
21 | mBinding.setUser(user);
22 |
23 | mBinding.setUrl("http://connorlin.github.io/images/avatar.jpg");
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.ActionBar;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.MenuItem;
7 |
8 | public class BaseActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 |
14 | ActionBar actionBar = getSupportActionBar();
15 | if (actionBar != null) {
16 | actionBar.setDisplayHomeAsUpEnabled(true);
17 | actionBar.setDisplayShowTitleEnabled(true);
18 | }
19 | }
20 |
21 | @Override
22 | public boolean onOptionsItemSelected(MenuItem item) {
23 | switch (item.getItemId()) {
24 | case android.R.id.home:
25 | finish();
26 | break;
27 | }
28 | return super.onOptionsItemSelected(item);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
12 |
13 |
17 |
18 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/handler/EventHandler.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.handler;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.widget.Toast;
6 |
7 | import com.connorlin.databinding.task.Task;
8 |
9 | /**
10 | * ClassName: ViewHandler
11 | * Description:
12 | * Author: connorlin
13 | * Date: Created on 2016-7-1.
14 | */
15 | public class EventHandler {
16 |
17 | private Context mContext;
18 |
19 | public EventHandler(Context context) {
20 | mContext = context;
21 | }
22 |
23 | public void onClickFriend(View view) {
24 | Toast.makeText(mContext, "onClickFriend", Toast.LENGTH_LONG).show();
25 | }
26 |
27 | public void onTaskClick(Task task) {
28 | task.run();
29 | }
30 |
31 | public void onTaskClickWithParams(View view, Task task) {
32 | task.run();
33 | }
34 |
35 | public void onCompletedChanged(Task task, boolean completed) {
36 | if(completed) {
37 | task.run();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/model/User.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.model;
2 |
3 | /**
4 | * ClassName: User
5 | * Description:
6 | * Author: connorlin
7 | * Date: Created on 2016-6-29.
8 | */
9 | public class User {
10 | private final String mFirstName;
11 | private final String mLastName;
12 | private int mAge;
13 |
14 | public User(String firstName, String lastName, int age) {
15 | this(firstName, lastName);
16 | mAge = age;
17 | }
18 |
19 | public User(String firstName, String lastName) {
20 | mFirstName = firstName;
21 | mLastName = lastName;
22 | }
23 |
24 | public String getFirstName() {
25 | return mFirstName;
26 | }
27 |
28 | public String getLastName() {
29 | return mLastName;
30 | }
31 |
32 | public String getFullName() {
33 | return mFirstName + " " + mLastName;
34 | }
35 |
36 | public int getAge() {
37 | return mAge;
38 | }
39 |
40 | public boolean isAdult() {
41 | return mAge >= 18;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/model/ObservableContact.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.model;
2 |
3 | import android.databinding.BaseObservable;
4 | import android.databinding.Bindable;
5 |
6 | import com.connorlin.databinding.BR;
7 |
8 | /**
9 | * ClassName: Contact
10 | * Description:
11 | * Author: connorlin
12 | * Date: Created on 2016-6-28.
13 | */
14 | public class ObservableContact extends BaseObservable {
15 | private String mName;
16 | private String mPhone;
17 |
18 | public ObservableContact(String name, String phone) {
19 | mName = name;
20 | mPhone = phone;
21 | }
22 |
23 | @Bindable
24 | public String getName() {
25 | return mName;
26 | }
27 |
28 | public void setName(String name) {
29 | mName = name;
30 | notifyPropertyChanged(BR.name);
31 | }
32 |
33 | @Bindable
34 | public String getPhone() {
35 | return mPhone;
36 | }
37 |
38 | public void setPhone(String phone) {
39 | mPhone = phone;
40 | notifyPropertyChanged(BR.phone);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/ObserverActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import com.connorlin.databinding.R;
8 | import com.connorlin.databinding.databinding.ActivityObserverBinding;
9 | import com.connorlin.databinding.model.ObservableContact;
10 |
11 | public class ObserverActivity extends BaseActivity {
12 |
13 | private ActivityObserverBinding mBinding;
14 | private ObservableContact mObservableContact;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | // setContentView(R.layout.activity_observer);
20 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_observer);
21 |
22 | mObservableContact = new ObservableContact("police","110");
23 | mBinding.setContact(mObservableContact);
24 | }
25 |
26 | public void onClick(View view) {
27 | mObservableContact.setName("ConnorLin");
28 | mObservableContact.setPhone("12345678901");
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/NormalObjectActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | import com.connorlin.databinding.R;
9 | import com.connorlin.databinding.databinding.ActivityNormalObjectBinding;
10 | import com.connorlin.databinding.model.Contact;
11 |
12 | public class NormalObjectActivity extends BaseActivity {
13 |
14 | private ActivityNormalObjectBinding mBinding;
15 | private Contact mContact;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | // setContentView(R.layout.activity_normal_object);
21 |
22 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_normal_object);
23 |
24 | mContact = new Contact("police", "110");
25 | mBinding.setContact(mContact);
26 | }
27 |
28 | public void onClick(View view) {
29 | // 更新普通java bean 对象并不会同步更新
30 | mContact.setName("ConnorLin");
31 | mContact.setPhone("12345678901");
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.DefaultItemAnimator;
7 | import android.support.v7.widget.LinearLayoutManager;
8 |
9 | import com.connorlin.databinding.R;
10 | import com.connorlin.databinding.adapter.RecyclerAdapter;
11 | import com.connorlin.databinding.databinding.ActivityMainBinding;
12 |
13 | public class MainActivity extends AppCompatActivity {
14 |
15 | private ActivityMainBinding mBinding;
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | // setContentView(R.layout.activity_main);
20 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
21 |
22 | mBinding.recyclerView.setHasFixedSize(true);
23 | mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
24 | mBinding.recyclerView.setItemAnimator(new DefaultItemAnimator());
25 |
26 | RecyclerAdapter mRecyclerAdapter = new RecyclerAdapter();
27 | mBinding.recyclerView.setAdapter(mRecyclerAdapter);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/ObserverFieldActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | import com.connorlin.databinding.R;
9 | import com.connorlin.databinding.databinding.ActivityObserverFieldBinding;
10 | import com.connorlin.databinding.model.ObservableFieldContact;
11 |
12 | public class ObserverFieldActivity extends AppCompatActivity {
13 |
14 | ActivityObserverFieldBinding mBinding;
15 | private ObservableFieldContact mObservableFieldContact;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | // setContentView(R.layout.activity_observer_field);
21 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_observer_field);
22 |
23 | mObservableFieldContact = new ObservableFieldContact("police","110");
24 | mBinding.setContact(mObservableFieldContact);
25 | }
26 |
27 | public void onClick(View view) {
28 | mObservableFieldContact.mName.set("ConnorLin");
29 | mObservableFieldContact.mPhone.set("12345678901");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/DemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 |
6 | import com.connorlin.databinding.DemoBinding;
7 | import com.connorlin.databinding.R;
8 | import com.connorlin.databinding.adapter.ListAdapter;
9 | import com.connorlin.databinding.handler.DemoHandler;
10 | import com.connorlin.databinding.model.ObservableContact;
11 |
12 | public class DemoActivity extends BaseActivity {
13 |
14 | private DemoBinding mDemoBinding;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 |
20 | mDemoBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo);
21 | ObservableContact contact = new ObservableContact("", "");
22 | mDemoBinding.setContact(contact);
23 |
24 | initViews();
25 | }
26 |
27 | private void initViews() {
28 | mDemoBinding.listView.setEmptyView(mDemoBinding.emptyView);
29 |
30 | ListAdapter listAdapter = new ListAdapter(this);
31 | mDemoBinding.listView.setAdapter(listAdapter);
32 |
33 | DemoHandler viewHandler = new DemoHandler(this, mDemoBinding, listAdapter);
34 | mDemoBinding.setHandler(viewHandler);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_view_stub.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
18 |
19 |
24 |
25 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DataBindingDemo
3 | Empty
4 | Add contacts
5 | Phone
6 |
7 | FirstName: %1$s
8 | LastName: %1$s
9 | FullName: %1$s
10 | String from context
11 | Method Preference
12 | Listener binding empty params
13 | Listener binding arbitrary params
14 | Listener binding params
15 | Inflate the viewstub
16 | Converter
17 |
18 |
19 | - zero
20 | - one
21 | - other
22 |
23 |
24 |
25 | - Have an number
26 | - Have %1$d numbers
27 |
28 |
29 | 白舟篆古印
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_normal_object.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
20 |
21 |
27 |
28 |
32 |
33 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_observer.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
20 |
21 |
27 |
28 |
32 |
33 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_observer_field.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
20 |
21 |
27 |
28 |
32 |
33 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
18 |
19 |
26 |
27 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/view/Card.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.view;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.LinearLayout;
6 | import android.widget.TextView;
7 |
8 | import com.connorlin.databinding.R;
9 | import com.connorlin.databinding.model.User;
10 |
11 | /**
12 | * ClassName: Card
13 | * Description:
14 | * Author: connorlin
15 | * Date: Created on 2016/7/2.
16 | */
17 | public class Card extends LinearLayout {
18 |
19 | private TextView mFirstName;
20 | private TextView mLastName;
21 | private TextView mAge;
22 |
23 | public Card(Context context) {
24 | this(context, null);
25 | }
26 |
27 | public Card(Context context, AttributeSet attrs) {
28 | this(context, attrs, 0);
29 | }
30 |
31 | public Card(Context context, AttributeSet attrs, int defStyleAttr) {
32 | super(context, attrs, defStyleAttr);
33 |
34 | inflate(context, R.layout.card, this);
35 | setOrientation(VERTICAL);
36 |
37 | mFirstName = (TextView) findViewById(R.id.firstname);
38 | mLastName = (TextView) findViewById(R.id.lastname);
39 | mAge = (TextView) findViewById(R.id.age);
40 | }
41 |
42 | // 自动 Setter
43 | public void setObject(User user) {
44 | mFirstName.setText(user.getFirstName());
45 | mLastName.setText(user.getLastName());
46 | mAge.setText(String.valueOf(user.getAge()));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/TwoWayActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.view.View;
6 |
7 | import com.connorlin.databinding.R;
8 | import com.connorlin.databinding.TwoWayBinding;
9 | import com.connorlin.databinding.model.ObservableFieldContact;
10 |
11 | public class TwoWayActivity extends BaseActivity {
12 |
13 | private TwoWayBinding mBinding;
14 | private ObservableFieldContact mFieldContact;
15 | // private TwoWayModel mTwoWayModel;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | // setContentView(R.layout.activity_two_way);
21 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_two_way);
22 |
23 | mFieldContact = new ObservableFieldContact("Connor","Lin");
24 | mBinding.setContact(mFieldContact);
25 |
26 | // mTwoWayModel = new TwoWayModel();
27 | // mBinding.setModel(mTwoWayModel);
28 | }
29 |
30 | public void onClick(View view) {
31 | // 改变 Model,将同时改变 UI
32 | mFieldContact.mName.set("two-way");
33 | }
34 |
35 | // public void updateModel(View view) {
36 | // mTwoWayModel.mColor.set(Color.RED);
37 | // }
38 | //
39 | // public void updateView(View view) {
40 | // mBinding.twowayView.setColor(Color.GREEN);
41 | // mTwoWayModel.mColor.set(mBinding.twowayView.getColor());
42 | // }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/model/FontFamily.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.model;
2 |
3 | import android.content.Context;
4 | import android.graphics.Typeface;
5 |
6 | import java.util.HashMap;
7 |
8 | /**
9 | * ClassName: FontFamily
10 | * Description:
11 | * Author: connorlin
12 | * Date: Created on 2016-7-4.
13 | */
14 | public class FontFamily {
15 | HashMap mFontMap = new HashMap<>();
16 | HashMap mFontCache = new HashMap<>();
17 |
18 | public FontFamily() {
19 | addFont("zhuanti", "zhuanti.ttf");
20 | }
21 |
22 | public void addFont(String alias, String fontName) {
23 | mFontMap.put(alias, fontName);
24 | }
25 |
26 | public static FontFamily getInstance() {
27 | return Singleton.instance;
28 | }
29 |
30 | public Typeface getFont(String alias, Context context) {
31 | String fontFilename = mFontMap.get(alias);
32 | if (fontFilename == null) {
33 | return null;
34 | }
35 |
36 | if (mFontCache.containsKey(alias)) {
37 | return mFontCache.get(alias);
38 | } else {
39 | Typeface typeface =
40 | Typeface.createFromAsset(context.getApplicationContext().getAssets(),
41 | "fonts/" + fontFilename);
42 | mFontCache.put(fontFilename, typeface);
43 | return typeface;
44 | }
45 | }
46 |
47 | private static class Singleton {
48 | private static FontFamily instance = new FontFamily();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_attribute_setters.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
14 |
15 |
16 |
20 |
21 |
22 |
26 |
27 |
28 |
33 |
34 |
35 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/CombineActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 |
6 | import com.connorlin.databinding.R;
7 | import com.connorlin.databinding.databinding.ActivityCombineBinding;
8 | import com.connorlin.databinding.handler.ContextHandler;
9 | import com.connorlin.databinding.model.User;
10 |
11 | import java.util.ArrayList;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | public class CombineActivity extends BaseActivity {
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | // setContentView(R.layout.activity_combine);
22 |
23 | ActivityCombineBinding baseBinding =
24 | DataBindingUtil.setContentView(this, R.layout.activity_combine);
25 |
26 | User user = new User("connor", "lin", 28);
27 | baseBinding.setUser(user);
28 |
29 | baseBinding.setTest("Java lang");
30 | baseBinding.setNum(1);
31 |
32 | // 容器类
33 | List mUserList = new ArrayList<>();
34 | mUserList.add(user);
35 | mUserList.add(user);
36 | mUserList.add(user);
37 | baseBinding.setUserList(mUserList);
38 |
39 | Map map = new HashMap<>();
40 | map.put("1", "map1");
41 | map.put("2", "map2");
42 | map.put("3", "map3");
43 | baseBinding.setMap(map);
44 |
45 | // Context
46 | ContextHandler handler = new ContextHandler();
47 | baseBinding.setHandler(handler);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/ViewStubActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 | import android.view.ViewStub;
8 |
9 | import com.connorlin.databinding.R;
10 | import com.connorlin.databinding.databinding.ActivityViewStubBinding;
11 | import com.connorlin.databinding.databinding.IncludeBinding;
12 | import com.connorlin.databinding.model.User;
13 |
14 | import java.util.Map;
15 |
16 | public class ViewStubActivity extends BaseActivity {
17 |
18 | private ActivityViewStubBinding mActivityViewStubBinding;
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | // setContentView(R.layout.activity_view_stub);
24 | mActivityViewStubBinding =
25 | DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
26 | mActivityViewStubBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
27 | @Override
28 | public void onInflate(ViewStub stub, View inflated) {
29 | IncludeBinding viewStubBinding = DataBindingUtil.bind(inflated);
30 | User user = new User("Connor", "Lin", 28);
31 | viewStubBinding.setUser(user);
32 | }
33 | });
34 | }
35 |
36 | // 此处会标红,请不要担心,这并不是错误,标红方法是 ViewStubProxy 里的方法。
37 | public void inflate(View view) {
38 | if (!mActivityViewStubBinding.viewStub.isInflated()) {
39 | mActivityViewStubBinding.viewStub.getViewStub().inflate();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/handler/DemoHandler.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.handler;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.view.inputmethod.InputMethodManager;
6 |
7 | import com.connorlin.databinding.DemoBinding;
8 | import com.connorlin.databinding.adapter.ListAdapter;
9 | import com.connorlin.databinding.model.ObservableContact;
10 |
11 | /**
12 | * ClassName: ViewHandler
13 | * Description:
14 | * Author: connorlin
15 | * Date: Created on 2016-6-28.
16 | */
17 | public class DemoHandler {
18 |
19 | private Context mContext;
20 | private DemoBinding mDemoBinding;
21 | private ListAdapter mListAdapter;
22 |
23 | public DemoHandler(Context context, DemoBinding mainBinding,
24 | ListAdapter listAdapter) {
25 | mContext = context;
26 | mDemoBinding = mainBinding;
27 | mListAdapter = listAdapter;
28 | }
29 |
30 | public void onClick(View v) {
31 | ObservableContact contact = new ObservableContact(mDemoBinding.editName.getText().toString(),
32 | mDemoBinding.editPhone.getText().toString());
33 | mListAdapter.addContact(contact);
34 |
35 | reset();
36 |
37 | hideInput();
38 | }
39 |
40 | private void reset() {
41 | mDemoBinding.getContact().setName("");
42 | mDemoBinding.getContact().setPhone("");
43 | }
44 |
45 | private void hideInput() {
46 | InputMethodManager mInputMethodManager =
47 | (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
48 | mInputMethodManager.hideSoftInputFromWindow(mDemoBinding.editPhone.getWindowToken(),
49 | 0);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/ConvertersActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.BindingConversion;
4 | import android.databinding.DataBindingUtil;
5 | import android.databinding.ObservableBoolean;
6 | import android.graphics.drawable.ColorDrawable;
7 | import android.os.Bundle;
8 | import android.view.View;
9 |
10 | import com.connorlin.databinding.R;
11 | import com.connorlin.databinding.databinding.ActivityConvertersBinding;
12 | import com.connorlin.databinding.model.User;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | public class ConvertersActivity extends BaseActivity {
18 |
19 | private ObservableBoolean mIsError = new ObservableBoolean();
20 |
21 | ActivityConvertersBinding mBinding;
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | // setContentView(R.layout.activity_converters);
27 |
28 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_converters);
29 |
30 | List usrList = new ArrayList<>();
31 | usrList.add(new User("Connor", "Lin", 28));
32 | mBinding.setList(usrList);
33 |
34 | mIsError.set(true);
35 | mBinding.setIsError(mIsError);
36 | }
37 |
38 | public void onToggle(View view) {
39 | mIsError.set(!mIsError.get());
40 | }
41 |
42 | // 方法名可自定义,只需关心参数 User
43 | @BindingConversion
44 | public static String convertUserToCharSequence(User user) {
45 | return user.getFullName();
46 | }
47 |
48 | @BindingConversion
49 | public static ColorDrawable convertColorToDrawable(int color) {
50 | return new ColorDrawable(color);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_converters.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
18 |
19 |
20 |
29 |
30 |
34 |
35 |
41 |
42 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/context/ObserverCollectionActivity.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.context;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.databinding.ObservableArrayList;
5 | import android.databinding.ObservableArrayMap;
6 | import android.os.Bundle;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.view.View;
9 |
10 | import com.connorlin.databinding.R;
11 | import com.connorlin.databinding.databinding.ActivityObserverCollectionBinding;
12 |
13 | public class ObserverCollectionActivity extends BaseActivity {
14 |
15 | private ActivityObserverCollectionBinding mBinding;
16 |
17 | private ObservableArrayMap mUserMap;
18 | private ObservableArrayList mUserList;
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | // setContentView(R.layout.activity_observer_collection);
24 | mBinding = DataBindingUtil.setContentView(this, R.layout.activity_observer_collection);
25 |
26 | mUserMap = new ObservableArrayMap<>();
27 | mUserMap.put("firstName", "Connor");
28 | mUserMap.put("lastName", "Lin");
29 | mUserMap.put("age", "28");
30 | mBinding.setUser(mUserMap);
31 |
32 | mUserList = new ObservableArrayList<>();
33 | mUserList.add(mUserMap.valueAt(0));
34 | mUserList.add(mUserMap.valueAt(1));
35 | mUserList.add(mUserMap.valueAt(2));
36 | mBinding.setUserList(mUserList);
37 | }
38 |
39 | public void onClick(View view) {
40 | mUserMap.put("firstName", "Google");
41 | mUserMap.put("lastName", "Inc.");
42 | mUserMap.put("age", "17");
43 |
44 | mUserList.set(0, mUserMap.valueAt(0));
45 | mUserList.set(1, mUserMap.valueAt(1));
46 | mUserList.set(2, mUserMap.valueAt(2));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_event.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
29 |
30 |
31 |
38 |
39 |
40 |
47 |
48 |
49 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/adapter/ListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.adapter;
2 |
3 | import android.content.Context;
4 | import android.databinding.DataBindingUtil;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.BaseAdapter;
9 |
10 | import com.connorlin.databinding.R;
11 | import com.connorlin.databinding.databinding.ListItemBinding;
12 | import com.connorlin.databinding.model.ObservableContact;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /**
18 | * ClassName: ListAdapter
19 | * Description:
20 | * Author: connorlin
21 | * Date: Created on 2016-6-28.
22 | */
23 | public class ListAdapter extends BaseAdapter {
24 |
25 | private final LayoutInflater mInflater;
26 |
27 | private List mContactsList = new ArrayList<>();
28 |
29 | public ListAdapter(Context context) {
30 | mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
31 | }
32 |
33 | public void addContact(ObservableContact contact) {
34 | mContactsList.add(contact);
35 | notifyDataSetChanged();
36 | }
37 |
38 | @Override
39 | public int getCount() {
40 | return mContactsList.size();
41 | }
42 |
43 | @Override
44 | public ObservableContact getItem(int position) {
45 | return mContactsList.get(position);
46 | }
47 |
48 | @Override
49 | public long getItemId(int position) {
50 | return 0;
51 | }
52 |
53 | @Override
54 | public View getView(int position, View convertView, ViewGroup parent) {
55 | final ViewHolder holder;
56 |
57 | if (convertView == null) {
58 | holder = new ViewHolder();
59 | convertView = mInflater.inflate(R.layout.list_item, parent, false);
60 | holder.listItemBinding = DataBindingUtil.bind(convertView);
61 | convertView.setTag(holder);
62 | } else {
63 | holder = (ViewHolder) convertView.getTag();
64 | }
65 |
66 | holder.listItemBinding.setContact(getItem(position));
67 |
68 | return convertView;
69 | }
70 |
71 | class ViewHolder {
72 | ListItemBinding listItemBinding;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_observer_collection.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
30 |
31 |
37 |
38 |
42 |
43 |
47 |
48 |
52 |
53 |
54 |
55 |
60 |
61 |
66 |
67 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/adapter/RecyclerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.adapter;
2 |
3 | import android.databinding.DataBindingUtil;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.connorlin.databinding.BR;
10 | import com.connorlin.databinding.R;
11 | import com.connorlin.databinding.databinding.RecyclerItemBinding;
12 | import com.connorlin.databinding.handler.Presenter;
13 | import com.connorlin.databinding.model.RecyclerItem;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * ClassName: RecyclerAdapter
20 | * Description:
21 | * Author: connorlin
22 | * Date: Created on 2016-7-1.
23 | */
24 | public class RecyclerAdapter extends RecyclerView.Adapter {
25 |
26 | private static final String ACTION_PRE = "connorlin.databinding.action.";
27 |
28 | private String[] mType = new String[]{
29 | "Combine",
30 | "NormalObject",
31 | "Observer",
32 | "ObserverField",
33 | "ObserverCollection",
34 | "ViewStub",
35 | "Event",
36 | "AttributeSetters",
37 | "Converters",
38 | "Demo",
39 | "TwoWay"
40 | };
41 |
42 | private List mRecyclerItemList = new ArrayList<>();
43 |
44 | public RecyclerAdapter() {
45 | mRecyclerItemList.clear();
46 | for(String str : mType) {
47 | RecyclerItem mRecyclerItem = new RecyclerItem(str, ACTION_PRE + str);
48 | mRecyclerItemList.add(mRecyclerItem);
49 | }
50 | }
51 |
52 | @Override
53 | public BindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
54 | RecyclerItemBinding binding =
55 | DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
56 | R.layout.recycler_item, parent, false);
57 |
58 | Presenter presenter = new Presenter();
59 | binding.setPresenter(presenter);
60 |
61 | BindingHolder holder = new BindingHolder(binding.getRoot());
62 | holder.setBinding(binding);
63 | return holder;
64 | }
65 |
66 | @Override
67 | public void onBindViewHolder(BindingHolder holder, int position) {
68 | // 动态绑定变量
69 | holder.getBinding().setVariable(BR.item, mRecyclerItemList.get(position));
70 | holder.getBinding().executePendingBindings();
71 | }
72 |
73 | @Override
74 | public int getItemCount() {
75 | return mRecyclerItemList.size();
76 | }
77 |
78 | public class BindingHolder extends RecyclerView.ViewHolder {
79 | private RecyclerItemBinding binding;
80 |
81 | public BindingHolder(View itemView) {
82 | super(itemView);
83 | }
84 |
85 | public RecyclerItemBinding getBinding() {
86 | return binding;
87 | }
88 |
89 | public void setBinding(RecyclerItemBinding binding) {
90 | this.binding = binding;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
16 |
17 |
18 |
25 |
26 |
36 |
37 |
47 |
48 |
54 |
55 |
66 |
67 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_two_way.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
25 |
26 |
32 |
33 |
34 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connorlin/databinding/view/TwoWayView.java:
--------------------------------------------------------------------------------
1 | package com.connorlin.databinding.view;
2 |
3 | import android.content.Context;
4 | import android.databinding.BindingAdapter;
5 | import android.databinding.InverseBindingAdapter;
6 | import android.databinding.InverseBindingListener;
7 | import android.databinding.InverseBindingMethod;
8 | import android.databinding.InverseBindingMethods;
9 | import android.databinding.adapters.ListenerUtil;
10 | import android.util.AttributeSet;
11 | import android.view.View;
12 |
13 | import com.connorlin.databinding.listener.ColorChangeListener;
14 |
15 | /**
16 | * ClassName: TwoWay
17 | * Description:
18 | * Author: connorlin
19 | * Date: Created on 2016/7/2.
20 | */
21 |
22 |
23 | @InverseBindingMethods({
24 | @InverseBindingMethod(
25 | type = TwoWayView.class,
26 | attribute = "color",
27 | event = "colorAttrChanged", // 不是必须的,仅作标记用
28 | method = "getColor" // 不是必须的,仅作标记用
29 | )
30 | })
31 | public class TwoWayView extends View {
32 |
33 | private int mColor;
34 | private ColorChangeListener mColorChangeListener;
35 |
36 | public TwoWayView(Context context) {
37 | this(context, null);
38 | }
39 |
40 | public TwoWayView(Context context, AttributeSet attrs) {
41 | this(context, attrs, 0);
42 | }
43 |
44 | public TwoWayView(Context context, AttributeSet attrs, int defStyleAttr) {
45 | super(context, attrs, defStyleAttr);
46 | }
47 |
48 | public void setColor(int color) {
49 | mColor = color;
50 | }
51 |
52 | public int getColor() {
53 | return mColor;
54 | }
55 |
56 | @InverseBindingAdapter(attribute = "color", event = "colorAttrChanged")
57 | public static int getColor(TwoWayView view) {
58 | return view.getColor();
59 | }
60 |
61 | @BindingAdapter("color")
62 | public static void setColor(TwoWayView view, int color) {
63 | if (color != view.getColor()) {
64 | view.setColor(color);
65 | view.setBackgroundColor(color);
66 | }
67 | }
68 |
69 | public void setOnColorChangeListener(ColorChangeListener listener) {
70 | mColorChangeListener = listener;
71 | }
72 |
73 | @BindingAdapter(value = {"colorChangeListener", "colorAttrChanged"}, requireAll = false)
74 | public static void setColorListener(TwoWayView view,
75 | final ColorChangeListener listener,
76 | final InverseBindingListener colorChange) {
77 |
78 | ColorChangeListener newValue = new ColorChangeListener() {
79 | @Override
80 | public void onColorChange(TwoWayView view, int color) {
81 | if (listener != null) {
82 | listener.onColorChange(view, color);
83 | }
84 |
85 | if (colorChange != null) {
86 | colorChange.onChange();
87 | }
88 | }
89 | };
90 |
91 | ColorChangeListener oldValue =
92 | ListenerUtil.trackListener(view, newValue, view.getId());
93 |
94 | if (oldValue != null) {
95 | view.setOnColorChangeListener(null);
96 | }
97 | view.setOnColorChangeListener(newValue);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
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 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_combine.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
39 |
40 |
41 |
44 |
45 |
48 |
49 |
52 |
53 |
54 |
63 |
64 |
65 |
66 |
70 |
71 |
75 |
76 |
77 |
81 |
82 |
83 |
87 |
88 |
89 |
93 |
94 |
95 |
99 |
100 |
101 |
107 |
108 |
109 |
113 |
114 |
115 |
116 |
120 |
121 |
122 |
123 |
127 |
128 |
129 |
133 |
134 |
135 |
138 |
139 |
140 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 写在前面
2 | 要学习新东西,最好的办法是先学会如何使用。所以,本文仅作 Android Data Binding 的介绍并结合 [DataBindingDemo][DataBindingDemo] 来理解它的用法,后续再对其原理进行深入探讨。
3 |
4 | ## 简介
5 | Data binding 在2015年7月发布的Android Studio v1.3.0 版本上引入,在2016年4月Android Studio v2.0.0 上正式支持。目前为止,Data Binding 已经支持双向绑定了。
6 |
7 | Databinding 是一个实现数据和UI绑定的框架,是一个实现 MVVM 模式的工具,有了 Data Binding,在Android中也可以很方便的实现MVVM开发模式。
8 |
9 | Data Binding 是一个support库,最低支持到Android 2.1(API Level 7+)。
10 |
11 | Data Binding 之前,我们不可避免地要编写大量的毫无营养的代码,如 findViewById()、setText(),setVisibility(),setEnabled() 或 setOnClickListener() 等,通过 Data Binding , 我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局,这样就不用编写大量的毫无营养的代码了。
12 |
13 |
14 | ## 构建环境
15 | 1. 首先,确保能使用Data Binding,需要下载最新的 Support repository。否则可能报错,如图:
16 |
17 | 
18 |
19 | 2. 在模块的build.gradle文件中添加dataBinding配置
20 |
21 | android {
22 | ....
23 | dataBinding {
24 | enabled = true
25 | }
26 | }
27 |
28 | > 注意:如果app依赖了一个使用 Data Binding 的库,那么app module 的 build.gradle 也必须配置 Data Binding。
29 |
30 | ## Data Binding 布局文件 - (View)
31 | Data binding 的布局文件与传统布局文件有一点不同。它以一个 layout 标签作为根节点,里面是 data 标签与 view 标签。view 标签的内容就是不使用 Data Binding 时的普通布局文件内容。以下是一个例子:
32 |
33 | ```xml
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
45 |
46 |
49 |
50 |
51 |
54 |
55 |
56 | ```
57 |
58 | ### 数据对象 - (Model)
59 | 假设你有一个 plain-old Java object(POJO) 的 User 对象。
60 |
61 | ```java
62 | public class User {
63 | private final String mFirstName;
64 | private final String mLastName;
65 | private int mAge;
66 |
67 | public User(String firstName, String lastName, int age) {
68 | mFirstName = firstName;
69 | mLastName = lastName;
70 | mAge = age;
71 | }
72 | }
73 | ```
74 | 或者是 JavaBean 对象:
75 |
76 | ```java
77 | public class User {
78 | private final String mFirstName;
79 | private final String mLastName;
80 | private int mAge;
81 |
82 | public User(String firstName, String lastName, int age) {
83 | mFirstName = firstName;
84 | mLastName = lastName;
85 | mAge = age;
86 | }
87 |
88 | public String getFirstName() {
89 | return mFirstName;
90 | }
91 |
92 | public String getLastName() {
93 | return mLastName;
94 | }
95 |
96 | public int getAge() {
97 | return mAge;
98 | }
99 | }
100 | ```
101 |
102 | 从 Data Binding 的角度看,这两个类是一样的。用于 TextView 的 `android:text` 属性的表达式`@{user.firstName} `,会读取 POJO 对象的 `firstName` 字段以及 JavaBeans 对象的 `getFirstName() `方法。
103 |
104 | ### 绑定数据 - (ViewModel)
105 | 在默认情况下,会基于布局文件生成一个继承于 `ViewDataBinding` 的 Binding 类,将它转换成帕斯卡命名并在名字后面接上`Binding`。例如,布局文件叫 `main_activity.xml `,所以会生成一个 `MainActivityBinding` 类。这个类包含了布局文件中所有的绑定关系,会根据绑定表达式给布局文件赋值。在 inflate 的时候创建 binding 的方法如下:
106 |
107 | ```java
108 | @Override
109 | protected void onCreate(Bundle savedInstanceState) {
110 | super.onCreate(savedInstanceState);
111 |
112 | // ActivityBaseBinding 类是自动生成的
113 | ActivityBaseBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_base);
114 | User user = new User("Connor", "Lin");
115 | // 所有的 set 方法也是根据布局中 variable 名称生成的
116 | binding.setUser(user);
117 | }
118 | ```
119 |
120 | ***
121 |
122 | ### 事件处理
123 | 本部分源码请参考 [DataBindingDemo][DataBindingDemo] -> `EventActivity` 部分。
124 |
125 | 类似于 android:onClick 可以指定 Activity 中的函数,Data Binding 也允许处理从视图中发送的事件。
126 |
127 | 有两种实现方式:
128 |
129 | + 方法调用
130 | + 监听绑定
131 |
132 | > 二者主要区别在于方法调用在编译时处理,而监听绑定于事件发生时处理。
133 |
134 | #### 方法调用
135 | 相较于 android:onClick ,它的优势在于表达式会在编译时处理,如果函数不存在或者函数签名不对,编译将会报错。
136 |
137 | 以下是个例子:
138 |
139 | ```java
140 | public class EventHandler {
141 | private Context mContext;
142 | public EventHandler(Context context) {
143 | mContext = context;
144 | }
145 |
146 | public void onClickFriend(View view) {
147 | Toast.makeText(mContext, "onClickFriend", Toast.LENGTH_LONG).show();
148 | }
149 | }
150 | ```
151 |
152 | 表达式如下:
153 |
154 | ```xml
155 |
156 |
157 |
158 |
161 |
162 |
163 |
167 |
171 |
172 |
173 |
174 |
175 | ```
176 |
177 | #### 监听绑定
178 | 监听绑定在事件发生时调用,可以使用任意表达式
179 |
180 | > 此功能在 Android Gradle Plugin version 2.0 或更新版本上可用.
181 |
182 | 在方法引用中,方法的参数必须与监听器对象的参数相匹配。在监听绑定中,只要返回值与监听器对象的预期返回值相匹配即可。
183 |
184 | 以下是个例子:
185 |
186 | ```java
187 | public void onTaskClick(Task task) {
188 | task.run();
189 | }
190 | ```
191 |
192 | 表达式如下:
193 |
194 | ```xml
195 |
196 |
197 |
198 |
200 |
202 |
203 |
204 |
207 |
211 |
212 |
213 | ```
214 |
215 | 当一个回调函数在表达式中使用时,数据绑定会自动为事件创建必要的监听器并注册监听。
216 |
217 | ##### 关于参数
218 |
219 | + 参数有两种选择:要么不写,要么就要写全。
220 |
221 | ```xml
222 |
226 | 或
227 |
231 | ```
232 |
233 | + lambda 表达式可添加一个或多个参数,同时参数可任意命名
234 |
235 | ```java
236 | public class EventHandler {
237 | public void onTaskClickWithParams(View view, Task task) {
238 | task.run();
239 | }
240 | }
241 | ```
242 | ```xml
243 |
247 | ```
248 | 或者
249 |
250 | ```java
251 | public class EventHandler {
252 | public void onCompletedChanged(Task task, boolean completed) {
253 | if(completed) {
254 | task.run();
255 | }
256 | }
257 | }
258 | ```
259 | ```xml
260 |
264 | ```
265 |
266 | + 表达式结果有默认值 null、0、false等等
267 |
268 | + 表达式中可以使用void
269 |
270 | ```xml
271 |
275 | ```
276 |
277 | ##### 关于表达式
278 |
279 | + 复杂的表达式会使布局难以阅读和维护,这种情况我们最好将业务逻辑写到回调函数中
280 |
281 | + 也有一些特殊的点击事件 我们需要使用不同于 android:onClick 的属性来避免冲突。
282 |
283 | 下面是一些用来避免冲突的属性:
284 |
285 | |Class|Listener Setter|Attribute|
286 | |:---:|:---:|:---:|
287 | |SearchView|setOnSearchClickListener(View.OnClickListener)|android:onSearchClick|
288 | |ZoomControls|setOnZoomInClickListener(View.OnClickListener)|android:onZoomIn|
289 | |ZoomControls|setOnZoomOutClickListener(View.OnClickListener)|android:onZoomOut|
290 |
291 | ***
292 |
293 | ## 布局详情
294 | 本部分源码请参考 [DataBindingDemo][DataBindingDemo] -> `CombineActivity` 部分
295 |
296 | ### 导入(Imports)
297 |
298 | + data 标签内可以有多个 import 标签。你可以在布局文件中像使用 Java 一样导入引用
299 |
300 | ```xml
301 |
302 |
303 |
304 |
305 |
309 | ```
310 |
311 | + 当类名发生冲突时,可以使用 alias
312 |
313 | ```xml
314 |
315 |
316 | ```
317 |
318 | + 导入的类型也可以用于变量的类型引用和表达式中
319 |
320 | ```xml
321 |
322 |
323 |
324 |
325 |
326 |
327 | ```
328 |
329 | > 注意:Android Studio 还没有对导入提供自动补全的支持。你的应用还是可以被正常编译,要解决这个问题,你可以在变量定义中使用完整的包名。
330 |
331 | + 导入也可以用于在表达式中使用静态方法
332 |
333 | ```java
334 | public class MyStringUtils {
335 | public static String capitalize(final String word) {
336 | if (word.length() > 1) {
337 | return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);
338 | }
339 | return word;
340 | }
341 | }
342 | ```
343 |
344 | ```xml
345 |
346 |
347 |
348 |
349 | …
350 |
354 | ```
355 |
356 | + java.lang.* 包中的类会被自动导入,可以直接使用,例如, 要定义一个 String 类型的变量
357 |
358 | ```xml
359 |
360 | ```
361 |
362 | ### 变量 Variables
363 | + data 标签中可以有任意数量的 variable 标签。每个 variable 标签描述了会在 binding 表达式中使用的属性。
364 |
365 | ```xml
366 |
367 |
368 |
369 |
370 |
371 |
372 | ```
373 |
374 | + 可以在表达式中直接引用带 id 的 view,引用时采用驼峰命名法。
375 |
376 | ```xml
377 |
382 |
383 |
388 |
389 | ```
390 |
391 | + binding 类会生成一个命名为 context 的特殊变量(其实就是 rootView 的 getContext() ) 的返回值),这个变量可用于表达式中。 如果有名为 context 的变量存在,那么生成的这个 context 特殊变量将被覆盖。
392 |
393 | ```xml
394 |
398 | ```
399 | ```java
400 | public String loadString(Context context) {
401 | // 使用生成的context变量
402 | return context.getResources().getString(R.string.string_from_context);
403 | }
404 | ```
405 |
406 | ### 自定义绑定类名
407 | 默认情况下,binding 类的名称取决于布局文件的命名,以大写字母开头,移除下划线,后续字母大写并追加 “Binding” 结尾。这个类会被放置在 databinding 包中。举个例子,布局文件 contact_item.xml 会生成 ContactItemBinding 类。如果 module 包名为 com.example.my.app ,binding 类会被放在 com.example.my.app.databinding 中。
408 |
409 | 通过修改 data 标签中的 class 属性,可以修改 Binding 类的命名与位置。举个例子:
410 |
411 | ```xml
412 |
413 | ...
414 |
415 | ```
416 |
417 | 以上会在 databinding 包中生成名为 CustomBinding 的 binding 类。如果需要放置在不同的包下,可以在前面加 `“.”`:
418 |
419 | ```xml
420 |
421 | ...
422 |
423 | ```
424 |
425 | 这样的话, CustomBinding 会直接生成在 module 包下。如果提供完整的包名,binding 类可以放置在任何包名中:
426 |
427 | ```xml
428 |
429 | ...
430 |
431 | ```
432 |
433 | ### Includes
434 | 在使用应用命名空间的布局中,变量可以传递到任何 `include` 布局中。
435 |
436 | ```xml
437 |
438 |
440 |
441 |
442 |
443 |
447 |
449 |
450 |
451 | ```
452 |
453 | > 需要注意, activity_combine.xml 与 include.xml 中都需要声明 user 变量。
454 |
455 | Data binding 不支持直接包含 `merge` 节点。举个例子, 以下的代码**不能正常运行** :
456 |
457 | ```xml
458 |
459 |
461 |
462 |
463 |
464 |
465 |
467 |
468 |
469 | ```
470 |
471 | ### 表达式语言
472 | #### 通用特性
473 | 表达式语言与 Java 表达式有很多相似之处。下面是相同之处:
474 |
475 | + 数学计算 + - / * %
476 | + 字符串连接 +
477 | + 逻辑 && ||
478 | + 二进制 & | ^
479 | + 一元 + - ! ~
480 | + 位移 >> >>> <<
481 | + 比较 == > < >= <=
482 | + instanceof
483 | + 组 ()
484 | + 字面量 - 字符,字符串,数字, null
485 | + 类型转换
486 | + 函数调用
487 | + 字段存取
488 | + 数组存取 []
489 | + 三元运算符 ?:
490 |
491 | 例子:
492 |
493 | ```xml
494 |
495 |
499 |
500 |
501 |
505 | ```
506 | > 在xml中转义是不可避免的,如 : 使用“&&”是编译不通过的,需要使用转义字符 "&&"
507 |
508 | **附:常用的转义字符**
509 |
510 | |显示结果|描述|转义字符|十进制|
511 | |:---:|:---:|:---:|:---:|
512 | | |空格| | |
513 | |<|小于号|<|<|
514 | |>|大于号|>|>|
515 | |&|与号|&|&|
516 | |"|引号|"|"|
517 | |'|撇号|' |'|
518 | |×|乘号|×|×|
519 | |÷|除号|÷|÷|
520 |
521 | #### 不支持的操作符
522 | 一些 Java 中的操作符在表达式语法中不能使用。
523 |
524 | + this
525 | + super
526 | + new
527 | + 显式泛型调用 ``
528 |
529 | #### Null合并运算符
530 | Null合并运算符 `??` 会在非 null 的时候选择左边的操作,反之选择右边。
531 |
532 | ```
533 | android:text="@{user.lastName ?? `Default LastName`}"
534 | ```
535 |
536 | 等同于
537 |
538 | ```
539 | android:text="@{user.lastName != null ? user.lastName : `Default LastName`}"
540 | ```
541 |
542 | #### 容器类
543 | 通用的容器类:数组,lists,sparse lists,和 maps,可以用 `[]` 操作符来存取
544 |
545 | ```xml
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 | …
557 | android:text="@{list[index]}"
558 | …
559 | android:text="@{sparse[index]}"
560 | …
561 | android:text="@{map[key]}"
562 | ```
563 |
564 | #### 字符串常量
565 | 使用单引号把属性包起来,就可以很简单地在表达式中使用双引号:
566 |
567 | ```xml
568 | android:text='@{map["firstName"]}'
569 | ```
570 |
571 | 也可以用双引号将属性包起来。这样的话,字符串常量就可以用 " 或者反引号 ( ` ) 来调用
572 |
573 | ```xml
574 | android:text="@{map[`firstName`}"
575 | android:text="@{map["firstName"]}"
576 | ```
577 |
578 | #### 资源
579 | 也可以在表达式中使用普通的语法来引用资源:
580 |
581 | ```
582 | android:text="@{@string/fullname(user.fullName)"
583 | ```
584 |
585 | 字符串格式化和复数形式可以这样实现:
586 |
587 | ```
588 | android:text="@{@plurals/sample_plurals(num)}"
589 | ```
590 |
591 | 当复数形式有多个参数时,应该这样写:
592 |
593 | ```
594 | android:text="@{@plurals/numbers(num, num)}"
595 | ```
596 |
597 | 一些资源需要显示类型调用。
598 |
599 | |Type|Normal Reference|Expression Reference|
600 | |:---|:---|:---|
601 | |String[]|@array|@stringArray|
602 | |int[]|@array|@intArray|
603 | |TypedArray|@array|@typedArray|
604 | |Animator |@animator |@animator|
605 | |StateListAnimator|@animator |@stateListAnimator|
606 | |color int|@color|@color|
607 | |ColorStateList|@color|@colorStateList|
608 |
609 | ***
610 |
611 | ## 数据对象 (Data Objects)
612 | 任何 POJO 对象都能用在 Data Binding 中,但是更改 POJO 并不会同步更新 UI。Data Binding 的强大之处就在于它可以让你的数据拥有更新通知的能力。
613 |
614 | 有三种不同的动态更新数据的机制:
615 |
616 | + Observable 对象
617 | + Observable 字段
618 | + Observable 容器类
619 |
620 | 当以上的 observable 对象绑定在 UI 上,数据发生变化时,UI 就会同步更新。
621 |
622 | ### Observable 对象
623 | 当一个类实现了 Observable 接口时,Data Binding 会设置一个 listener 在绑定的对象上,以便监听对象字段的变动。
624 |
625 | Observable 接口有一个添加/移除 listener 的机制,但通知取决于开发者。为了简化开发,Android 原生提供了一个基类 `BaseObservable` 来实现 listener 注册机制。这个类也实现了字段变动的通知,只需要在 getter 上使用 Bindable 注解,并在 setter 中通知更新即可。
626 |
627 | ```java
628 | public class ObservableContact extends BaseObservable {
629 | private String mName;
630 | private String mPhone;
631 |
632 | public ObservableContact(String name, String phone) {
633 | mName = name;
634 | mPhone = phone;
635 | }
636 |
637 | @Bindable
638 | public String getName() {
639 | return mName;
640 | }
641 |
642 | public void setName(String name) {
643 | mName = name;
644 | notifyPropertyChanged(BR.name);
645 | }
646 |
647 | @Bindable
648 | public String getPhone() {
649 | return mPhone;
650 | }
651 |
652 | public void setPhone(String phone) {
653 | mPhone = phone;
654 | notifyPropertyChanged(BR.phone);
655 | }
656 | }
657 | ```
658 |
659 | `BR` 是编译阶段生成的一个类,功能与 R.java 类似,用 @Bindable 标记过 getter 方法会在 BR 中生成一个 entry。
660 | 当数据发生变化时需要调用 `notifyPropertyChanged(BR.firstName)` 通知系统 `BR.firstName` 这个 entry 的数据已经发生变化以更新UI。
661 |
662 | ### ObservableFields
663 | 创建 Observable 类还是需要花费一点时间的,如果想要省时,或者数据类的字段很少的话,可以使用 `ObservableField` 以及它的派生 `ObservableBoolean`、
664 | `ObservableByte` 、`ObservableChar `、`ObservableShort`、`ObservableInt`、`ObservableLong`、`ObservableFloat`、`ObservableDouble`、
665 | `ObservableParcelable` 。
666 |
667 | ObservableFields 是包含 observable 对象的单一字段。原始版本避免了在存取过程中做打包/解包操作。要使用它,在数据类中创建一个 public final 字段:
668 |
669 | ```java
670 | public class ObservableFieldContact {
671 | public ObservableField mName = new ObservableField<>();
672 | public ObservableField mPhone = new ObservableField<>();
673 |
674 | public ObservableFieldContact(String name, String phone) {
675 | mName.set(name);
676 | mPhone.set(phone);
677 | }
678 | }
679 | ```
680 |
681 | 要存取数据,只需要使用 get() / set() 方法:
682 |
683 | ```java
684 | mObservableFieldContact.mName.set("ConnorLin");
685 | mObservableFieldContact.mPhone.set("12345678901");
686 |
687 | String name = mObservableFieldContact.mName.get();
688 | ```
689 |
690 | ### Observable Collections 容器类
691 | 一些应用会使用更加灵活的结构来保持数据。Observable 容器类允许使用 key 来获取这类数据。当 key 是类似 String 的一类引用类型时,使用 ObservableArrayMap 会非常方便。
692 |
693 | ```java
694 | ObservableArrayMap mUser = new ObservableArrayMap<>();
695 | mUser.put("firstName", "Connor");
696 | mUser.put("lastName", "Lin");
697 | mUser.put("age", "28");
698 | mBinding.setUser(mUser);
699 | ```
700 |
701 | 在布局中,可以用 String key 来获取 map 中的数据:
702 |
703 | ```xml
704 |
705 |
706 |
707 |
708 | …
709 |
713 |
714 |
718 |
719 |
723 | ```
724 |
725 | 当 key 是整数类型时,可以使用 ObservableArrayList :
726 |
727 | ```java
728 | ObservableArrayList user = new ObservableArrayList<>();
729 | user.add("Google");
730 | user.add("Inc.");
731 | user.add("17");
732 | ```
733 |
734 | 在布局文件中,使用下标获取列表数据:
735 |
736 | ```xml
737 |
738 |
739 |
740 |
741 | …
742 |
746 |
747 |
751 |
752 |
756 | ```
757 |
758 | ***
759 |
760 | ## 生成绑定
761 | 生成的 binding 类将布局中的 View 与变量绑定在一起。就像先前提到过的,类名和包名可以自定义 。生成的 binding 类会继承 ViewDataBinding 。
762 |
763 | ### Creating
764 | binding 应该在 inflate 之后创建,确保 View 的层次结构不会在绑定前被干扰。绑定布局有好几种方式。最常见的是使用 binding 类中的静态方法。inflate 函数会 inflate View 并将 View 绑定到 binding 类上。此外有更加简单的函数,只需要一个 LayoutInflater 或一个 ViewGroup:
765 |
766 | ```java
767 | MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
768 | MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
769 | ```
770 |
771 | 如果布局使用不同的机制来 inflate,则可以独立做绑定操作:
772 |
773 | ```java
774 | MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
775 | ```
776 |
777 | 有时绑定关系是不能提前确定的。这种情况下,可以使用 DataBindingUtil :
778 |
779 | ```java
780 | ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId, parent, attachToParent);
781 | ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
782 | ```
783 |
784 | ### Views With IDs
785 | 布局中每一个带有 ID 的 View,都会生成一个 public final 字段。binding 过程会做一个简单的赋值,在 binding 类中保存对应 ID 的 View。这种机制相比调用 findViewById 效率更高。举个例子:
786 |
787 | ```xml
788 |
789 |
790 |
791 |
792 |
796 |
800 |
804 |
805 |
806 | ```
807 |
808 | 将会在 binding 类内生成:
809 |
810 | ```java
811 | public final TextView firstName;
812 | public final TextView lastName;
813 | ```
814 |
815 | ID 在 Data Binding 中并不是必需的,但是在某些情况下还是有必要对 View 进行操作。
816 |
817 | ### Variables
818 | 每一个变量会有相应的存取函数:
819 |
820 | ```xml
821 |
822 |
823 |
824 |
825 |
826 |
827 | ```
828 |
829 | 并在 binding 类中生成对应的 getters 和 setters:
830 |
831 | ```java
832 | public com.connorlin.databinding.model.User getUser();
833 | public void setUser(com.connorlin.databinding.model.User user);
834 | public Drawable getImage();
835 | public void setImage(Drawable image);
836 | public String getNote();
837 | public void setNote(String note);
838 | ```
839 |
840 | ### ViewStubs
841 | 本部分源码请参考 [DataBindingDemo][DataBindingDemo] -> `ViewStubActivity` 部分。
842 |
843 | ViewStub 相比普通 View 有一些不同。ViewStub 一开始是不可见的,当它们被设置为可见,或者调用 inflate 方法时,ViewStub 会被替换成另外一个布局。
844 |
845 | 因为 ViewStub 实际上不存在于 View 结构中,binding 类中的类也得移除掉,以便系统回收。因为 binding 类中的 View 都是 final 的,所以Android 提供了一个叫 `ViewStubProxy` 的类来代替 ViewStub 。开发者可以使用它来操作 ViewStub,获取 ViewStub inflate 时得到的视图。
846 |
847 | 但 inflate 一个新的布局时,必须为新的布局创建一个 binding。因此, `ViewStubProxy` 必须监听 ViewStub 的 `ViewStub.OnInflateListener`,并及时建立 binding。由于 ViewStub 只能有一个 `OnInflateListener`,你可以将你自己的 listener 设置在 ViewStubProxy 上,在 binding 建立之后, listener 就会被触发。
848 |
849 | ```xml
850 |
851 |
852 |
856 |
857 |
858 | ```
859 |
860 | 在 Java 代码中获取 binding 实例,为 `ViewStubProy` 注册 `ViewStub.OnInflateListener` 事件:
861 |
862 | ```java
863 | mActivityViewStubBinding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
864 | mActivityViewStubBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
865 | @Override
866 | public void onInflate(ViewStub stub, View inflated) {
867 | IncludeBinding viewStubBinding = DataBindingUtil.bind(inflated);
868 | User user = new User("Connor", "Lin", 28);
869 | viewStubBinding.setUser(user);
870 | }
871 | });
872 | ```
873 |
874 | 通过 `ViewStubProxy` 来 inflate ViewStub :
875 |
876 | ```java
877 | public void inflate(View view) {
878 | if (!mActivityViewStubBinding.viewStub.isInflated()) {
879 | mActivityViewStubBinding.viewStub.getViewStub().inflate();
880 | }
881 | }
882 | ```
883 | > 此处 `isInflated()` 和 `getViewStub()` 会标红,请不要担心,这并不是错误,是 [ViewStubProxy](https://developer.android.com/reference/android/databinding/ViewStubProxy.html) 中的方法。
884 |
885 | ### 高级绑定
886 | #### 动态变量
887 | 有时候,有一些不可知的 binding 类。例如,RecyclerView.Adapter 可以用来处理不同布局,这样的话它就不知道应该使用哪一个 binding 类。而在 onBindViewHolder(VH, int) ) 的时候,binding 类必须被赋值。
888 |
889 | 在这种情况下,RecyclerView 的布局内置了一个 item 变量。 BindingHolder 有一个 getBinding 方法,返回一个 ViewDataBinding 基类。
890 |
891 | ```java
892 | public void onBindViewHolder(BindingHolder holder, int position) {
893 | holder.getBinding().setVariable(BR.item, mItemList.get(position));
894 | holder.getBinding().executePendingBindings();
895 | }
896 | ```
897 |
898 | 以上,详细请参考 [DataBindingDemo][DataBindingDemo] -> `MainActivity` 部分(使用 `RecyclerView` 实现)。
899 |
900 | #### 直接 binding
901 | 当变量或者 observable 发生变动时,会在下一帧触发 binding。有时候 binding 需要马上执行,这时候可以使用 `executePendingBindings()`。
902 |
903 | #### 后台线程
904 | 只要数据不是容器类,你可以直接在后台线程做数据变动。Data binding 会将变量/字段转为局部量,避免同步问题。
905 |
906 | ***
907 |
908 | ## 属性设置
909 | 本部分源码请参考 [DataBindingDemo][DataBindingDemo] -> `AttributeSettersActivity` 部分。
910 |
911 | 当绑定数据发生变动时,生成的 binding 类必须根据 binding 表达式调用 View 的 setter 函数。Data binding 框架内置了几种自定义赋值的方法。
912 |
913 | ### 自动设置属性
914 | 对一个 attribute 来说,Data Binding 会尝试寻找对应的 setAttribute 函数。属性的命名空间不会对这个过程产生影响,只有属性的命名才是决定因素。
915 |
916 | 举个例子,针对一个与 TextView 的 android:text 绑定的表达式,Data Binding会自动寻找 setText(String) 函数。如果表达式返回值为 int 类型, Data Binding则会寻找 setText(int) 函数。所以需要小心处理函数的返回值类型,必要的时候使用强制类型转换。
917 |
918 | 需要注意的是,Data Binding 在对应名称的属性不存在的时候也能继续工作。你可以轻而易举地使用 Data Binding 为任何 setter “创建” 属性。
919 | 如 [DataBindingDemo][DataBindingDemo] 中的自定义布局 Card,并没有添加 `declare-styleable`,但是可以使用自动 setter 的特性来调用这些函数。
920 |
921 | ```xml
922 |
926 | ```
927 |
928 | ### 重命名属性设置
929 | 一些属性的命名与 setter 不对应。针对这些函数,可以用 BindingMethods 注解来将属性与 setter 绑定在一起。举个例子, `android:tint` 属性可以这样与 `setImageTintList(ColorStateList) )` 绑定,而不是 setTint :
930 |
931 | ```java
932 | @BindingMethods({
933 | @BindingMethod(type = "android.widget.ImageView",
934 | attribute = "android:tint",
935 | method = "setImageTintList"),
936 | })
937 | ```
938 |
939 | Android 框架中的 setter 重命名已经在库中实现了,我们只需要专注于自己的 setter。
940 |
941 | ### 自定义属性设置
942 | 一些属性需要自定义 setter 逻辑。例如,目前没有与 `android:paddingLeft` 对应的 setter,只有一个 `setPadding(left, top, right, bottom)` 函数。结合静态 binding adapter 函数与 BindingAdapter 注解可以让开发者自定义属性 setter。
943 |
944 | Android 属性已经内置一些 BindingAdapter。例如,这是一个 paddingLeft 的自定义 setter:
945 |
946 | ```java
947 | @BindingAdapter("android:paddingLeft")
948 | public static void setPaddingLeft(View view, int padding) {
949 | view.setPadding(padding,
950 | view.getPaddingTop(),
951 | view.getPaddingRight(),
952 | view.getPaddingBottom());
953 | }
954 | ```
955 |
956 | Binding adapter 在其他自定义类型上也很好用。举个例子,一个 loader 可以在非主线程加载图片。
957 |
958 | 当存在冲突时,开发者创建的 binding adapter 会覆盖 Data Binding 的默认 adapter。
959 |
960 | 你也可以创建多个参数的 adapter:
961 |
962 | ```java
963 | // 无需手动调用此函数
964 | @BindingAdapter({"imageUrl", "error"})
965 | public static void loadImage(ImageView view, String url, Drawable error) {
966 | Glide.with(view.getContext()).load(url).error(error).into(view);
967 | }
968 | ```
969 |
970 | ```xml
971 |
972 |
975 | ```
976 |
977 | 当 imageUrl 与 error 存在时这个 adapter 会被调用。imageUrl 是一个 String,error 是一个 Drawable。
978 |
979 | - 在匹配时自定义命名空间会被忽略
980 | - 你可以为 android 命名空间编写 adapter
981 |
982 | Binding adapter 方法可以获取旧的赋值。只需要将旧值放置在前,新值放置在后:
983 |
984 | ```java
985 | @BindingAdapter("android:paddingLeft")
986 | public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
987 | if (oldPadding != newPadding) {
988 | view.setPadding(newPadding,
989 | view.getPaddingTop(),
990 | view.getPaddingRight(),
991 | view.getPaddingBottom());
992 | }
993 | }
994 | ```
995 |
996 | 事件 handlers 仅可用于只拥有一个抽象方法的接口或者抽象类。例如:
997 |
998 | ```java
999 | @BindingAdapter("android:onLayoutChange")
1000 | public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
1001 | View.OnLayoutChangeListener newValue) {
1002 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
1003 | if (oldValue != null) {
1004 | view.removeOnLayoutChangeListener(oldValue);
1005 | }
1006 | if (newValue != null) {
1007 | view.addOnLayoutChangeListener(newValue);
1008 | }
1009 | }
1010 | }
1011 | ```
1012 |
1013 | 当 listener 内置多个函数时,必须分割成多个 listener。例如, View.OnAttachStateChangeListener 内置两个函数: `onViewAttachedToWindow()`与 `onViewDetachedFromWindow()` 。在这里必须为两个不同的属性创建不同的接口。
1014 |
1015 | ```java
1016 | @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
1017 | public interface OnViewDetachedFromWindow {
1018 | void onViewDetachedFromWindow(View v);
1019 | }
1020 |
1021 | @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
1022 | public interface OnViewAttachedToWindow {
1023 | void onViewAttachedToWindow(View v);
1024 | }
1025 | ```
1026 |
1027 | 因为改变一个 listener 会影响到另外一个,我们必须编写三个不同的 adapter,包括修改一个属性的和修改两个属性的。
1028 |
1029 | ```java
1030 | @BindingAdapter("android:onViewAttachedToWindow")
1031 | public static void setListener(View view, OnViewAttachedToWindow attached) {
1032 | setListener(view, null, attached);
1033 | }
1034 |
1035 | @BindingAdapter("android:onViewDetachedFromWindow")
1036 | public static void setListener(View view, OnViewDetachedFromWindow detached) {
1037 | setListener(view, detached, null);
1038 | }
1039 |
1040 | @BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
1041 | public static void setListener(View view, final OnViewDetachedFromWindow detach,
1042 | final OnViewAttachedToWindow attach) {
1043 | if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
1044 | final OnAttachStateChangeListener newListener;
1045 | if (detach == null && attach == null) {
1046 | newListener = null;
1047 | } else {
1048 | newListener = new OnAttachStateChangeListener() {
1049 | @Override
1050 | public void onViewAttachedToWindow(View v) {
1051 | if (attach != null) {
1052 | attach.onViewAttachedToWindow(v);
1053 | }
1054 | }
1055 |
1056 | @Override
1057 | public void onViewDetachedFromWindow(View v) {
1058 | if (detach != null) {
1059 | detach.onViewDetachedFromWindow(v);
1060 | }
1061 | }
1062 | };
1063 | }
1064 | final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
1065 | newListener, R.id.onAttachStateChangeListener);
1066 | if (oldListener != null) {
1067 | view.removeOnAttachStateChangeListener(oldListener);
1068 | }
1069 | if (newListener != null) {
1070 | view.addOnAttachStateChangeListener(newListener);
1071 | }
1072 | }
1073 | }
1074 | ```
1075 |
1076 | 上面的例子比普通情况下复杂,因为 View 是 `add/remove` `View.OnAttachStateChangeListener` 而不是 `set`。 `android.databinding.adapters.ListenerUtil`可以用来辅助跟踪旧的 listener 并移除它。
1077 |
1078 | 对应 `addOnAttachStateChangeListener(View.OnAttachStateChangeListener) ) `支持的 api 版本,
1079 | 通过向 `OnViewDetachedFromWindow` 和 `OnViewAttachedToWindow` 添加 `@TargetApi(VERSION_CODES.HONEYCHOMB_MR1)` 注解,
1080 | Data Binding 代码生成器会知道这些 listener 只会在 Honeycomb MR1 或更新的设备上使用。
1081 |
1082 | ***
1083 |
1084 | ## 转换器Converters
1085 | ### 对象转换
1086 | 当 binding 表达式返回对象时,会选择一个 setter(自动 Setter,重命名 Setter,自定义 Setter),将返回对象强制转换成 setter 需要的类型。
1087 |
1088 | 下面是一个使用 ObservableMap 保存数据的例子:
1089 |
1090 | ```xml
1091 |
1095 | ```
1096 |
1097 | 在这里, userMap 会返回 Object 类型的值,而返回值会被自动转换成 setText(CharSequence) 需要的类型。当对参数类型存在疑惑时,开发者需要手动做类型转换。
1098 |
1099 | ### 自定义转换
1100 | 有时候会自动在特定类型直接做类型转换。例如,当设置背景的时候:
1101 |
1102 | ```xml
1103 |
1107 | ```
1108 |
1109 | 在这里,背景需要的是 Drawable ,但是 color 是一个整数。当需要 Drawable 却返回了一个整数时, int 会自动转换成 ColorDrawable 。这个转换是在一个 BindingConversation 注解的静态函数中实现:
1110 |
1111 | ```java
1112 | @BindingConversion
1113 | public static ColorDrawable convertColorToDrawable(int color) {
1114 | return new ColorDrawable(color);
1115 | }
1116 | ```
1117 |
1118 | 需要注意的是,这个转换只能在 setter 阶段生效,所以 不允许 混合类型:
1119 |
1120 | ```xml
1121 |
1125 | ```
1126 |
1127 | ***
1128 |
1129 | ## Android Studio对Data Binding的支持
1130 |
1131 | * Android Studio 支持 Data Binding 表现为:
1132 |
1133 | * 语法高亮
1134 | * 标记表达式语法错误
1135 | * XML 代码补全
1136 | * 跳转到声明或快速文档
1137 |
1138 | > 注意:数组和泛型类型,如 Observable 类,当没有错误时可能会显示错误。
1139 |
1140 | * 在预览窗口可显示 Data Binding 表达式的默认值。例如:
1141 |
1142 | ```xml
1143 |
1147 |
1148 | ```
1149 |
1150 | 如果你需要在设计阶段显示默认值,你可以使用 `tools` 属性代替默认值表达式,详见 [设计阶段布局属性 ](http://tools.android.com/tips/layout-designtime-attributes)
1151 |
1152 | [DataBindingDemo]: https://github.com/ConnorLin/DataBindingDemo
1153 |
1154 | ## 参考资料
1155 | 1. [Data Binding Library](https://developer.android.com/topic/libraries/data-binding/index.html)
1156 | 2. [安卓 Data Binding 使用方法总结](http://blog.csdn.net/zhaizu/article/details/51491455)
1157 | 3. [(译)Data Binding 指南](http://yanghui.name/blog/2016/02/17/data-binding-guide/?utm_source=tuicool&utm_medium=referral)
1158 | 4. [精通 Android Data Binding](https://github.com/LyndonChin/MasteringAndroidDataBinding/blob/master/README.md)
1159 |
1160 | ***
1161 |
1162 | **我的简书账号是 ConnorLin,欢迎关注!**
1163 |
1164 | **我的简书专题是 Android开发技术分享,欢迎关注!**
1165 |
1166 | **[我的个人博客](http://connorlin.github.io/) 欢迎关注!**
1167 |
1168 | **原创文章,欢迎转载,转载请注明出处!**
1169 |
1170 | 
1171 |
1172 | **欢迎您扫一扫上面的微信公众号,订阅我的博客!**
1173 |
--------------------------------------------------------------------------------