-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
474 additions
and
15 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
app/src/main/java/com/ebr163/android/attributesdispatcher/MyCustomView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.ebr163.android.attributesdispatcher; | ||
|
||
import android.content.Context; | ||
import android.util.AttributeSet; | ||
import android.widget.EditText; | ||
|
||
import com.ebr163.attributesdispatcher.Attribute; | ||
import com.ebr163.attributesdispatcher.CustomView; | ||
import com.ebr163.attributesdispatcher.attr.ColorAttr; | ||
import com.ebr163.attributesdispatcher.attr.StringAttr; | ||
|
||
/** | ||
* Created by mac1 on 24.11.16. | ||
*/ | ||
|
||
@CustomView | ||
public class MyCustomView extends EditText { | ||
|
||
@ColorAttr("custom_color") | ||
protected int color; | ||
@StringAttr("custom_text") | ||
protected String text; | ||
|
||
public MyCustomView(Context context) { | ||
super(context); | ||
} | ||
|
||
public MyCustomView(Context context, AttributeSet attrs) { | ||
super(context, attrs); | ||
MyCustomViewAttribute.init(this, attrs); | ||
} | ||
|
||
public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) { | ||
super(context, attrs, defStyleAttr); | ||
MyCustomViewAttribute.init(this, attrs); | ||
} | ||
|
||
@Attribute | ||
protected void setCustomAttr(@ColorAttr("custom_color") int color) { | ||
this.setTextColor(color); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,26 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<RelativeLayout | ||
xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools" | ||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:app="http://schemas.android.com/apk/res-auto" | ||
android:id="@+id/activity_main" | ||
android:orientation="vertical" | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent" | ||
android:paddingBottom="@dimen/activity_vertical_margin" | ||
android:paddingLeft="@dimen/activity_horizontal_margin" | ||
android:paddingRight="@dimen/activity_horizontal_margin" | ||
android:paddingTop="@dimen/activity_vertical_margin" | ||
android:paddingBottom="@dimen/activity_vertical_margin" | ||
tools:context="com.ebr163.android.attributesdispatcher.MainActivity"> | ||
android:paddingTop="@dimen/activity_vertical_margin"> | ||
|
||
<com.ebr163.android.attributesdispatcher.MyCustomView | ||
android:id="@+id/custom_view" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
app:custom_text="Text1" | ||
app:custom_color="@android:color/holo_blue_light" /> | ||
|
||
<TextView | ||
android:layout_width="wrap_content" | ||
<com.ebr163.android.attributesdispatcher.MyCustomView | ||
android:id="@+id/custom_view1" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
android:text="Hello World!" /> | ||
</RelativeLayout> | ||
app:custom_text="Text2" | ||
app:custom_color="@android:color/holo_red_dark" /> | ||
</LinearLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<resources> | ||
<declare-styleable name="MyCustomView"> | ||
<attr name="custom_text" format="string" /> | ||
<attr name="custom_color" format="color" /> | ||
</declare-styleable> | ||
</resources> |
208 changes: 207 additions & 1 deletion
208
compiler/src/main/java/com/ebr163/attributesdispatcher/internal/AttributesProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,220 @@ | ||
package com.ebr163.attributesdispatcher.internal; | ||
|
||
import com.ebr163.attributesdispatcher.Attribute; | ||
import com.ebr163.attributesdispatcher.CustomView; | ||
import com.ebr163.attributesdispatcher.attr.BooleanAttr; | ||
import com.ebr163.attributesdispatcher.attr.ColorAttr; | ||
import com.ebr163.attributesdispatcher.attr.DimenAttr; | ||
import com.ebr163.attributesdispatcher.attr.FloatAttr; | ||
import com.ebr163.attributesdispatcher.attr.IntAttr; | ||
import com.ebr163.attributesdispatcher.attr.ReferenceAttr; | ||
import com.ebr163.attributesdispatcher.attr.StringAttr; | ||
import com.squareup.javapoet.ClassName; | ||
import com.squareup.javapoet.JavaFile; | ||
import com.squareup.javapoet.MethodSpec; | ||
import com.squareup.javapoet.ParameterSpec; | ||
import com.squareup.javapoet.TypeName; | ||
import com.squareup.javapoet.TypeSpec; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
import javax.annotation.processing.AbstractProcessor; | ||
import javax.annotation.processing.RoundEnvironment; | ||
import javax.annotation.processing.SupportedAnnotationTypes; | ||
import javax.lang.model.SourceVersion; | ||
import javax.lang.model.element.Element; | ||
import javax.lang.model.element.ExecutableElement; | ||
import javax.lang.model.element.Modifier; | ||
import javax.lang.model.element.TypeElement; | ||
import javax.lang.model.type.DeclaredType; | ||
|
||
@SupportedAnnotationTypes("com.ebr163.attributesdispatcher.CustomView") | ||
public class AttributesProcessor extends AbstractProcessor { | ||
private static final String METHOD_PREFIX = "Generation"; | ||
|
||
private ClassName AttributeSet = ClassName.get("android.util", "AttributeSet"); | ||
private ClassName TypedArray = ClassName.get("android.content.res", "TypedArray"); | ||
private ClassName Build = ClassName.get("android.os", "Build"); | ||
|
||
@Override | ||
public SourceVersion getSupportedSourceVersion() { | ||
return SourceVersion.latestSupported(); | ||
} | ||
|
||
@Override | ||
public Set<String> getSupportedAnnotationTypes() { | ||
Set<String> set = new HashSet<>(); | ||
set.add(CustomView.class.getCanonicalName()); | ||
return Collections.unmodifiableSet(set); | ||
} | ||
|
||
@Override | ||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { | ||
return false; | ||
for (Element element : roundEnv.getElementsAnnotatedWith(CustomView.class)) { | ||
JavaFile javaFile = createJavaFile(new CustomViewElement((TypeElement) element)); | ||
try { | ||
javaFile.writeTo(processingEnv.getFiler()); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
private JavaFile createJavaFile(CustomViewElement customViewElement) { | ||
return JavaFile.builder(customViewElement.packageName, createTypeSpec(customViewElement)) | ||
.addFileComment("This file was generated by AttrGenerator. Do not modify!") | ||
.build(); | ||
} | ||
|
||
private TypeSpec createTypeSpec(CustomViewElement customViewElement) { | ||
TypeSpec.Builder builder = TypeSpec.classBuilder(customViewElement.generatedClassName) | ||
.addModifiers(Modifier.FINAL) | ||
.addMethod(createConstructor()) | ||
.addMethods(createAttrMethods(customViewElement)) | ||
.addMethod(createInitMethod(customViewElement)); | ||
return builder.build(); | ||
|
||
|
||
} | ||
|
||
private MethodSpec createConstructor() { | ||
return MethodSpec.constructorBuilder() | ||
.addModifiers(Modifier.PRIVATE) | ||
.build(); | ||
} | ||
|
||
private MethodSpec createInitMethod(CustomViewElement customViewElement) { | ||
MethodSpec.Builder builder = MethodSpec.methodBuilder("init") | ||
.addTypeVariables(customViewElement.typeVariables) | ||
.addModifiers(Modifier.STATIC) | ||
.returns(TypeName.VOID) | ||
.addParameter(customViewElement.typeName, "target") | ||
.addParameter(ParameterSpec.builder(AttributeSet, "attributeSet").build()); | ||
|
||
addAttrGenBodyInit(builder, customViewElement); | ||
return builder.build(); | ||
} | ||
|
||
private List<MethodSpec> createAttrMethods(CustomViewElement customViewElement) { | ||
List<MethodSpec> methods = new ArrayList<>(); | ||
for (ExecutableElement method : customViewElement.attrElements) { | ||
methods.add(createAttrGenMethod(customViewElement, method)); | ||
} | ||
|
||
return methods; | ||
} | ||
|
||
private void addAttrGenBodyInit(MethodSpec.Builder builder, CustomViewElement customViewElement) { | ||
builder.addStatement("$T typedArray = $N.getContext().getTheme().obtainStyledAttributes(\n" + | ||
" $N,\n" + | ||
" R.styleable.$L,\n" + | ||
" 0, 0)", TypedArray, "target", "attributeSet", customViewElement.inputClassName); | ||
|
||
addInitTargetField(builder, customViewElement); | ||
|
||
for (ExecutableElement method : customViewElement.attrElements) { | ||
builder.addStatement(method.getSimpleName().toString() + METHOD_PREFIX + "($N, $N)", "target", "typedArray"); | ||
} | ||
builder.addStatement("$N.recycle()", "typedArray"); | ||
} | ||
|
||
private void addInitTargetField(MethodSpec.Builder builder, CustomViewElement customViewElement) { | ||
for (Element field : customViewElement.booleanElements) { | ||
String value = field.getAnnotation(BooleanAttr.class).value(); | ||
builder.addStatement("$N." + field.getSimpleName().toString() + " = $N.getBoolean(R.styleable.$L_$L, false)", "target", "typedArray", customViewElement.inputClassName, value); | ||
} | ||
|
||
for (Element field : customViewElement.colorElements) { | ||
String value = field.getAnnotation(ColorAttr.class).value(); | ||
builder.addStatement("$N." + field.getSimpleName().toString() + " = $N.getColor(R.styleable.$L_$L, 0)", "target", "typedArray", customViewElement.inputClassName, value); | ||
} | ||
|
||
for (Element field : customViewElement.dimenElements) { | ||
String value = field.getAnnotation(DimenAttr.class).value(); | ||
builder.addStatement("$N." + field.getSimpleName().toString() + " = $N.getDimension(R.styleable.$L_$L, 0f)", "target", "typedArray", customViewElement.inputClassName, value); | ||
} | ||
|
||
for (Element field : customViewElement.floatElements) { | ||
String value = field.getAnnotation(FloatAttr.class).value(); | ||
builder.addStatement("$N." + field.getSimpleName().toString() + " = $N.getFloat(R.styleable.$L_$L, 0f)", "target", "typedArray", customViewElement.inputClassName, value); | ||
} | ||
|
||
for (Element field : customViewElement.integerElements) { | ||
String value = field.getAnnotation(IntAttr.class).value(); | ||
builder.addStatement("$N." + field.getSimpleName().toString() + " = $N.getInt(R.styleable.$L_$L, 0)", "target", "typedArray", customViewElement.inputClassName, value); | ||
} | ||
|
||
for (Element field : customViewElement.referenceElements) { | ||
String value = field.getAnnotation(ReferenceAttr.class).value(); | ||
builder.addStatement("$N." + field.getSimpleName().toString() + " = $N.getResourceId(R.styleable.$L_$L, -1)", "target", "typedArray", customViewElement.inputClassName, value); | ||
} | ||
|
||
for (Element field : customViewElement.stringElements) { | ||
String value = field.getAnnotation(StringAttr.class).value(); | ||
builder.addStatement("$N." + field.getSimpleName().toString() + " = $N.getString(R.styleable.$L_$L)", "target", "typedArray", customViewElement.inputClassName, value); | ||
} | ||
} | ||
|
||
private MethodSpec createAttrGenMethod(CustomViewElement customViewElement, ExecutableElement method) { | ||
MethodSpec.Builder builder = MethodSpec.methodBuilder(method.getSimpleName().toString() + METHOD_PREFIX) | ||
.addTypeVariables(customViewElement.typeVariables) | ||
.addModifiers(Modifier.PRIVATE, Modifier.STATIC) | ||
.returns(TypeName.VOID) | ||
.addParameter(customViewElement.typeName, "target") | ||
.addParameter(ParameterSpec.builder(TypedArray, "typedArray").build()); | ||
|
||
addAttrGenBody(builder, method, customViewElement); | ||
return builder.build(); | ||
} | ||
|
||
private void addAttrGenBody(MethodSpec.Builder builder, ExecutableElement method, CustomViewElement customViewElement) { | ||
int maxSdkVersion = method.getAnnotation(Attribute.class).maxSdkVersion(); | ||
builder.beginControlFlow("if ($T.VERSION.SDK_INT < $L)", Build, maxSdkVersion); | ||
builder.addStatement("return"); | ||
builder.endControlFlow(); | ||
|
||
String params = ""; | ||
for (int i = 0; i < method.getParameters().size(); i++) { | ||
TypeName type = TypeName.get(method.getParameters().get(i).asType()); | ||
String name = method.getParameters().get(i).getSimpleName().toString(); | ||
|
||
Element parameter = method.getParameters().get(i); | ||
DeclaredType annotation = parameter.getAnnotationMirrors().get(0).getAnnotationType(); | ||
|
||
if (annotation.toString().equals(IntAttr.class.getCanonicalName())) { | ||
String key = parameter.getAnnotation(IntAttr.class).value(); | ||
builder.addStatement("$T $N = $N.getInt(R.styleable.$L_$L, 0)", type, name, "typedArray", customViewElement.inputClassName, key); | ||
} else if (annotation.toString().equals(BooleanAttr.class.getCanonicalName())) { | ||
String key = parameter.getAnnotation(BooleanAttr.class).value(); | ||
builder.addStatement("$T $N = $N.getBoolean(R.styleable.$L_$L, false)", type, name, "typedArray", customViewElement.inputClassName, key); | ||
} else if (annotation.toString().equals(ColorAttr.class.getCanonicalName())) { | ||
String key = parameter.getAnnotation(ColorAttr.class).value(); | ||
builder.addStatement("$T $N = $N.getColor(R.styleable.$L_$L, 0)", type, name, "typedArray", customViewElement.inputClassName, key); | ||
} else if (annotation.toString().equals(DimenAttr.class.getCanonicalName())) { | ||
String key = parameter.getAnnotation(DimenAttr.class).value(); | ||
builder.addStatement("$T $N = $N.getDimension(R.styleable.$L_$L, 0f)", type, name, "typedArray", customViewElement.inputClassName, key); | ||
} else if (annotation.toString().equals(FloatAttr.class.getCanonicalName())) { | ||
String key = parameter.getAnnotation(FloatAttr.class).value(); | ||
builder.addStatement("$T $N = $N.getFloat(R.styleable.$L_$L, 0f)", type, name, "typedArray", customViewElement.inputClassName, key); | ||
} else if (annotation.toString().equals(StringAttr.class.getCanonicalName())) { | ||
String key = parameter.getAnnotation(StringAttr.class).value(); | ||
builder.addStatement("$T $N = $N.getString(R.styleable.$L_$L)", type, name, "typedArray", customViewElement.inputClassName, key); | ||
} else if (annotation.toString().equals(ReferenceAttr.class.getCanonicalName())) { | ||
String key = parameter.getAnnotation(ReferenceAttr.class).value(); | ||
builder.addStatement("$T $N = $N.getResourceId(R.styleable.$L_$L, -1)", type, name, "typedArray", customViewElement.inputClassName, key); | ||
} | ||
if (i == 0) { | ||
params += name; | ||
} else { | ||
params += ", " + name; | ||
} | ||
} | ||
builder.addStatement("$N." + method.getSimpleName().toString() + "($L)", "target", params); | ||
} | ||
} |
Oops, something went wrong.