From 318508b4aab2d3b8d254c72f3b00ea83c4de766b Mon Sep 17 00:00:00 2001 From: Eli Hart Date: Tue, 12 Sep 2017 23:17:28 -0700 Subject: [PATCH 1/2] Allow AutoModel usage without an xml layout --- .../main/java/com/airbnb/epoxy/ModelView.java | 11 ++ .../java/com/airbnb/epoxy/ClassNames.java | 2 + .../com/airbnb/epoxy/GeneratedModelInfo.java | 9 +- .../airbnb/epoxy/GeneratedModelWriter.java | 101 +++++++++++++----- .../java/com/airbnb/epoxy/ModelViewInfo.java | 1 + .../airbnb/epoxy/sample/views/HeaderView.java | 8 +- 6 files changed, 103 insertions(+), 29 deletions(-) diff --git a/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java b/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java index 1552f6151e..51b573bdca 100644 --- a/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java +++ b/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java @@ -16,6 +16,17 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface ModelView { + + enum Size { + NONE, + WRAP_WIDTH_WRAP_HEIGHT, + WRAP_WIDTH_MATCH_HEIGHT, + MATCH_WIDTH_WRAP_HEIGHT, + MATCH_WIDTH_MATCH_HEIGHT + } + + Size autoLayout() default Size.NONE; + /** * The layout file to use in the generated model to inflate the view. This is required unless a * default pattern is set via {@link PackageModelViewConfig}. diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ClassNames.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/ClassNames.java index 11aabd794b..61fb3edf65 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ClassNames.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ClassNames.java @@ -19,6 +19,8 @@ private ClassNames() { static final ClassName ANDROID_CONTEXT = get(PKG_ANDROID_CONTENT, "Context"); static final ClassName ANDROID_VIEW = get(PKG_ANDROID_VIEW, "View"); static final ClassName ANDROID_VIEW_GROUP = get(PKG_ANDROID_VIEW, "ViewGroup"); + static final ClassName ANDROID_MARGIN_LAYOUT_PARAMS = + get(PKG_ANDROID_VIEW, "ViewGroup", "MarginLayoutParams"); static final ClassName ANDROID_R = get(PKG_ANDROID, "R"); static final ClassName LITHO_COMPONENT = get(PKG_LITHO, "Component"); diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java index ad2ca3cc76..0b45fed8b3 100755 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java @@ -2,6 +2,7 @@ import android.support.annotation.Nullable; +import com.airbnb.epoxy.ModelView.Size; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; @@ -64,6 +65,8 @@ abstract class GeneratedModelInfo { */ private ParisStyleAttributeInfo styleBuilderInfo; + Size layoutParams = Size.NONE; + /** * Get information about methods returning class type of the original class so we can duplicate * them in the generated class for chaining purposes @@ -204,12 +207,16 @@ boolean isStyleable() { return getStyleBuilderInfo() != null; } - public void setStyleable( + void setStyleable( @NotNull ParisStyleAttributeInfo parisStyleAttributeInfo) { styleBuilderInfo = parisStyleAttributeInfo; addAttribute(parisStyleAttributeInfo); } + boolean isProgrammaticView() { + return isStyleable() || layoutParams != Size.NONE; + } + static class ConstructorInfo { final Set modifiers; final List params; diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java index e41db06440..d424ec473b 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java @@ -75,6 +75,11 @@ class GeneratedModelWriter { private static final String GET_DEFAULT_LAYOUT_METHOD_NAME = "getDefaultLayout"; static final String ATTRIBUTES_BITSET_FIELD_NAME = "assignedAttributes" + GENERATED_FIELD_SUFFIX; + private static final CodeBlock LAYOUT_PARAMS_MATCH_PARENT = + CodeBlock.of("$T.MATCH_PARENT", ClassNames.ANDROID_MARGIN_LAYOUT_PARAMS); + private static final CodeBlock LAYOUT_PARAMS_WRAP_CONTENT = + CodeBlock.of("$T.WRAP_CONTENT", ClassNames.ANDROID_MARGIN_LAYOUT_PARAMS); + private final Filer filer; private final Types typeUtils; private final ErrorLogger errorLogger; @@ -151,6 +156,7 @@ void generateClassForModel(GeneratedModelInfo info, BuilderHooks builderHooks) generateDebugAddToMethodIfNeeded(builder, info); builder + .addMethods(generateProgrammaticViewMethods(info)) .addMethods(generateBindMethods(info)) .addMethods(generateStyleableViewMethods(info)) .addMethods(generateSettersAndGetters(info)) @@ -175,7 +181,7 @@ void generateClassForModel(GeneratedModelInfo info, BuilderHooks builderHooks) private Iterable generateOtherLayoutOptions(GeneratedModelInfo info) { if (!info.includeOtherLayoutOptions - || info.isStyleable()) { // Layout resources can't be mixed with programmatic styles + || info.isProgrammaticView()) { // Layout resources can't be mixed with programmatic views return Collections.emptyList(); } @@ -410,6 +416,66 @@ private static int attributeIndex(GeneratedModelInfo modelInfo, AttributeInfo at return index; } + private Iterable generateProgrammaticViewMethods(GeneratedModelInfo modelInfo) { + + if (!modelInfo.isProgrammaticView()) { + return Collections.emptyList(); + } + + List methods = new ArrayList<>(); + + // getViewType method so that view type is generated at runtime + methods.add(MethodSpec.methodBuilder("getViewType") + .addAnnotation(Override.class) + .addModifiers(PROTECTED) + .returns(TypeName.INT) + .addStatement("return 0", modelInfo.boundObjectTypeName) + .build()); + + // buildView method to return new view instance + Builder builder = MethodSpec.methodBuilder("buildView") + .addAnnotation(Override.class) + .addParameter(ClassNames.ANDROID_VIEW_GROUP, "parent") + .addModifiers(PROTECTED) + .returns(modelInfo.boundObjectTypeName) + .addStatement("$T v = new $T(parent.getContext())", modelInfo.boundObjectTypeName, + modelInfo.boundObjectTypeName); + + CodeBlock layoutWidth; + CodeBlock layoutHeight; + switch (modelInfo.layoutParams) { + case WRAP_WIDTH_MATCH_HEIGHT: + layoutWidth = LAYOUT_PARAMS_WRAP_CONTENT; + layoutHeight = LAYOUT_PARAMS_MATCH_PARENT; + break; + case MATCH_WIDTH_MATCH_HEIGHT: + layoutWidth = LAYOUT_PARAMS_MATCH_PARENT; + layoutHeight = LAYOUT_PARAMS_MATCH_PARENT; + break; + case MATCH_WIDTH_WRAP_HEIGHT: + layoutWidth = LAYOUT_PARAMS_MATCH_PARENT; + layoutHeight = LAYOUT_PARAMS_WRAP_CONTENT; + break; + case WRAP_WIDTH_WRAP_HEIGHT: + default: + layoutWidth = LAYOUT_PARAMS_WRAP_CONTENT; + layoutHeight = LAYOUT_PARAMS_WRAP_CONTENT; + } + + builder + .addStatement("v.setLayoutParams(new $T($L, $L))", + ClassNames.ANDROID_MARGIN_LAYOUT_PARAMS, layoutWidth, layoutHeight); + + ParisStyleAttributeInfo styleBuilderInfo = modelInfo.getStyleBuilderInfo(); + if (styleBuilderInfo != null) { + addStyleApplierCode(builder, styleBuilderInfo, "v"); + } + + methods.add(builder.addStatement("return v").build()); + + return methods; + } + private Iterable generateBindMethods(GeneratedModelInfo classInfo) { List methods = new ArrayList<>(); @@ -600,28 +666,6 @@ private Iterable generateStyleableViewMethods(GeneratedModelInfo mod TypeName styleType = styleBuilderInfo.getTypeName(); ClassName styleBuilderClass = styleBuilderInfo.getStyleBuilderClass(); - // buildView method to return new view instance - Builder buildViewMethodBuilder = MethodSpec.methodBuilder("buildView") - .addAnnotation(Override.class) - .addParameter(ClassNames.ANDROID_VIEW_GROUP, "parent") - .addModifiers(PROTECTED) - .returns(modelInfo.boundObjectTypeName) - .addStatement("$T v = new $T(parent.getContext())", modelInfo.boundObjectTypeName, - modelInfo.boundObjectTypeName) - .addStatement("v.setLayoutParams(parent.generateLayoutParams(null))"); - - addStyleApplierCode(buildViewMethodBuilder, styleBuilderInfo, "v"); - buildViewMethodBuilder.addStatement("return v"); - methods.add(buildViewMethodBuilder.build()); - - // getViewType method so that view type is generated at runtime - methods.add(MethodSpec.methodBuilder("getViewType") - .addAnnotation(Override.class) - .addModifiers(PROTECTED) - .returns(TypeName.INT) - .addStatement("return 0", modelInfo.boundObjectTypeName) - .build()); - // setter for style object Builder builder = MethodSpec.methodBuilder(PARIS_STYLE_ATTR_NAME) .addModifiers(PUBLIC) @@ -684,13 +728,14 @@ private Iterable generateMethodsReturningClassType(GeneratedModelInf .varargs(methodInfo.varargs) .returns(info.getParameterizedGeneratedName()); - if (info.isStyleable() + if (info.isProgrammaticView() && "layout".equals(methodInfo.name) && methodInfo.params.size() == 1 && methodInfo.params.get(0).type == TypeName.INT) { builder - .addStatement("throw new $T(\"Layout resources are unsupported in @Styleable views.\")", + .addStatement( + "throw new $T(\"Layout resources are unsupported with programmatic views.\")", UnsupportedOperationException.class); } else { @@ -716,10 +761,12 @@ private Iterable generateMethodsReturningClassType(GeneratedModelInf private Iterable generateDefaultMethodImplementations(GeneratedModelInfo info) { List methods = new ArrayList<>(); - if (info.isStyleable()) { + if (info.isProgrammaticView()) { methods.add(buildDefaultLayoutMethodBase() .toBuilder() - .addStatement("throw new $T(\"Layout resources are unsupported in @Styleable views\")", + .addStatement( + "throw new $T(\"Layout resources are unsupported for views created programmatically" + + ".\")", UnsupportedOperationException.class) .build()); } else { diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java index 35cf7e02f7..5d4c2e8eba 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java @@ -60,6 +60,7 @@ class ModelViewInfo extends GeneratedModelInfo { boundObjectTypeName = ClassName.get(viewElement.asType()); saveViewState = viewAnnotation.saveViewState(); + layoutParams = viewAnnotation.autoLayout(); fullSpanSize = viewAnnotation.fullSpan(); includeOtherLayoutOptions = configManager.includeAlternateLayoutsForViews(viewElement); } diff --git a/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java b/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java index 7808662ffc..41e57ca54e 100755 --- a/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java +++ b/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java @@ -8,13 +8,14 @@ import com.airbnb.epoxy.ModelProp; import com.airbnb.epoxy.ModelProp.Option; import com.airbnb.epoxy.ModelView; +import com.airbnb.epoxy.ModelView.Size; import com.airbnb.epoxy.R; import com.airbnb.epoxy.TextProp; import butterknife.BindView; import butterknife.ButterKnife; -@ModelView +@ModelView(autoLayout = Size.WRAP_WIDTH_WRAP_HEIGHT) public class HeaderView extends LinearLayout { @BindView(R.id.title_text) TextView title; @@ -25,6 +26,11 @@ public HeaderView(Context context, AttributeSet attrs) { init(); } + public HeaderView(Context context) { + super(context); + init(); + } + private void init() { setOrientation(VERTICAL); inflate(getContext(), R.layout.view_header, this); From 594fb964971c0b75d3220debb43b92960f1071cd Mon Sep 17 00:00:00 2001 From: Eli Hart Date: Wed, 13 Sep 2017 15:43:25 -0700 Subject: [PATCH 2/2] tests --- .../main/java/com/airbnb/epoxy/ModelView.java | 16 +- .../com/airbnb/epoxy/GeneratedModelInfo.java | 4 + .../airbnb/epoxy/GeneratedModelWriter.java | 1 + .../com/airbnb/epoxy/ViewProcessorTest.java | 10 + .../test/resources/AutoLayoutModelView.java | 17 ++ .../AutoLayoutModelViewMatchParent.java | 19 ++ .../AutoLayoutModelViewMatchParentModel_.java | 254 ++++++++++++++++++ .../resources/AutoLayoutModelViewModel_.java | 254 ++++++++++++++++++ .../airbnb/epoxy/sample/views/HeaderView.java | 6 - .../src/main/res/layout/header_view.xml | 5 - 10 files changed, 574 insertions(+), 12 deletions(-) create mode 100644 epoxy-processortest/src/test/resources/AutoLayoutModelView.java create mode 100644 epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParent.java create mode 100644 epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParentModel_.java create mode 100644 epoxy-processortest/src/test/resources/AutoLayoutModelViewModel_.java delete mode 100755 epoxy-sample/src/main/res/layout/header_view.xml diff --git a/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java b/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java index 51b573bdca..b1362837eb 100644 --- a/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java +++ b/epoxy-annotations/src/main/java/com/airbnb/epoxy/ModelView.java @@ -17,6 +17,11 @@ @Retention(RetentionPolicy.CLASS) public @interface ModelView { + /** + * Use with {@link #autoLayout()} to declare what layout parameters should be used to size your + * view when it is added to a RecyclerView. This maps to the LayoutParams options {@code + * layout_width} and {@code layout_height}. + */ enum Size { NONE, WRAP_WIDTH_WRAP_HEIGHT, @@ -25,11 +30,20 @@ enum Size { MATCH_WIDTH_MATCH_HEIGHT } + /** + * If set to an option besides {@link Size#NONE} Epoxy will create an instance of this view + * programmatically at runtime instead of inflating the view from xml. This is an alternative to + * using {@link #defaultLayout()}, and is a good option if you just need to specify layout + * parameters on your view with no other styling. + *

+ * The size option you choose will define which layout parameters Epoxy uses at runtime when + * creating the view. + */ Size autoLayout() default Size.NONE; /** * The layout file to use in the generated model to inflate the view. This is required unless a - * default pattern is set via {@link PackageModelViewConfig}. + * default pattern is set via {@link PackageModelViewConfig} or {@link #autoLayout()} is used. *

* Overrides any default set in {@link PackageModelViewConfig} */ diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java index 0b45fed8b3..1c8ed392ee 100755 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java @@ -65,6 +65,10 @@ abstract class GeneratedModelInfo { */ private ParisStyleAttributeInfo styleBuilderInfo; + /** + * An option set via {@link ModelView#autoLayout()} to have Epoxy create the view programmatically + * instead of via xml layout resource inflation. + */ Size layoutParams = Size.NONE; /** diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java index d424ec473b..dc0dc1c91b 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java @@ -458,6 +458,7 @@ private Iterable generateProgrammaticViewMethods(GeneratedModelInfo break; case WRAP_WIDTH_WRAP_HEIGHT: default: + // This will be used for Styleable views as the default layoutWidth = LAYOUT_PARAMS_WRAP_CONTENT; layoutHeight = LAYOUT_PARAMS_WRAP_CONTENT; } diff --git a/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.java b/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.java index fb4b5f4e9e..1c86fe01c8 100644 --- a/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.java +++ b/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.java @@ -700,4 +700,14 @@ public void callbackPropMustBeNullable() { public void testModelBuilderInterface() { assertGeneration("TestManyTypesView.java", "TestManyTypesViewModelBuilder.java"); } + + @Test + public void testAutoLayout() { + assertGeneration("AutoLayoutModelView.java", "AutoLayoutModelViewModel_.java"); + } + + @Test + public void testAutoLayoutMatchParent() { + assertGeneration("AutoLayoutModelViewMatchParent.java", "AutoLayoutModelViewMatchParentModel_.java"); + } } diff --git a/epoxy-processortest/src/test/resources/AutoLayoutModelView.java b/epoxy-processortest/src/test/resources/AutoLayoutModelView.java new file mode 100644 index 0000000000..b766d95c29 --- /dev/null +++ b/epoxy-processortest/src/test/resources/AutoLayoutModelView.java @@ -0,0 +1,17 @@ +package com.airbnb.epoxy; + +import android.content.Context; +import android.view.View; + +@ModelView(autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT) +public class AutoLayoutModelView extends View { + + public AutoLayoutModelView(Context context) { + super(context); + } + + @ModelProp + void setValue(int value) { + + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParent.java b/epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParent.java new file mode 100644 index 0000000000..c574323967 --- /dev/null +++ b/epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParent.java @@ -0,0 +1,19 @@ +package com.airbnb.epoxy; + +import android.content.Context; +import android.view.View; + +import com.airbnb.epoxy.ModelView.Size; + +@ModelView(autoLayout = Size.MATCH_WIDTH_MATCH_HEIGHT) +public class AutoLayoutModelViewMatchParent extends View { + + public AutoLayoutModelViewMatchParent(Context context) { + super(context); + } + + @ModelProp + void setValue(int value) { + + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParentModel_.java b/epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParentModel_.java new file mode 100644 index 0000000000..8ddc1d615f --- /dev/null +++ b/epoxy-processortest/src/test/resources/AutoLayoutModelViewMatchParentModel_.java @@ -0,0 +1,254 @@ +package com.airbnb.epoxy; + +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.view.ViewGroup; +import java.lang.CharSequence; +import java.lang.Number; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.lang.UnsupportedOperationException; +import java.util.BitSet; + +/** + * Generated file. Do not modify! */ +public class AutoLayoutModelViewMatchParentModel_ extends EpoxyModel implements GeneratedModel, AutoLayoutModelViewMatchParentModelBuilder { + private final BitSet assignedAttributes_epoxyGeneratedModel = new BitSet(1); + + private OnModelBoundListener onModelBoundListener_epoxyGeneratedModel; + + private OnModelUnboundListener onModelUnboundListener_epoxyGeneratedModel; + + /** + * Bitset index: 0 */ + private int value_Int = 0; + + @Override + public void addTo(EpoxyController controller) { + super.addTo(controller); + addWithDebugValidation(controller); + } + + @Override + protected int getViewType() { + return 0; + } + + @Override + protected AutoLayoutModelViewMatchParent buildView(ViewGroup parent) { + AutoLayoutModelViewMatchParent v = new AutoLayoutModelViewMatchParent(parent.getContext()); + v.setLayoutParams(new ViewGroup.MarginLayoutParams(ViewGroup.MarginLayoutParams.MATCH_PARENT, ViewGroup.MarginLayoutParams.MATCH_PARENT)); + return v; + } + + @Override + public void handlePreBind(final EpoxyViewHolder holder, + final AutoLayoutModelViewMatchParent object, int position) { + validateStateHasNotChangedSinceAdded("The model was changed between being added to the controller and being bound.", position); + } + + @Override + public void bind(final AutoLayoutModelViewMatchParent object) { + super.bind(object); + object.setValue(value_Int); + } + + @Override + public void bind(final AutoLayoutModelViewMatchParent object, EpoxyModel previousModel) { + if (!(previousModel instanceof AutoLayoutModelViewMatchParentModel_)) { + bind(object); + return; + } + AutoLayoutModelViewMatchParentModel_ that = (AutoLayoutModelViewMatchParentModel_) previousModel; + super.bind(object); + + if (value_Int != that.value_Int) { + object.setValue(value_Int); + } + } + + @Override + public void handlePostBind(final AutoLayoutModelViewMatchParent object, int position) { + if (onModelBoundListener_epoxyGeneratedModel != null) { + onModelBoundListener_epoxyGeneratedModel.onModelBound(this, object, position); + } + validateStateHasNotChangedSinceAdded("The model was changed during the bind call.", position); + } + + /** + * Register a listener that will be called when this model is bound to a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public AutoLayoutModelViewMatchParentModel_ onBind(OnModelBoundListener listener) { + onMutation(); + this.onModelBoundListener_epoxyGeneratedModel = listener; + return this; + } + + @Override + public void unbind(AutoLayoutModelViewMatchParent object) { + super.unbind(object); + if (onModelUnboundListener_epoxyGeneratedModel != null) { + onModelUnboundListener_epoxyGeneratedModel.onModelUnbound(this, object); + } + } + + /** + * Register a listener that will be called when this model is unbound from a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public AutoLayoutModelViewMatchParentModel_ onUnbind(OnModelUnboundListener listener) { + onMutation(); + this.onModelUnboundListener_epoxyGeneratedModel = listener; + return this; + } + + /** + * Optional: Default value is 0 + * + * @see AutoLayoutModelViewMatchParent#setValue(int) + */ + public AutoLayoutModelViewMatchParentModel_ value(int value) { + assignedAttributes_epoxyGeneratedModel.set(0); + onMutation(); + this.value_Int = value; + return this; + } + + public int value() { + return value_Int; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ id(long id) { + super.id(id); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ id(Number... ids) { + super.id(ids); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ id(long id1, long id2) { + super.id(id1, id2); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ id(CharSequence key) { + super.id(key); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ id(CharSequence key, CharSequence... otherKeys) { + super.id(key, otherKeys); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ id(CharSequence key, long id) { + super.id(key, id); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ layout(@LayoutRes int arg0) { + throw new UnsupportedOperationException("Layout resources are unsupported with programmatic views."); + } + + @Override + public AutoLayoutModelViewMatchParentModel_ spanSizeOverride(@Nullable EpoxyModel.SpanSizeOverrideCallback arg0) { + super.spanSizeOverride(arg0); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ show() { + super.show(); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ show(boolean show) { + super.show(show); + return this; + } + + @Override + public AutoLayoutModelViewMatchParentModel_ hide() { + super.hide(); + return this; + } + + @Override + @LayoutRes + protected int getDefaultLayout() { + throw new UnsupportedOperationException("Layout resources are unsupported for views created programmatically."); + } + + @Override + public AutoLayoutModelViewMatchParentModel_ reset() { + onModelBoundListener_epoxyGeneratedModel = null; + onModelUnboundListener_epoxyGeneratedModel = null; + assignedAttributes_epoxyGeneratedModel.clear(); + this.value_Int = 0; + super.reset(); + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof AutoLayoutModelViewMatchParentModel_)) { + return false; + } + if (!super.equals(o)) { + return false; + } + AutoLayoutModelViewMatchParentModel_ that = (AutoLayoutModelViewMatchParentModel_) o; + if ((onModelBoundListener_epoxyGeneratedModel == null) != (that.onModelBoundListener_epoxyGeneratedModel == null)) { + return false; + } + if ((onModelUnboundListener_epoxyGeneratedModel == null) != (that.onModelUnboundListener_epoxyGeneratedModel == null)) { + return false; + } + if (value_Int != that.value_Int) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (onModelBoundListener_epoxyGeneratedModel != null ? 1 : 0); + result = 31 * result + (onModelUnboundListener_epoxyGeneratedModel != null ? 1 : 0); + result = 31 * result + value_Int; + return result; + } + + @Override + public String toString() { + return "AutoLayoutModelViewMatchParentModel_{" + + "value_Int=" + value_Int + + "}" + super.toString(); + } + + @Override + public int getSpanSize(int totalSpanCount, int position, int itemCount) { + return totalSpanCount; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/AutoLayoutModelViewModel_.java b/epoxy-processortest/src/test/resources/AutoLayoutModelViewModel_.java new file mode 100644 index 0000000000..4a8452c731 --- /dev/null +++ b/epoxy-processortest/src/test/resources/AutoLayoutModelViewModel_.java @@ -0,0 +1,254 @@ +package com.airbnb.epoxy; + +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.view.ViewGroup; +import java.lang.CharSequence; +import java.lang.Number; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.lang.UnsupportedOperationException; +import java.util.BitSet; + +/** + * Generated file. Do not modify! */ +public class AutoLayoutModelViewModel_ extends EpoxyModel implements GeneratedModel, AutoLayoutModelViewModelBuilder { + private final BitSet assignedAttributes_epoxyGeneratedModel = new BitSet(1); + + private OnModelBoundListener onModelBoundListener_epoxyGeneratedModel; + + private OnModelUnboundListener onModelUnboundListener_epoxyGeneratedModel; + + /** + * Bitset index: 0 */ + private int value_Int = 0; + + @Override + public void addTo(EpoxyController controller) { + super.addTo(controller); + addWithDebugValidation(controller); + } + + @Override + protected int getViewType() { + return 0; + } + + @Override + protected AutoLayoutModelView buildView(ViewGroup parent) { + AutoLayoutModelView v = new AutoLayoutModelView(parent.getContext()); + v.setLayoutParams(new ViewGroup.MarginLayoutParams(ViewGroup.MarginLayoutParams.WRAP_CONTENT, ViewGroup.MarginLayoutParams.WRAP_CONTENT)); + return v; + } + + @Override + public void handlePreBind(final EpoxyViewHolder holder, final AutoLayoutModelView object, + int position) { + validateStateHasNotChangedSinceAdded("The model was changed between being added to the controller and being bound.", position); + } + + @Override + public void bind(final AutoLayoutModelView object) { + super.bind(object); + object.setValue(value_Int); + } + + @Override + public void bind(final AutoLayoutModelView object, EpoxyModel previousModel) { + if (!(previousModel instanceof AutoLayoutModelViewModel_)) { + bind(object); + return; + } + AutoLayoutModelViewModel_ that = (AutoLayoutModelViewModel_) previousModel; + super.bind(object); + + if (value_Int != that.value_Int) { + object.setValue(value_Int); + } + } + + @Override + public void handlePostBind(final AutoLayoutModelView object, int position) { + if (onModelBoundListener_epoxyGeneratedModel != null) { + onModelBoundListener_epoxyGeneratedModel.onModelBound(this, object, position); + } + validateStateHasNotChangedSinceAdded("The model was changed during the bind call.", position); + } + + /** + * Register a listener that will be called when this model is bound to a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public AutoLayoutModelViewModel_ onBind(OnModelBoundListener listener) { + onMutation(); + this.onModelBoundListener_epoxyGeneratedModel = listener; + return this; + } + + @Override + public void unbind(AutoLayoutModelView object) { + super.unbind(object); + if (onModelUnboundListener_epoxyGeneratedModel != null) { + onModelUnboundListener_epoxyGeneratedModel.onModelUnbound(this, object); + } + } + + /** + * Register a listener that will be called when this model is unbound from a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public AutoLayoutModelViewModel_ onUnbind(OnModelUnboundListener listener) { + onMutation(); + this.onModelUnboundListener_epoxyGeneratedModel = listener; + return this; + } + + /** + * Optional: Default value is 0 + * + * @see AutoLayoutModelView#setValue(int) + */ + public AutoLayoutModelViewModel_ value(int value) { + assignedAttributes_epoxyGeneratedModel.set(0); + onMutation(); + this.value_Int = value; + return this; + } + + public int value() { + return value_Int; + } + + @Override + public AutoLayoutModelViewModel_ id(long id) { + super.id(id); + return this; + } + + @Override + public AutoLayoutModelViewModel_ id(Number... ids) { + super.id(ids); + return this; + } + + @Override + public AutoLayoutModelViewModel_ id(long id1, long id2) { + super.id(id1, id2); + return this; + } + + @Override + public AutoLayoutModelViewModel_ id(CharSequence key) { + super.id(key); + return this; + } + + @Override + public AutoLayoutModelViewModel_ id(CharSequence key, CharSequence... otherKeys) { + super.id(key, otherKeys); + return this; + } + + @Override + public AutoLayoutModelViewModel_ id(CharSequence key, long id) { + super.id(key, id); + return this; + } + + @Override + public AutoLayoutModelViewModel_ layout(@LayoutRes int arg0) { + throw new UnsupportedOperationException("Layout resources are unsupported with programmatic views."); + } + + @Override + public AutoLayoutModelViewModel_ spanSizeOverride(@Nullable EpoxyModel.SpanSizeOverrideCallback arg0) { + super.spanSizeOverride(arg0); + return this; + } + + @Override + public AutoLayoutModelViewModel_ show() { + super.show(); + return this; + } + + @Override + public AutoLayoutModelViewModel_ show(boolean show) { + super.show(show); + return this; + } + + @Override + public AutoLayoutModelViewModel_ hide() { + super.hide(); + return this; + } + + @Override + @LayoutRes + protected int getDefaultLayout() { + throw new UnsupportedOperationException("Layout resources are unsupported for views created programmatically."); + } + + @Override + public AutoLayoutModelViewModel_ reset() { + onModelBoundListener_epoxyGeneratedModel = null; + onModelUnboundListener_epoxyGeneratedModel = null; + assignedAttributes_epoxyGeneratedModel.clear(); + this.value_Int = 0; + super.reset(); + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof AutoLayoutModelViewModel_)) { + return false; + } + if (!super.equals(o)) { + return false; + } + AutoLayoutModelViewModel_ that = (AutoLayoutModelViewModel_) o; + if ((onModelBoundListener_epoxyGeneratedModel == null) != (that.onModelBoundListener_epoxyGeneratedModel == null)) { + return false; + } + if ((onModelUnboundListener_epoxyGeneratedModel == null) != (that.onModelUnboundListener_epoxyGeneratedModel == null)) { + return false; + } + if (value_Int != that.value_Int) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (onModelBoundListener_epoxyGeneratedModel != null ? 1 : 0); + result = 31 * result + (onModelUnboundListener_epoxyGeneratedModel != null ? 1 : 0); + result = 31 * result + value_Int; + return result; + } + + @Override + public String toString() { + return "AutoLayoutModelViewModel_{" + + "value_Int=" + value_Int + + "}" + super.toString(); + } + + @Override + public int getSpanSize(int totalSpanCount, int position, int itemCount) { + return totalSpanCount; + } +} \ No newline at end of file diff --git a/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java b/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java index 41e57ca54e..2077c3e97b 100755 --- a/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java +++ b/epoxy-sample/src/main/java/com/airbnb/epoxy/sample/views/HeaderView.java @@ -1,7 +1,6 @@ package com.airbnb.epoxy.sample.views; import android.content.Context; -import android.util.AttributeSet; import android.widget.LinearLayout; import android.widget.TextView; @@ -21,11 +20,6 @@ public class HeaderView extends LinearLayout { @BindView(R.id.title_text) TextView title; @BindView(R.id.caption_text) TextView caption; - public HeaderView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - public HeaderView(Context context) { super(context); init(); diff --git a/epoxy-sample/src/main/res/layout/header_view.xml b/epoxy-sample/src/main/res/layout/header_view.xml deleted file mode 100755 index e7fb47d362..0000000000 --- a/epoxy-sample/src/main/res/layout/header_view.xml +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file