Android自定义工具类获取按钮并绑定事件(利用暴力反射和注解)
Android中为按钮绑定事件的有几种常见方式,你可以在布局文件中为按钮设置id,然后在MainActivity中通过findViewById方法获取按钮对象实例,再通过setOnClickListener为按钮绑定事件,如下所示:
//1.获取控件 btn = (Button)findViewById(R.id.button1); //2.绑定事件 btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { callPhone(); } });
你也可以在布局文件中直接为按钮设置onClick属性,然后在MainActivity中实现方法(实际应用中相较应用较少,本文主要以前一种方式为契机,故不再赘述基本语法格式)。
当一个应用中有很多按钮都需要绑定事件,甚至很多按钮需要绑定的事件具有一定的通用性的时候,我们可以参考一些流行的工具类,利用Java中的反射原理和注解,来写一个简单的工具类,帮助我们完成上述工作,让代码看起来更简洁,提高工作效率。
首先还是先简单介绍一下本文将用到的暴力反射和注解方面的一些知识吧。
反射机制,就是Java中任意一个类,可以通过获取其字节码的方式,将其属性、构造方法、方法和注解等等映射成Method、Constructor、Field、Annotation类,然后对其进行操作。所谓暴力反射是针对类中一些声明为private的成员,通过obj.setAccessible(true);的方式来强行使用私有成员。
Java中的注解Annotation,平时我们常见的一些jdk提供的注解相信大家一定不会陌生,比如@Override(提示重写父类方法,如果不能构成重写的话会报错);@Deprecated(抑制过时警告);@SuppressWarnings(抑制警告),简而言之,注解就是给JVM看的注释。本文将会用到自定义注解,大概写个小例子说明一下自定义注解的用法:
package com.yuki.vu; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyInject { int value(); }
@interface就是注解类的标志性声明了(注意,类成员只能是以下几种:基本数据类型、Class类型、枚举类型
元注解: |
@Retention:指定的注解作用范围 |
值: |
RetentionPolicy.SOURCE java源码范围可见 |
RetentionPolicy.CLASS .class字节码可见 |
RetentionPolicy.RUNTIME 运行的时候都可见 |
@Target:代表定义的注解修饰范围(属性,方法,类) |
ElementType.TYPE:注解修饰类 |
ElementType.METHOD:注解修饰方法 |
ElementType.FILED:注解修饰属性 |
以上只是对反射和注解的简单介绍,详细使用还请另寻资料。接下来开始编写我们的小工具,代码如下:
MyViewUtils.java文件
package com.yuki.vu; import java.lang.reflect.Field; import java.lang.reflect.Method; import android.app.Activity; import android.view.View; import android.view.View.OnClickListener; public class MyViewUtils { public static void inject(final Activity activity){ //反射属性 Field[] declaredFields = activity.getClass().getDeclaredFields(); for (int i = 0; i < declaredFields.length; i++) { Field field = declaredFields[i]; field.setAccessible(true); MyInject annotation = field.getAnnotation(MyInject.class); if (annotation!=null) { int id = annotation.value(); View view = activity.findViewById(id); try { field.set(activity, view); } catch (Exception e) { e.printStackTrace(); } } } //反射方法 Method[] declaredMethods = activity.getClass().getDeclaredMethods(); for (int i = 0; i < declaredMethods.length; i++) { final Method method = declaredMethods[i]; method.setAccessible(true); MyClick annotation = method.getAnnotation(MyClick.class); if (annotation!=null) { int[] value = annotation.value(); for (int j : value) { int id = j; final View btn = activity.findViewById(id); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { method.invoke(activity, btn); } catch (Exception e) { e.printStackTrace(); } } }); } } } } }
MyInject.java文件:
package com.yuki.vu; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyInject { int value(); }
MyClick.java文件:
package com.yuki.vu; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyClick { int[] value(); }
在掌握了反射和注解用法的基础上,看懂以上代码应该不难(代码有哪里不清楚的地方可以评论留言哈^.^),你可以将这三个文件的源文件直接导入工程,也可以把它们打包成一个jar文件,日后导入libs中使用,具体使用如下:
package com.yuki.vu; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { @MyInject(R.id.tv) private TextView tv; @MyInject(R.id.et) private EditText et; @MyInject(R.id.btn1) private Button btn1; @MyInject(R.id.btn2) private Button btn2; @MyInject(R.id.btn3) private Button btn3; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyViewUtils.inject(this); Log.d("tag", ""+btn1.getText()); } @MyClick({R.id.btn1, R.id.btn2, R.id.btn3}) public void submit(View view){ Toast.makeText(this, ((Button)view).getText(), Toast.LENGTH_SHORT).show(); } }
以上就是MainActivity的代码了。可以看到,在使用工具类的情况下,首先通过@MyInject注解获取每个带有id的按钮控件,再通过MyClick的方法为每个按钮绑定事件,最后在onCreate方法中通过一句MyViewUtils.inject(this)来完成按钮与事件的绑定,至此大功告成~撒花
其实以上内容也是从著名的工具类xUtils中参考而来,你可以在github等网站找到这些强大的工具类的源码以及详细的使用说明,故本文只为我们自己编写工具类做一个抛砖引玉的小例子,仅供参考,欢迎交流~