Android用注解与反射实现Butterknife功能

目录
  • 自定义注解
  • 使用自定义注解
  • 通过反射机制获取注解参数
    • 1. 布局文件获取
    • 2. 控件获取实现
    • 3. 控件点击响应

自定义注解

1) 先定义布局文件注入

//注解的作用域在类上
@Target(ElementType.TYPE)
//让保持性策略为运行时态,将注解编码到class文件中,让虚拟机读取
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
    int value();//使用时直接@ContentView(R.layout.xxx)指定的R.layout.xxx就是布局文件,会自动注入布局文件
}

2) 布局中控件注入文件

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
}

3) 控件的点击响应注入文件

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface OnClick {
    int[] value();
}

使用自定义注解

@ContentView(R.layout.activity_test)
public class TestActivity extends AppCompatActivity implements View.OnClickListener{
    @ViewInject(R.id.edit_text)
    EditText mEditText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        injectContentView(this);
        injectView(this);
        injectEvent(this);
    }
    @OnClick(R.id.button)
    @Override
    public void onClick(View v) {
        Toast.makeText(TestActivity.this,"点击成功"+mEditText.getText().toString(),Toast.LENGTH_SHORT).show();
    }
}

通过反射机制获取注解参数

Method相关方法的介绍:

method.invoke(Object obj,Object args[])的作用就是调用method类代表的方法,其中obj是对象名,args是传入method方法的参数

  • getMethods(): 获得类的public类型的方法
  • getMethod(String name, Class[] params): 获得类的特定方法,name参数指定方法的名字,params参数指定方法的参数类型
  • getDeclaredMethods(): 获取类中所有的方法(public、protected、default、private)
  • getDeclaredMethod(String name, Class[] params): 获得类的特定方法,name参数指定方法的名字,params参数指定方法的参数类型

1. 布局文件获取

    public static void injectContentView(Activity activity){
        //获取activity的类实例
        Class<? extends Activity> clazz = activity.getClass();
        //获取到activity的ContentView注解
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if(contentView!=null){
            //如果activity上面存在这个注解的话,就取出这个注解对应的value值,就是前面设置的布局
            int layoutId=contentView.value();
            try {
                //利用反射调用setContentView方法,完成注入
                Method setViewMethod = clazz.getMethod("setContentView", int.class);
                setViewMethod.invoke(activity,layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

2. 控件获取实现

    private static void injectView(Activity activity){
        //获取activity的类实例
        Class<? extends Activity> clazz = activity.getClass();
        //获取activity的所有成员变量
        Field[] fields = clazz.getDeclaredFields();
        //遍历成员变量,获取成员变量上的ViewInject注解
        for(Field field:fields){
            //获取字段上面的注解对象,同有则继续下一个字段
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if(viewInject!=null){
                //获取ViewInject注解的View的id
                int viewId = viewInject.value();
                //获取控件
                View view = activity.findViewById(viewId);
                try {
                    //设置field为可访问,就算私有的也能访问到,能够提高效率
                    field.setAccessible(true);
                    //将该控件设置给field对象
                    field.set(activity,view);//将activity对象的view控件设置给属性对应上面的例子是mEditText=findViewById(viewId)
                }catch (IllegalAccessException e){
                    e.printStackTrace();
                }
            }
        }
    }

3. 控件点击响应

    private static void injectEvent(final Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        //获取所有方法(私有方法也可以获取到)
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            //获取方法上面的OnClick注解
            OnClick click = method.getAnnotation(OnClick.class);
            //有则继续下面代码
            if (click != null) {
                //获取注解中的数据,因可以给多个button绑定点击事件,因此定义的注解类时使用的是int[]数据类型
                int[] viewId = click.value();
                //在需要发射时将method的setAccessible设置为true,可以提高反射速度,原因是设置为true后则跳过了访问检查,即使private修饰的也可以访问。
                method.setAccessible(true);
                //设置一个代理对象,当调用setOnClickListener时,把代理对象传进去,当点击发生时,就会invoke方法,可以调用带有onClick注解的method方法
               //此时的listener就是actvivty实现View.OnClickListener接口,并实现其接口中onClick方法的对象,其实也就是返回的invoke对象。
                Object listener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(),
                        new Class[]{View.OnClickListener.class}, new InvocationHandler() {
                    //这个invoke,其实就是调用的意思,第一个参数就是被代理的对象,第二个参数就是调用的方法,第三个参数是被调用方法的参数数组。
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                Log.d(TAG, "method"+method);
                                Log.d(TAG, "args "+args);
                                //执行activity对象,参数args的方法,activity对象就是当前的Activity,并且已经实现View.OnClickListener
                                //也就是说如果点击了控件将会执行实现了View.OnClickListener的Activity的onClick方法
                                Object invoke = method.invoke(activity, args);
                                return invoke;
                            }
                        });
                 //listener是一个OnClickListener类的对象,因为method传的是activity,所以是当前类的OnClickListener对象
                //method是onClick方法
                try {
                    for (int id : viewId) {
                        //获取相应的控件
                        View v = activity.findViewById(id);
                        //"setOnClickListener"是方法名,View.OnClickListener.class方法参数的类型
                        Method setClickListener = v.getClass().getMethod("setOnClickListener", View.OnClickListener.class);
                        Log.d(TAG, "injectEvent: "+listener+"v.getClass()"+v);
                        //View类中的setOnClickListener方法需要一个OnClickListener类型的参数也就是我们的代理对象
                        //我们需要将对应的控件进行设置
                        //这时当我们点击v(也就是我们找到的控件)时就会调用代理对象的onClick方法。而代理对象的onClick方法就是Activity中实现接口的onClick方法。相当于执行方法setOnClickListener(listener)。而这个listener就是我们上面代理(当前activity实现OnClicker接口的对象)对象。
                        setClickListener.invoke(v, listener);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

上面的实现都是在运行时通过反射完成注入,但它会对性能有一定损耗,而Butterknife用的是APT(Annotation Processing Tool)编译时解析技术。它在Java代码编译成Java字节码时就已经处理了@Bind, @OnClick这些注解。它的原理是读入Java源代码,解析注解,生成新的Java代码,新生成的Java代码最后被编译成Java字节码。

到此这篇关于Android用注解与反射实现Butterknife功能的文章就介绍到这了,更多相关Android Butterknife内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Android Studio安装ButterKnife插件(手动安装)

    写完布局后 我们一般需要 findViewById找到这个控件,但是现在有一个很好用的插件ButterKnife 可以一键转化布局文件中的所有有id属性的控件到activitry中:现在我们介绍下此插件使用: 通过AndroidStudio 下载安装 1.打开Android Studio中的设置界面,并且点击左边的插件Plugins,在搜索框中输入 Android ButterKnife Zelezny 如图: 点击 Search in repositories 如图: 点击 install就自

  • Android中butterknife的使用与自动化查找组件插件详解

    前言 Android开发中经常使用findViewById来获取控件然后进行一些列操作,当控件太多的时候代码就非常臃肿,今天就来学习一个新的开源库ButterKnife,真的可以帮助我们高效,快捷的开发,让我们的代码更加简洁. 首先我们来把ButterKnife集成在我们的项目中:ButterKnife的GitHub官方地址:github.com/JakeWharton- 一.集成分为了两部分: 1.仅仅在App主工程使用: 在App的 build.gradle 中添加如下代码: android

  • Android注解使用之ButterKnife 8.0详解

    前言: App项目开发大部分时候还是以UI页面为主,这时我们需要调用大量的findViewById以及setOnClickListener等代码,控件的少的时候我们还能接受,控件多起来有时候就会有一种想砸键盘的冲动.所以这个时候我们想着可以借助注解的方式让我们从这种繁重的工作中脱离出来,也让代码变得更加简洁,便于维护,今天主要学习一下只专注View.Resource.Action注解框架ButterKnife. ButterKnife介绍 ButterKnife是一个专注于Android系统的V

  • Android Studio使用ButterKnife和Zelezny的方法

    前言 ButterKnife是一个专注于Android的View注入框架,可以减少大量的findViewById以及setOnClickListener代码,可视化一键生成. 有着以下的优点: 1.强大的View绑定和Click事件处理功能,简化代码,提升开发效率 2.方便的处理Adapter里的ViewHolder绑定问题 3.运行时不会影响APP效率,使用配置方便 4.代码清晰,可读性强 Android Studio配置ButterKnife 第一步 第二步 配置project的build.

  • Android注解ButterKnife的基本使用

    ButterKnife的最新版本是8.4.0. 首先,需要导入ButterKnife的jar包. 在AndroidStudio中,File->Project Structure->Dependencies->Library dependency 搜索butterknife即可,第一个就是. 另外一种就是直接在build:grade(app)dependencies里添加: compile 'com.jakewharton:butterknife:8.4.0' annotationProc

  • Android Kotlin环境使用ButterKnife的方法

    Butter Knife 黄油刀大家应该都挺熟悉的,有这个之后,就不用写一堆的findViewById,体力活,最近试着玩玩Kotlin语言,也就尝试在Kotlin语言环境下使用ButterKnife,有一点小问题,解决并分享一下. 先看看java环境的用法 1.安装插件,然后重启Android studio. 安装插件.jpg 2.使用,点击一下在setContentView(R.layout.activity_main);然后快捷键Alt+insert. Alt+insert.jpg 3.使

  • Android Studio中ButterKnife插件的安装与使用详解

    1>Android Studio 安装ButterKnife插件 同安装其他插件类似,如下: 1.1>打开Plugins界面 按照上图中1,2,3指示操作(注意:这里我的Android Studio中已经安装了该插件,所以显示的内容不太一样).然后重启Android Studio. 2>在项目上使用该开源项目(以Android Studio 为例) 2.1>在bulid.gradle中添加依赖 重新编译一下该项目,通过后继续操作. 2.2>在代码中就可以使用注解的方式了 2.

  • Android用注解与反射实现Butterknife功能

    目录 自定义注解 使用自定义注解 通过反射机制获取注解参数 1. 布局文件获取 2. 控件获取实现 3. 控件点击响应 自定义注解 1) 先定义布局文件注入 //注解的作用域在类上 @Target(ElementType.TYPE) //让保持性策略为运行时态,将注解编码到class文件中,让虚拟机读取 @Retention(RetentionPolicy.RUNTIME) public @interface ContentView { int value();//使用时直接@ContentVi

  • SpringBoot利用切面注解及反射实现事件监听功能

    目录 前言 效果图 监听原理 核心源码 源码地址 前言 当某个事件需要被监听的时候,我们需要去做其他的事前,最简单的方式就是将自己的业务 方法追加到该事件之后. 但是当有N多个这样的需求的时候我们都这样一个个去添加修改事件的源码吗? 这篇文章将告诉你如何用一个注解,就可以将你的业务代码通过切面的方式添加到事件的前后,而不需要修改事件的代码 效果图 如下图所示,add方法内并没有调用其他的方法,但是其他方法仍然被执行了. 只要给监听方法加@AddEventListener()注解就可以让它在事件前

  • Android AOP 注解详解及简单使用实例(三)

    Android  注解 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) 一.简介 在Android 里面 注解主要用来干这么几件事: 和编译器一起给你一些提示警告信息. 配合一些ide 可以更加方便快捷 安全有效的编写Java代码.谷歌出的support-annotations这个库 就是主要干这个的. 和反射一起 提供一些类似于spring 可配置的功能,方便简洁. 二

  • Android AOP注解Annotation详解(一)

    Android 注解Annotation 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) Android AOP 等在Android上应用越来越广泛,例如框架ButterKnife,Dagger2,EventBus3等等,这里我自己总结了一个学习路程. - Java的注解Annotation - 注解处理解析器APT(Annotation Processing Tool)

  • Android sdutio配置Zxing进行扫码功能的实现方法

    github开源项目(Zxing)demo 最快的调用Zxing方法 1.关联第三方库 2.调用基础的扫码 3.获取返回值 具体代码如下: //1.默认选项启动意图 new IntentIntegrator(MainActivity.this).initiateScan(); // `this` is the current Activity //2.获取得到的结果: @Override protected void onActivityResult(int requestCode, int r

  • Android编程实现通过反射获取资源Id的方法

    本文实例讲述了Android编程实现通过反射获取资源Id的方法.分享给大家供大家参考,具体如下: 在将自己写的工具打成.jar包的时候,有时候会需要引用到res中的资源,这时候不能将资源一起打包,只能通过反射机制动态的获取资源. 特别用在自己定义一个工具将其打成.jar包时,特别注意资源的获取 1.封装成一个工具类 package com.cp.utils; import android.content.Context; public class CPResourceUtil { public

  • Android中 TeaScreenPopupWindow多类型筛选弹框功能的实例代码

    Github地址 YangsBryant/TeaScreenPopupWindow (Github排版比较好,建议进入这里查看详情,如果觉得好,点个star吧!) 引入module allprojects { repositories { google() jcenter() maven { url 'https://www.jitpack.io' } } } implementation 'com.github.YangsBryant:TeaScreenPopupWindow:1.0.2' 主

  • Android实现多级列表中的新建功能

    本文实例为大家分享了Android实现多级列表中的新建功能,供大家参考,具体内容如下 多级列表的页面实现比较简单,所以把新建的功能拿出来了. 窗口代码 /** * 新建一个第一级列表的条目 * 1.选择图片和附件都用Intent.ACTION_GET_CONTENT实现 * 2.打开文件用Intent.ACTION_VIEW实现 * 3.回传的URI需要转化成真实路径 * 4.提交数据之后需要刷新列表 */ public class SectionNewActivity extends AppC

  • android实现微信朋友圈发布动态功能

    本文实例为大家分享了android仿微信朋友圈发布动态功能的具体代码,供大家参考,具体内容如下 效果图: 本文概述 用到的开源库:仿照微信的图片选择器,Luban压缩图片,glide 上面红圈部分,当用户选中图片时,当=9张时,那个加号图片就会消失! 本文主要仿照微信的发布动态功能,主要时针对微信发布动态时,选中图片后的变化状态! 整体布局 这可以看到,控制图片状态的地方就是一个recyclerview 思路 利用recyclerview的多行视图来实现! 微信上的效果是,先选中图片后才会进入这

  • Android使用注解进行代码检查的实现方法

    Android Studio 内置了代码检查工具 Lint,可在菜单栏选择 Analyze > Inspect Code 执行相应的代码检查,代码检查能够根据推断一些不合法的潜在问题,有助于在开发阶段发现开发者因为主管原因导致的一下代码问题,Android 官方提供了注解库 support-annotations 来帮助开发者及早发现问题,下面是常用的一些注解,主要内容如下: Nullness注解 资源注解 线程注解 值约束注解 权限注解 返回值注解 CallSuper注解 Typedef注解

随机推荐