Skip to content

Commit

Permalink
Databinding model (airbnb#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
elihart authored Apr 16, 2017
1 parent bc9499c commit e5111b3
Show file tree
Hide file tree
Showing 40 changed files with 2,026 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DiffPayload {
private final EpoxyModel<?> singleModel;
private final LongSparseArray<EpoxyModel<?>> modelsById;

DiffPayload(List<EpoxyModel<?>> models) {
DiffPayload(List<? extends EpoxyModel<?>> models) {
if (models.isEmpty()) {
throw new IllegalStateException("Models must not be empty");
}
Expand Down
1 change: 1 addition & 0 deletions epoxy-databinding/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
36 changes: 36 additions & 0 deletions epoxy-databinding/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion rootProject.COMPILE_SDK_VERSION
buildToolsVersion rootProject.ANDROID_BUILD_TOOLS_VERSION

defaultConfig {
minSdkVersion rootProject.MIN_SDK_VERSION
targetSdkVersion rootProject.TARGET_SDK_VERSION
}

dataBinding {
enabled = true
}
}

configurations.all { strategy ->
strategy.resolutionStrategy.force rootProject.deps.androidAnnotations, rootProject.deps.androidRecyclerView,
rootProject.deps.androidDesignLibrary, rootProject.deps.androidSupportLibrary, rootProject.deps.junit,
rootProject.deps.robolectric, rootProject.deps.mockito
}

dependencies {
compile rootProject.deps.androidAnnotations
compile rootProject.deps.androidRecyclerView
compile rootProject.deps.androidDesignLibrary
compile rootProject.deps.androidSupportLibrary
compile project(':epoxy-annotations')
compile project(':epoxy-adapter')

testCompile rootProject.deps.junit
testCompile rootProject.deps.robolectric
testCompile rootProject.deps.mockito
}

apply from: rootProject.file('gradle/gradle-maven-push.gradle')
3 changes: 3 additions & 0 deletions epoxy-databinding/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_NAME=Epoxy Data Binding Support
POM_ARTIFACT_ID=epoxy-databinding
POM_PACKAGING=jar
25 changes: 25 additions & 0 deletions epoxy-databinding/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/eli_hart/tools/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
10 changes: 10 additions & 0 deletions epoxy-databinding/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<manifest package="com.airbnb.epoxy.databinding"

xmlns:android="http://schemas.android.com/apk/res/android">

<application android:allowBackup="true" android:label="@string/app_name"
android:supportsRtl="true">

</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.airbnb.epoxy;

import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.airbnb.epoxy.DataBindingEpoxyModel.DataBindingHolder;

import java.util.List;

/**
* A version of {@link com.airbnb.epoxy.EpoxyModel} that can be used with databinding. The layout
* resource used with this model must be a databinding layout. This class will create the
* databinding object from the layout and call the {@link #setDataBindingVariables} methods when the
* view needs binding to the model.
* <p>
* The easiest way to use this model is to have Epoxy generate a model to do all the binding work
* for you. To do this, create an abstract subclass of this model, annotate it with {@link
* EpoxyModelClass}, and pass your layout resource as the layout param. (You must pass the layout
* this way instead of implementing {@link #getDefaultLayout()}).
* <p>
* Then, make a field to represent each of the data variables in your layout and annotate each one
* with {@link EpoxyAttribute}. The name of each field must match the name of the variable in the
* layout exactly.
* <p>
* Epoxy will generate a subclass of your model at compile time that implements {@link
* #setDataBindingVariables(ViewDataBinding)} and {@link #setDataBindingVariables(ViewDataBinding,
* EpoxyModel)} for you. This will do all binding for you, and also only bind variables that change
* if you update your model (Note: The change optimization only works when used with {@link
* EpoxyController}).
*/
public abstract class DataBindingEpoxyModel extends EpoxyModelWithHolder<DataBindingHolder> {

@Override
View buildView(ViewGroup parent) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, getViewType(), parent, false);
View view = binding.getRoot();
view.setTag(binding);
return view;
}

@Override
public void bind(DataBindingHolder holder) {
setDataBindingVariables(holder.dataBinding);
holder.dataBinding.executePendingBindings();
}

@Override
public void bind(DataBindingHolder holder, EpoxyModel<?> previouslyBoundModel) {
setDataBindingVariables(holder.dataBinding, previouslyBoundModel);
holder.dataBinding.executePendingBindings();
}

@Override
public void bind(DataBindingHolder holder, List<Object> payloads) {
setDataBindingVariables(holder.dataBinding, payloads);
holder.dataBinding.executePendingBindings();
}

/**
* This is called when the model is bound to a view, and the view's variables should be updated
* with the model's data. {@link ViewDataBinding#executePendingBindings()} is called for you after
* this method is run.
* <p>
* If you leave your class abstract and have a model generated for you via annotations this will
* be implemented for you. However, you may choose to implement this manually if you like.
*/
protected abstract void setDataBindingVariables(ViewDataBinding binding);

/**
* Similar to {@link #setDataBindingVariables(ViewDataBinding)}, but this method only binds
* variables that have changed. The changed model comes from {@link #bind(DataBindingHolder,
* EpoxyModel)}. This will only be called if the model is used in an {@link EpoxyController}
* <p>
* If you leave your class abstract and have a model generated for you via annotations this will
* be implemented for you. However, you may choose to implement this manually if you like.
*/
protected void setDataBindingVariables(ViewDataBinding dataBinding,
EpoxyModel<?> previouslyBoundModel) {
setDataBindingVariables(dataBinding);
}

protected void setDataBindingVariables(ViewDataBinding dataBinding, List<Object> payloads) {
setDataBindingVariables(dataBinding);
}

@Override
public void unbind(DataBindingHolder holder) {
holder.dataBinding.unbind();
}

@Override
protected final DataBindingHolder createNewHolder() {
return new DataBindingHolder();
}

public static class DataBindingHolder extends EpoxyHolder {
private ViewDataBinding dataBinding;

public ViewDataBinding getDataBinding() {
return dataBinding;
}

@Override
protected void bindView(View itemView) {
dataBinding = (ViewDataBinding) itemView.getTag();
}
}
}
3 changes: 3 additions & 0 deletions epoxy-databinding/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Epoxy DataBinding</string>
</resources>
12 changes: 11 additions & 1 deletion epoxy-integrationtest/build.gradle
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
apply plugin: 'com.android.library'
apply plugin: 'com.android.application'

android {

compileSdkVersion rootProject.COMPILE_SDK_VERSION
buildToolsVersion rootProject.ANDROID_BUILD_TOOLS_VERSION

defaultConfig {
applicationId "com.airbnb.android.epoxy"
minSdkVersion rootProject.MIN_SDK_VERSION
targetSdkVersion rootProject.TARGET_SDK_VERSION
vectorDrawables.useSupportLibrary = true
versionCode 1
versionName "1.0"
}

dataBinding {
enabled = true
}
}

dependencies {
compile project(':epoxy-adapter')
compile project(':epoxy-annotations')
compile project(':epoxy-databinding')
annotationProcessor project(':epoxy-processor')

testCompile rootProject.deps.junit
Expand Down
10 changes: 7 additions & 3 deletions epoxy-integrationtest/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<manifest package="com.airbnb.epoxy" xmlns:android="http://schemas.android.com/apk/res/android">
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.airbnb.epoxy"
xmlns:android="http://schemas.android.com/apk/res/android">

<application android:allowBackup="true" android:label="@string/app_name"
android:supportsRtl="true">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light">

</application>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.airbnb.epoxy;

class AdapterWithFieldAssigned extends EpoxyController {

@AutoModel Model_ model1 = new Model_();

@Override
protected void buildModels() {
add(model1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.airbnb.epoxy;

class AdapterWithIdChanged extends EpoxyController {

@AutoModel Model_ model1 = new Model_();

@Override
protected void buildModels() {
add(model1.id(23));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.airbnb.epoxy;

class BasicAutoModelsAdapter extends EpoxyController {

@AutoModel Model_ model1;
@AutoModel Model_ model2;

@Override
protected void buildModels() {
add(model1.id(1));
add(model2.id(2));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.airbnb.epoxy;

import android.view.View;

class ModelChangesDuringBind extends EpoxyModel<View> {
@EpoxyAttribute int value;

@Override
protected int getDefaultLayout() {
return R.layout.model_with_click_listener;
}

@Override
public void bind(View view) {
super.bind(view);
value = 3;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.airbnb.epoxy;

@EpoxyModelClass(layout = R.layout.model_with_data_binding)
abstract class TestDataBindingModel extends DataBindingEpoxyModel {
@EpoxyAttribute String stringValue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable
name="stringValue"
type="String" />
</data>

<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="@{stringValue}" />

</layout>
Loading

0 comments on commit e5111b3

Please sign in to comment.