Last active
March 26, 2018 04:52
-
-
Save ChaitanyaPramod/5e44378e105f6f0f33ccc8b3043a0d91 to your computer and use it in GitHub Desktop.
Demystifying Butterknife snippets
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
BindAnim | |
BindArray | |
BindBitmap | |
BindBool | |
BindColor | |
BindDimen | |
BindDrawable | |
BindFloat | |
BindFont | |
BindInt | |
BindString | |
BindView | |
BindViews | |
OnCheckedChanged | |
OnClick | |
OnEditorAction | |
OnFocusChange | |
OnItemClick | |
OnItemLongClick | |
OnItemSelected | |
OnLongClick | |
OnPageChange | |
OnTextChanged | |
OnTouch |
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
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) { | |
Class<?> targetClass = target.getClass(); | |
... | |
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); | |
... | |
try { | |
return constructor.newInstance(target, source); | |
} | |
... | |
} | |
@Nullable @CheckResult @UiThread | |
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) { | |
Constructor<? extends Unbinder> bindingCtor; | |
... // Removed cache code | |
String clsName = cls.getName(); | |
... | |
try { | |
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding"); | |
... | |
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class); | |
... | |
} | |
... | |
return bindingCtor; | |
} |
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
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap, | |
Set<TypeElement> erasedTargetNames) { | |
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); | |
... | |
// Assemble information on the field. | |
int id = element.getAnnotation(BindView.class).value(); | |
BindingSet.Builder builder = builderMap.get(enclosingElement); | |
QualifiedId qualifiedId = elementToQualifiedId(element, id); | |
if (builder != null) { | |
String existingBindingName = builder.findExistingBindingName(getId(qualifiedId)); | |
... | |
} else { | |
builder = getOrCreateBindingBuilder(builderMap, enclosingElement); | |
} | |
String name = simpleName.toString(); | |
TypeName type = TypeName.get(elementType); | |
boolean required = isFieldRequired(element); | |
builder.addField(getId(qualifiedId), new FieldViewBinding(name, type, required)); | |
// Add the type-erased version to the valid binding targets set. | |
erasedTargetNames.add(enclosingElement); | |
} |
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
private void parseListenerAnnotation(Class<? extends Annotation> annotationClass, Element element, | |
Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames) | |
throws Exception { | |
... | |
ExecutableElement executableElement = (ExecutableElement) element; | |
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); | |
// Assemble information on the method. | |
Annotation annotation = element.getAnnotation(annotationClass); | |
Method annotationValue = annotationClass.getDeclaredMethod("value"); | |
... | |
int[] ids = (int[]) annotationValue.invoke(annotation); | |
String name = executableElement.getSimpleName().toString(); | |
boolean required = isListenerRequired(executableElement); | |
... | |
ListenerClass listener = annotationClass.getAnnotation(ListenerClass.class); | |
... | |
ListenerMethod method; | |
ListenerMethod[] methods = listener.method(); | |
if (methods.length > 1) { | |
... | |
} else if (methods.length == 1) { | |
... | |
method = methods[0]; | |
} else { | |
Method annotationCallback = annotationClass.getDeclaredMethod("callback"); | |
Enum<?> callback = (Enum<?>) annotationCallback.invoke(annotation); | |
Field callbackField = callback.getDeclaringClass().getField(callback.name()); | |
method = callbackField.getAnnotation(ListenerMethod.class); | |
... | |
} | |
// Verify that the method has equal to or less than the number of parameters as the listener. | |
List<? extends VariableElement> methodParameters = executableElement.getParameters(); | |
... | |
Parameter[] parameters = Parameter.NONE; | |
if (!methodParameters.isEmpty()) { | |
parameters = new Parameter[methodParameters.size()]; | |
BitSet methodParameterUsed = new BitSet(methodParameters.size()); | |
String[] parameterTypes = method.parameters(); | |
for (int i = 0; i < methodParameters.size(); i++) { | |
VariableElement methodParameter = methodParameters.get(i); | |
TypeMirror methodParameterType = methodParameter.asType(); | |
if (methodParameterType instanceof TypeVariable) { | |
TypeVariable typeVariable = (TypeVariable) methodParameterType; | |
methodParameterType = typeVariable.getUpperBound(); | |
} | |
for (int j = 0; j < parameterTypes.length; j++) { | |
if (methodParameterUsed.get(j)) { | |
continue; | |
} | |
if ((isSubtypeOfType(methodParameterType, parameterTypes[j]) | |
&& isSubtypeOfType(methodParameterType, VIEW_TYPE)) | |
|| isTypeEqual(methodParameterType, parameterTypes[j]) | |
|| isInterface(methodParameterType)) { | |
parameters[i] = new Parameter(j, TypeName.get(methodParameterType)); | |
methodParameterUsed.set(j); | |
break; | |
} | |
} | |
... | |
} | |
} | |
MethodViewBinding binding = new MethodViewBinding(name, Arrays.asList(parameters), required); | |
BindingSet.Builder builder = getOrCreateBindingBuilder(builderMap, enclosingElement); | |
for (int id : ids) { | |
QualifiedId qualifiedId = elementToQualifiedId(element, id); | |
builder.addMethod(getId(qualifiedId), listener, method, binding) | |
... | |
} | |
// Add the type-erased version to the valid binding targets set. | |
erasedTargetNames.add(enclosingElement); | |
} |
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
public final class FinalRClassBuilder { | |
... | |
private static final String[] SUPPORTED_TYPES = { | |
"anim", "array", "attr", "bool", "color", "dimen", "drawable", "id", "integer", "layout", "menu", "plurals", | |
"string", "style", "styleable" | |
}; | |
... | |
public static void brewJava(File rFile, File outputDir, String packageName, String className) | |
throws Exception { | |
CompilationUnit compilationUnit = JavaParser.parse(rFile); | |
TypeDeclaration resourceClass = compilationUnit.getTypes().get(0); | |
TypeSpec.Builder result = | |
TypeSpec.classBuilder(className).addModifiers(PUBLIC).addModifiers(FINAL); | |
for (Node node : resourceClass.getChildNodes()) { | |
if (node instanceof ClassOrInterfaceDeclaration) { | |
addResourceType(Arrays.asList(SUPPORTED_TYPES), result, (ClassOrInterfaceDeclaration) node); | |
} | |
} | |
JavaFile finalR = JavaFile.builder(packageName, result.build()) | |
... | |
.build(); | |
finalR.writeTo(outputDir); | |
} | |
private static void addResourceType(List<String> supportedTypes, TypeSpec.Builder result, | |
ClassOrInterfaceDeclaration node) { | |
... | |
String type = node.getNameAsString(); | |
TypeSpec.Builder resourceType = TypeSpec.classBuilder(type).addModifiers(PUBLIC, STATIC, FINAL); | |
for (BodyDeclaration field : node.getMembers()) { | |
if (field instanceof FieldDeclaration) { | |
FieldDeclaration declaration = (FieldDeclaration) field; | |
// Check that the field is an Int because styleable also contains Int arrays which can't be | |
// used in annotations. | |
if (isInt(declaration)) { | |
addResourceField(resourceType, declaration.getVariables().get(0), | |
getSupportAnnotationClass(type)); | |
} | |
} | |
} | |
result.addType(resourceType.build()); | |
} | |
... | |
private static void addResourceField(TypeSpec.Builder resourceType, VariableDeclarator variable, | |
ClassName annotation) { | |
String fieldName = variable.getNameAsString(); | |
String fieldValue = variable.getInitializer().map(Node::toString).orElse(null); | |
FieldSpec.Builder fieldSpecBuilder = FieldSpec.builder(int.class, fieldName) | |
.addModifiers(PUBLIC, STATIC, FINAL) | |
.initializer(fieldValue); | |
... | |
resourceType.addField(fieldSpecBuilder.build()); | |
} | |
... | |
} |
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
... | |
public class MainActivity_ViewBinding implements Unbinder { | |
private MainActivity target; | |
@UiThread | |
public MainActivity_ViewBinding(MainActivity target) { | |
this(target, target.getWindow().getDecorView()); | |
} | |
@UiThread | |
public MainActivity_ViewBinding(MainActivity target, View source) { | |
this.target = target; | |
target.myTextView = Utils.findRequiredViewAsType(source, R.id.my_text_view, "field 'myTextView'", TextView.class); | |
} | |
@Override | |
@CallSuper | |
public void unbind() { | |
... | |
this.target = null; | |
target.myTextView = null; | |
} | |
} |
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
public class MyActivity extends Activity { | |
@BindView(R.id.my_text_view) TextView myTextView; | |
private Unbinder unbinder; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
// instead of reflection using | |
unbinder = Butterknife.bind(this); | |
// you can write | |
unbinder = new MyActivity_ViewBinding(this); | |
myTextView.setText("Ta-da!"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment