github项目解析(六)-->自定义实现ButterKnife框架

转载请标明出处:一片枫叶的专栏

目前在  友友用车  项目中使用到了ButterKnife框架,这是一个通过注解的方式简化程序员代码量,自动映射xml布局文件与对象关系的框架。其github上的地址  ButterKnife

这里简单介绍一下他的使用方式;android注解Butterknife的使用及代码分析

(一)使用方式

1)在activity中如何使用

@InjectView(R.id.feedback_content_edit)
    EditText feedContent; // 意见反馈功能

    @InjectView(R.id.feedback_contact_edit)
    EditText feedContact; // 联系方式

    @InjectView(R.id.b3_button)
    Button feedOk; // 提交按钮

    @OnClick(R.id.open_car_door)
    public void openCarDorClick() {
        dosomething;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_feedback);
        ButterKnife.inject(this);

        initView();
    }

是不是很简单?其实这就是butterKnife的基本用法了

  • 通过注解(@InjectView)的方式将xml布局文件中定义的组件与Activity中定义的组件对象对应起来
  • 通过注解(@OnClick)实现对布局文件的点击事件
  • 在onCreate方法中通过静态方法:ButterKnife.inject(this);真正的将xml布局组件与activity中的组件对象映射起来;

2)在Fragment中如何使用

public class SimpleFragment extends Fragment {

    @InjectView(R.id.fragment_text_view)
    TextView mTextView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_simple, container, false);
        ButterKnife.inject(this, view);
        mTextView.setText("TextView in Fragment are found!");
        return view;
    }
}

注意这里需要调用ButterKnife的重载方法:

public static void inject(Object target, View source) {
        inject(target, source, ButterKnife.Finder.VIEW);
    }

3)在Adapter的ViewHolder是如何使用的

static class ViewHolder {
        @InjectView(R.id.person_name)
        TextView name;
        @InjectView(R.id.person_age)
        TextView age;
        @InjectView(R.id.person_location)
        TextView location;
        @InjectView(R.id.person_work)
        TextView work;

        public ViewHolder(View view) {
            ButterKnife.inject(this, view);
        }
    }

可以发现ButterKnife的主要使用作用是简化我们加载xml布局文件的写法,通过注解的方式将内存对象与布局文件绑定,这对于懒程序员真是一个偷懒的利器啊。

那么ButterKnife的实现原理是怎样的呢?我们能否自己实现一个简单的ButterKnife框架呢?带着这两个问题,我们开始今天的自定义ButterKnife之旅。

预备知识点:

  • java注解相关

下面是一段关于java中注解的说明:

注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。

具体关于java注解方面的内容我们可以参考:Java注解Annotation详解

  • java反射相关

下面一段是百度百科中关于java反射的说明:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

具体关于java反射方面的内容我们可以参考:JAVA中的反射机制

在了解了java的注解和反射机制之后我们可以开始我们的自定义实现ButterKnife之旅了。

(二)绑定View组件;

  1. 自定义注解
@Target(ElementType.FIELD) // 标识改注解应用在成员变量上
@Retention(RetentionPolicy.RUNTIME) // 标识生命周期是运行时
public @interface ViewBinder {
    int id() default -1;
}

@interface是用于自定义注解的,它里面定义的方法的声明不能有参数,也不能抛出异常,并且方法的返回值被限制为简单类型、String、Class、emnus、@interface,和这些类型的数组。

关于注解方面的内容,可自行查询学习;

  1. 创建View解析类
/**
 * View解析类,主要用于解析含有注解的View成员变量
 */
public class ViewBinderParser {

    /**
     * 初始化解析
     * @param object
     */
    public static void inject(Object object) {
        // 创建解析对象并开始执行解析方法
        ViewBinderParser parser = new ViewBinderParser();
        try {
            parser.parser(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 开始执行解析方法
     * @param object
     * @throws Exception
     */
    public void parser(final Object object) throws Exception{
        View view = null;
        // 获取目标对象字节码
        final Class<?> clazz = object.getClass();
        // 获取目标对象定义的成员变量
        Field[] fields = clazz.getDeclaredFields();
        // 循环遍历成员变量
        for (Field field : fields) {
            if (field.isAnnotationPresent(ViewBinder.class)) {
                ViewBinder inject = field.getAnnotation(ViewBinder.class);
                int id = inject.id();
                if (id < 0) {
                    throw new Exception("id must not be null!!!");
                }
                if (id > 0) {
                    field.setAccessible(true);
                    if (object instanceof View) {
                        view = ((View) object).findViewById(id);
                    } else if (object instanceof Activity) {
                        view = ((Activity) object).findViewById(id);
                    }
                    field.set(object, view);// 给我们要找的字段设置id
                }
            }
        }
    }
}

这里可以看出,主要是通过创建一个解析对象,然后通过反射方法获取定义的成员变量,判断是否注解属性,如果是的话,则将注解的id值通过findViewById获取并给View对象赋值;

  1. 在activity执行解析方法
/**
 * 测试Activity,主要用于测试通过注册实现View组件的初始化过程
 */
public class MainActivity extends AppCompatActivity {

    @ViewBinder(id = R.id.button1)
    public Button button1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ViewBinderParser.inject(MainActivity.this);

                Log.i("tag", button1.getText() + " ####");
            }
        });

    }

}

可以发现当我们点击Button2的时候我们执行了Log.i方法,并将button1的text打印出来了,正式我们在布局文件中初始化的时候设置的text字符串,从而说明我们通过注解的方式实现了button1组件的初始化工作,初始化过程可能有一些地方有待优化,但这个其实就是butterKnife框架实现组件初始化工作的核心流程。

既然我们已经实现了组件的初始化工作,下面我们将尝试的绑定View组件的事件点击事件。

(三)绑定View的OnClick事件

(一)自定义OnClick注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int id() default -1;
}

和设置View组件的初始化注解类似,这里定义了组件点击事件OnClick的注解。

(二)创建解析类,解析方法

/**
     * 解析成员方法
     * @param clazz
     * @throws Exception
     */
    public void parserMethod(Class<?> clazz, final Object object) throws Exception{
        View view = null;
        // 获取目标对象定义的成员方法
        Method[] methods = clazz.getDeclaredMethods();
        // 循环遍历成员变量
        for (final Method method : methods) {
            if (method.isAnnotationPresent(OnClick.class)) {
                OnClick inject = method.getAnnotation(OnClick.class);
                int id = inject.id();
                if (id < 0) {
                    throw new Exception("id must not be null!!!");
                }
                if (id > 0) {
                    if (object instanceof View) {
                        view = ((View) object).findViewById(id);
                    } else if (object instanceof Activity) {
                        view = ((Activity) object).findViewById(id);
                    }
                    view.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(object, null);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
        }
    }

(三)在activity中测试解析

/**
 * 测试Activity,主要用于测试组件的点击事件
 */
public class MainActivity extends AppCompatActivity {

    @ViewBinder(id = R.id.button1)
    public Button button1;

    /**
     * 执行button1的点击事件
     */
    @OnClick(id = R.id.button1)
    public void button1OnClick() {
        Log.i("tag", "这是一个测试的例子");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewBinderParser.inject(MainActivity.this);
    }

}

可以发现我们在Activity中为button1OnClick方法绑定了组件id,这样就实现了button1组件的事件绑定定义,然后我们在Activity的onCreate方法执行了ViewBinderParser.inject方法,这样就真正实现了对组件的事件绑定,当我们点击button1的时候执行了Log.i方法,并打印:”这是一个测试的例子”,说明我们组件绑定操作执行成功了。O(∩_∩)O哈哈~

总结:

  • 本文主要分析了一下ButterKnife的简单实用与实现原理,并实际实现了一个简单的ButterKnife框架。
  • 已经将自己实现的ButterKnife框架上传至  我的github中  感兴趣的同学可以star和follow。
  • 自己实现简单的ButterKnife框架主要涉及到了java中的注解和反射相关知识。

另外对github项目,开源项目解析感兴趣的同学可以参考我的:

github项目解析(一)–>上传android项目至github

github项目解析(二)–>将Android项目发布至JCenter代码库

github项目解析(三)–>android内存泄露监测之leakcanary

github项目解析(四)–>动态更改TextView的字体大小

github项目解析(五)–>android日志框架

时间: 2024-12-30 00:18:49

github项目解析(六)-->自定义实现ButterKnife框架的相关文章

Github项目解析(十一)--&gt;一个简单,强大的自定义广告活动弹窗

转载请标明出处:一片枫叶的专栏 上一篇文章中讲解了我最近写的一个快速集成二维码扫描库,其核心的实现扫描的功能,是通过调用ZXing库实现的.由于在实现二维码扫描功能的时候发现集成二维码扫描功能并不是特别方便,于是有了将其制作成标准库的想法,这个二维码库能够快速,方便的集成二维码扫描功能,项目地址是在:android-zxingLibrary**,在项目开源后有不少同学提出了许多不错的意见,目前也在不断的迭代中,自己也学到了很多. 本文我们将讲解一个简单,强大的广告活动弹窗控件.不少App在打开的

Github项目解析(九)--&gt;实现Activity跳转动画的五种方式

转载请标明出处:一片枫叶的专栏 上一篇文章中我们讲解了在Activity启动过程中获取组件宽高的五种方式.在Activity的启动过程中如果我们直接在生命周期方法中通过view.getWidth()或者是view.getHeight()方法获取组件的宽度和高度其结果都是0,为什么会出现这个问题呢? 其实看过我以前写过的Activity启动流程  Activity布局加载流程  Activity布局绘制流程  的同学应该对Activity的启动流程和其布局加载绘制流程不陌生,Activity的启动

github项目解析(八)--&gt;Activity启动过程中获取组件宽高的三种方式

转载请标明出处:一片枫叶的专栏 上一个github小项目中我们介绍了防止按钮重复点击的小框架,其实现的核心逻辑是重写OnClickListener的onClick方法,添加防止重复点击的逻辑,即为第二次点击与第一次点击的时间间隔添加阙值,若第二次点击的时间间隔与第一次点击的时间间隔小于阙值,则此次点击无效,再次基础上我们又封装了点击组件验证网络Listener,点击组件验证是否登录Listener等,具体可参考:github项目解析(七)–>防止按钮重复点击 本文中我将介绍一下android中A

github项目解析(七)--&gt;防止按钮重复点击

转载请标明出处:一片枫叶的专栏 本文中我将介绍一下我自己封装的一个小的工具类库:按钮点击事件类库. 作用: 该类库可以防止按钮重复点击,可以判断网络状态,可以判断用户登录状态,以及自定义验证条件等等. 说明: 其实现的核心原理就是通过自定义实现自身的OnClickListener类,并重写其中的onClick方法,在onClick方法中执行相应的判断逻辑之后回调我们自定义的抽象方法. 具体效果如下图所示: 使用方式 屏蔽多次点击事件 /** * 测试快速点击事件 */ fastButton.se

Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架

前言: Annotation注解在Android的开发中的使用越来越普遍,例如EventBus.ButterKnife.Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直到后来android-apt的出现通过注解根据反射机制动态编译生成代码的方式来解决在运行时不再使用发射机制,不过随着android-apt的退出不再维护,我们今天利用Android studio的官方插件annotationProcessor来实现一下自己的ButterKnife UI注解框架. 需

Android开发之手把手教你写ButterKnife框架(二)

欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开发之手把手教你写ButterKnife框架(一)我们讲了ButterKnife是什么.ButterKnife的作用和功能介绍以及ButterKnife的实现原理. 本篇博客主要讲在android studio中如何使用apt. 一.新建个项目, 然后创建一个module名叫processor 新建m

GitHub 里面有大量优秀的第三方框架

写iOS 程序的时候往往需要很多第三方框架的支持,可以大大减少工作量,讲重点放在软件本身的逻辑实现上. GitHub 里面有大量优秀的第三方框架,而且 License 对商业很友好.一下摘录一下几乎每个项目都想集成的几个框架. SDWebImageView 1. Mantle Mantle 让我们能简化 Cocoa 和 Cocoa Touch 应用的 model 层.简单点说,程序中经常要进行网络请求,请求到得一般是 json 字符串,我们一般会建一个 Model 类来存放这些数据.这就要求我们

XML序列化与反序列化+自定义XML注解框架XmlUtils

背景 前面一篇总结了Serializable的序列化与反序列化,现在接着总结XML.主要内容:XML基本的序列化与反序列化方法.一些注意事项.以及自定义了一个XML注解框架(简洁代码,解放双手). XML的序列化与反序列化 先与Serializable进行简单的对比: Serializable存储的文件,打开后无法正常查看,安全性高.xml文件可通过文本编辑器查看与编辑,可读性高(浏览器会格式化xml文件,更方便查看),安全性低: Serializable文件通过了签名,只能在自己的程序中反序列

QT开发(三十六)——Model/View框架

QT开发(三十六)--Model/View框架 一.Model/View框架简介 1.Model/View框架核心思想 Model/View框架的核心思想是模型(数据)与视图(显示)相分离,模型对外提供标准接口存取数据,不关心数据如何显示,视图自定义数据的显示方式,不关心数据如何组织存储. Model/View框架中数据与显示的分离,可以允许使用不同界面显示同一数据,也能够在不改变数据的情况下添加新的显示界面.为了处理用户输入,引入了委托(delegate).引入委托的好处是可以自定义数据项的渲