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等网站找到这些强大的工具类的源码以及详细的使用说明,故本文只为我们自己编写工具类做一个抛砖引玉的小例子,仅供参考,欢迎交流~

时间: 2024-08-02 02:49:02

Android自定义工具类获取按钮并绑定事件(利用暴力反射和注解)的相关文章

Android常用工具类

主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.PreferencesUtils.JSONUtils.FileUtils.ResourceUtils.StringUtils.ParcelUtils.RandomUtils.ArrayUtils.ImageUtils.ListUtils.MapUtils.ObjectUtils.SerializeUtils.S

自定义工具类

在编程过程中可能遇到的情况:多次从一个文件(配置文件等)中取数据,但是配置的文件都是以字符串的形式表示的,获取的值也是字符串类型,这就需要自己手动将其转化为需要的类型,如果取得次数少还不是什么大问题,如果读取次数频繁,想想相同的代码会用到多少次,造成代码量大而且重复累赘.在这个时候我们通常需要自己写工具类,将经常用到的相似代码(方法等)封装起来.这里以从properties文件取数据为例.首先从文件中取出的数据可能是一个文件名,可能需要得到一个整数或者浮点数类型值,也可能得到一个日期数据,这就需

Android常用工具类(收藏)

Android常用工具类 主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java. 目前包括(HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.PreferencesUtils.JSONUtils.FileUtils.ResourceUtils.StringUtils.ParcelUtils.RandomUtils.ArrayUtils.ImageUtils.ListUtils.MapUtils.ObjectUtils.S

Android常用工具类 (转)

主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.PreferencesUtils.JSONUtils.FileUtils.ResourceUtils.StringUtils.ParcelUtils.RandomUtils.ArrayUtils.ImageUtils.ListUtils.MapUtils.ObjectUtils.SerializeUtils.S

53. Android常用工具类

主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.PreferencesUtils.JSONUtils.FileUtils.ResourceUtils.StringUtils.ParcelUtils.RandomUtils.ArrayUtils.ImageUtils.ListUtils.MapUtils.ObjectUtils.SerializeUtils.S

Android 系统工具类SystemUtils收集整理(持续更新)

最近做的功能中涉及到了一些关于系统方面的东西,自己摸索以及网上搜集整理出来了一个工具类方便调用 包含的功能有: 获取系统中所有APP应用.获取用户安装的APP应用.根据包名和Activity启动类查询应用信息.跳转到WIFI设置.WIFI网络开关.移动网络开关.GPS开关 当前若关则打开 当前若开则关闭.调节系统音量.设置亮度.获取屏幕的亮度.跳转到系统设置.获取文件夹下所有文件.获取视频的缩略图 .打开视频文件... 工具类会持续更新,与大家共同学习进步. SystemUtils.java p

ThinkPHP3验证码、文件上传、缩略图、分页(自定义工具类、session和cookie)

验证码 TP框架中自带了验证码类 位置:Think/verify.class.php 在LoginController控制器中创建生存验证码的方法 login.html登陆模板中 在LoginController控制器中判断验证码是否正确并且判断登陆是否成功 文件上传 用到的知识点: 1.文件上传的时候,要设置表单的enctype属性 2.$_FILE[名字][]用来接收文件的信息 第二维的字段: name size error type tmp_name 3.move_uploaded_fil

Thinkphp自定义工具类的使用!

在使用Thinkphp做开发的时候,很多时候会用到一些自己写的类,为了方便管理,可以把这些类,单独放到一个文件里. 这就是自定义工具类: 首先在 Application 目录下新建 Components 文件夹,里面防止一些常用的类: 示例:EmailTool.class.php <?php namespace Components; class EmailTool{ public function send(){ echo "邮件发送成功"; } } 如何使用: // 测试方法

Android基础工具类重构系列一Toast

前言: 一直在考虑写一下Android实际项目中的一些总结,翻看CSDN博客,上一篇已经是一年多曾经. 本系列定位Android基础工具类重构.旨在记录实际项目中经经常使用到的一些工具类,比方Toast.Dialog.动画类,ImageLoader类等等.正在梳理,但发现梳理完再写预计黄花菜都凉了.所以改变策略,边写边梳理. 首先要写的就是这个Toast. 一.说明 作为Android系统提供的基类,Toast是最简单的提示消息类.特点悬浮.跨界面(Activity)特定时间内自己主动销毁. 二