自定义xUtils框架
xUtils是基于Afinal开发的目前功能比较完善的一个Android开源框架,最近又发布了xUtil3.0,在增加新功能的同时又提高了框架的性能。它的功能很强大,但是有时候我们只需要其中的一些功能,如果把整个xUtils引进去没什么必要。
下面我们就讲讲如何自定义小型的xUtils,只有两个功能:通过注解找到省去findViewById()和setContentView().
一、首先:要自定义两个注解:
(1)找到activity视图的注解,即用来省去setContentView()的:
<strong>@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ContentView { int value(); }</strong>
(2)找到控件的注解,即用来省去findViewById()的。
<strong>@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface ViewInject { int value(); }</strong>
给大家解释一下,@Target ,@Retentio这种注解叫元注解。
Target的功能就是表明你这个注解是用在什么地方,它值是一个枚举类型:
<strong> public enum ElementType { /** * Class, interface or enum declaration. 用于描述类、接口(包括注解类型) */ TYPE, /** * Field declaration. 字段声明 */ FIELD, /** * Method declaration. 方法声明 */ METHOD, /** * Parameter declaration. 参数声明 */ PARAMETER, /** * Constructor declaration. 构造器声明 */ CONSTRUCTOR, /** * Local variable declaration. 局部变量 */ LOCAL_VARIABLE, /** * Annotation type declaration. 注释类型声明。 */ ANNOTATION_TYPE, /** * Package declaration. 用于描述包 */ PACKAGE }</strong>
Retention大概意思是注解的生命周期什么时候生效。
<strong>public enum RetentionPolicy { /** * Annotation is only available in the source code. 在源文件中有效(指定注解只保留在源文件当中,<br /> 编译成类文件后就把注解去掉;) */ SOURCE, /** * Annotation is available in the source code and in the class file, but not * at runtime. This is the default policy. 在class文件中有效,不是在运行时有效(指定注解只保留在源文件和编译后的class <br /> 文件中,当jvm加载类时就把注解去掉) */ CLASS, /** * Annotation is available in the source code, the class file and is * available at runtime. 运行时有效 */ RUNTIME }</strong>
二、我们需要自定义一个工具类,这个工具类里面可以获得我们的注解,通过反射来找到我们的View。
<strong>public class InjectUtils { public static void initContext(Object context) { //injectLayout必须在injectView前面,因为必须先找到contentView才能够找到控件 injectLayout(context); //找到contentView injectView(context); //找到控件 } private static void injectView(Object context) { Class<?> clazz = context.getClass(); Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ //获取注解 ViewInject viewInject = field.getAnnotation(ViewInject.class); //如果上面没有标明注解 if (viewInject == null) continue; //获取注解里面的Id int valueId = viewInject.value(); try { //用反射调用findViewById()方法 Method findViewById = context.getClass().getMethod("findViewById",int.class); View view = (View) findViewById.invoke(context,valueId); //反射访问私有成员,必须加上这句 field.setAccessible(true); //然后对这个属性复制 field.set(context,view); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } private static void injectLayout(Object context) { int layoutId = 0; //获得类 Class<?> clazz = context.getClass(); //获得该类声明的注解 ContentView contentView = clazz.getAnnotation(ContentView.class); //如果该类没有声明注解 if (contentView == null) { return; } //获得注解里面设置的Id layoutId = contentView.value(); try { //利用反射调用setContentView()方法 Method setContentView = context.getClass().getMethod("setContentView",int.class); setContentView.invoke(context,layoutId); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }</strong>
具体含义请看注释。
三、定义一个BaseActivity
<strong>public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); InjectUtils.initContext(this); } }</strong>
四、实现我们的activity
<strong>//给该class加上我们自定义的注解,InjectUtils会通过找到 //注解里面的R.layout.activity_main,然后通过反射调用 //setContentView()方法<br /> @ContentView(R.layout.activity_main) public class MainActivity extends BaseActivity { //给该组件加上我们自定义的注解,InjectUtils会通过找到 //注解里面的R.id.xx,然后通过反射调用 //findViewById()方法找到控件 @ViewInject(R.id.text1) private TextView textView1; @ViewInject(R.id.text2) private TextView textView2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); textView1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { textView1.setText("text1"); } }); textView2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { textView2.setText("text2"); } }); } }</strong>
要注意一点:如果某个注解属性使用value作为名称如ContentView中的value,那么赋值的时候可以直接@ContentView(类的ID),但是如果你使用的是其他名称,比如下面这个注解:
<strong>public @interface Person { //name是属性而不是方法,t是它的默认值,在定义的时候可以不用给定默认值 String name() default t; }</strong>
那么必须@Person(name=xx)这样调用