Butter Knife
来源:互联网 发布:数据建模培训班 编辑:程序博客网 时间:2024/05/29 18:10
首先是buffer knife这个框架的优势:
1.强大的View绑定和Click事件处理功能,简化代码,提升开发效率
2.方便的处理Adapter里的ViewHolder绑定问题
3.运行时不会影响APP效率,使用配置方便
4.代码清晰,可读性强
注解也叫元数据,例如我们常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它主要的作用有以下四方面:
- 生成文档,通过代码里标识的元数据生成javadoc文档。
- 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
- 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
- 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。
Retention注解
Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值:
1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
2.RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
3.RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
Target注解
Butter Knife 主要使用的是编译时动态处理,通过对编译过程中的注解生成绑定所需要的函数。
所以Butter Knife的使用并不会降低软件运行时的效率,因为他的工作是在代码编译时进行的,生成了所需代码。
在java中实现注解时间处理器:
mybutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSomething()
}
});
如果使用注解的话(使用java简单解释):
@ActionListenerFor(source="myButton")
void doSomething(){....}
public class ActionListenerInstaller { public static void processAnnotation(Object obj) { try{ Class<?> cl = obj.getClass(); for (Method m : cl.getDeclaredMethods()){ ActionListenerFor a = m.getAnnotations(ActionListenerFor.class); if(a != null){ Field f = cl.getDeclaredField(a.source); f.setAccessible(true); addListener(f.get(obj), obj, m); } } }catch (ReflectiveOperationException e) { e.printStackTrace(); } } public static void addListener(Object source, final Object param, final Method m)throws ReflectiveOperationException{ InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return m.invoke(param); } }; Object listener = Proxy.newProxyInstance(null, new Class[]{java.awt.event.ActionListener.class}, handler); Method adder = source.getClass().getMethod("addActionListener", ActionListener.class); adder.invoke(source, listener); } }
对外注解ji
*/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface ActionListenerFor { String source();}
butterknife主要包含三部分
1.butterknife可以被调用的公用方法,这里相当于对android本身的findviewbyid等操作进行了一次封装
2.butterknife-annotation的注解接口定义
3.通过编译对butterknife的方法进行代码生成(工程代码的编译过程,也就是butterknife的运行过程,因为它是一个在编译过程中生成代码的工具。)
注解接口定义分为两部分
先是定义监听类型以及监听方法:
@Retention(RUNTIME) @Target(ANNOTATION_TYPE)public @interface ListenerClass { String targetType(); /** Name of the setter method on the {@linkplain #targetType() target type} for the listener. */ String setter(); /** * Name of the method on the {@linkplain #targetType() target type} to remove the listener. If * empty {@link #setter()} will be used by default. */ String remover() default ""; /** Fully-qualified class name of the listener type. */ String type(); /** Enum which declares the listener callback methods. Mutually exclusive to {@link #method()}. */ Class<? extends Enum<?>> callbacks() default NONE.class; /** * Method data for single-method listener callbacks. Mutually exclusive with {@link #callbacks()} * and an error to specify more than one value. */ ListenerMethod[] method() default { }; /** Default value for {@link #callbacks()}. */ enum NONE { }}
@Retention(RUNTIME) @Target(FIELD)public @interface ListenerMethod { /** Name of the listener method for which this annotation applies. */ String name(); /** List of method parameters. If the type is not a primitive it must be fully-qualified. */ String[] parameters() default { }; /** Primitive or fully-qualified return type of the listener method. May also be {@code void}. */ String returnType() default "void"; /** If {@link #returnType()} is not {@code void} this value is returned when no binding exists. */ String defaultReturn() default "null";}
然后定义具体的操作如OnClick的操作
@Target(METHOD)@Retention(CLASS)@ListenerClass( targetType = "android.widget.AdapterView<?>", setter = "setOnItemClickListener", type = "android.widget.AdapterView.OnItemClickListener", method = @ListenerMethod( name = "onItemClick", parameters = { "android.widget.AdapterView<?>", "android.view.View", "int", "long" } ))public @interface OnItemClick { /** View IDs to which the method will be bound. */ @IdRes int[] value() default { View.NO_ID };}
之后是对文件中的注解进行遍历,这里面主要的方法是
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) { Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env); for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) { TypeElement typeElement = entry.getKey(); BindingSet binding = entry.getValue(); JavaFile javaFile = binding.brewJava(sdk); try { javaFile.writeTo(filer); } catch (IOException e) { error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage()); } } return false;}
这个方法的作用主要是扫描、评估和处理我们程序中的注解,然后生成Java文件。这一操作主要是通过调用findAndParseTargets:对每个注解进行查找与解析
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) { Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>(); Set<TypeElement> erasedTargetNames = new LinkedHashSet<>(); scanForRClasses(env); // Process each @BindArray element. for (Element element : env.getElementsAnnotatedWith(BindArray.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceArray(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindArray.class, e); } } // Process each @BindBitmap element. for (Element element : env.getElementsAnnotatedWith(BindBitmap.class)) { if (!SuperficialValidation.validateElement(element)) continue; try { parseResourceBitmap(element, builderMap, erasedTargetNames); } catch (Exception e) { logParsingError(element, BindBitmap.class, e); } }
- 扫描所有具有注解的类,然后根据这些类的信息生成BindingClass,最后生成以TypeElement为键,BindingClass为值的键值对。
- 循环遍历这个键值对,根据TypeElement和BindingClass里面的信息生成对应的java类。例如AnnotationActivity生成的类即为
Cliass$$ViewBinder
类。
因为我们之前用的例子是绑定的一个View,所以我们就只贴了解析View的代码。好吧,这里遍历了所有带有@BindView
的Element,然后对每一个Element进行解析,也就进入了parseBindView
这个方法中:
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames) { TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); // Start by verifying common generated code restrictions. boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element) || isBindingInWrongPackage(BindView.class, element); // Verify that the target type extends from View. TypeMirror elementType = element.asType(); if (elementType.getKind() == TypeKind.TYPEVAR) { TypeVariable typeVariable = (TypeVariable) elementType; elementType = typeVariable.getUpperBound(); } Name qualifiedName = enclosingElement.getQualifiedName(); Name simpleName = element.getSimpleName(); if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) { if (elementType.getKind() == TypeKind.ERROR) { note(element, "@%s field with unresolved type (%s) " + "must elsewhere be generated as a View or interface. (%s.%s)", BindView.class.getSimpleName(), elementType, qualifiedName, simpleName); } else { error(element, "@%s fields must extend from View or be an interface. (%s.%s)", BindView.class.getSimpleName(), qualifiedName, simpleName); hasError = true; } } if (hasError) { return; } // 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)); if (existingBindingName != null) { error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)", BindView.class.getSimpleName(), id, existingBindingName, enclosingElement.getQualifiedName(), element.getSimpleName()); return; } } 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);}
都是在拿到注解信息,然后验证注解的target的类型是否继承自view,然后上面这一行代码获得我们要绑定的View的id,再从targetClassMap里面取出BindingClass(这个BindingClass是管理了所有关于这个注解的一些信息还有实例本身的信息,其实最后是通过BindingClass来生成java代码的)。
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife
- Butter Knife使用教程
- Butter Knife在adapter
- Butter Knife 和 AndroidAnnotations
- Butter Knife 使用方法
- Butter Knife 的使用方法
- Android Butter Knife使用说明
- Butter Knife 使用方法
- Butter Knife 使用说明
- 根据接口服务端发送请求
- 1,List<VideoTotal> list; 根据VideoTotal的videoNum属性 把list 分组。2,把List<VideoTotal>list 根据VideoTotal属性去重
- Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
- 三目运算
- java:读取文件
- Butter Knife
- 全栈工程师之路-中级篇之小程序开发-第一章第一节注册小程序
- java的Object类的学习
- jq 与原生js 方法互相转换
- C++ explicit关键字详解
- 关于HTTP协议,一篇就够了
- psql命令参考
- java读取properties文件工具类
- matlab图像的读取和保存