欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

简单实现ButterKnife(运行时注解)

程序员文章站 2022-06-16 17:30:19
...

现在ButterKnife其实是基于编译时注解实现的,很大原因是出于对java反射机制效率的诟病,这里我主要是想使用运行时注解来实现ButterKnife的功能。在后面还会有一篇关于ButterKnife(8.7.0)的最新版本编译时注解的实现方式:简单实现ButterKnife(编译时注解)

一、运行时注解

java运行时注解是基于java的反射机制,就是在运行时,动态的获取类的方法、变量等信息以及进行相关操作的一种机制。但是这种机制效率很低,因此大部分人都对此不太乐观,但是就目前Android机器的性能来说,这些都不是太大问题吧。下面就以ButterKnife中的部分功能为例用运行时注解来实现。

二、代码实现

1. 新建注解类

package com.example.davidchen.blogdemo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 绑定view控件注解
 * Created by DavidChen on 2017/7/25.
 */

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

这里也没啥可多说的,Retention指定注解保留到Runtime时,Target指定注解针对变量。注解里接受一个int值,即控件id。

2. 注解处理器

package com.example.davidchen.blogdemo;

import android.app.Activity;
import android.view.View;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 工具类
 * Created by DavidChen on 2017/7/25.
 */

public class ButterKnife {
    public static void bind(final Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            BindView bindView = field.getAnnotation(BindView.class);
            if (bindView != null) {
                int id = bindView.value();
                if (id != -1) {
                    View view = activity.findViewById(id);
                    try {
                        field.set(activity, view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

这里通过反射,先获取到activity的类中所有public变量,然后遍历获取含有BindView注解的变量,之后获取注解值,通过findViewById来获取view并通过field.set来给指定对象activity的改变量赋值。这样就ok了。

3. 测试

package com.example.davidchen.blogdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/**
 * 测试activity
 * Created by DavidChen on 2017/7/25.
 */

public class TestActivity extends AppCompatActivity {

    @BindView(R.id.btn_enter)
    public Button btn_enter;

    @BindView(R.id.tv_result)
    public TextView tv_result;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        ButterKnife.bind(this);
        btn_enter.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tv_result.setText("注入成功");
            }
        });
    }
}

测试结果截图:

简单实现ButterKnife(运行时注解)

4. 添加OnClickListener

同样,view的点击事件我们可以通过注解来实现。
注解类:

package com.example.davidchen.blogdemo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 绑定点击事件
 * Created by DavidChen on 2017/7/25.
 */

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

处理器类:

package com.example.davidchen.blogdemo;

import android.app.Activity;
import android.view.View;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 工具类
 * Created by DavidChen on 2017/7/25.
 */

public class ButterKnife {
    public static void bind(final Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        Field[] fields = clazz.getFields();
        injectView(activity, fields);
        Method[] methods = clazz.getMethods();
        injectOnclick(activity, methods);
    }

    private static void injectOnclick(final Activity activity, Method[] methods) {
        for (final Method method : methods) {
            OnClick onClick = method.getAnnotation(OnClick.class);
            if (onClick != null) {
                int ids[] = onClick.value();
                for (int id : ids) {
                    if (id != -1) {
                        View view = activity.findViewById(id);
                        if (view != null) {
                            view.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    try {
                                        method.invoke(activity, v);
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    } catch (InvocationTargetException e) {
                                        e.printStackTrace();
                                    }
                                }
                            });
                        }
                    }
                }
            }
        }
    }

    private static void injectView(Activity activity, Field[] fields) {
        for (Field field : fields) {
            BindView bindView = field.getAnnotation(BindView.class);
            if (bindView != null) {
                int id = bindView.value();
                if (id != -1) {
                    View view = activity.findViewById(id);
                    try {
                        field.set(activity, view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

测试类:

package com.example.davidchen.blogdemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/**
 * 测试activity
 * Created by DavidChen on 2017/7/25.
 */

public class TestActivity extends AppCompatActivity {

    @BindView(R.id.btn_enter)
    public Button btn_enter;

    @BindView(R.id.tv_result)
    public TextView tv_result;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        ButterKnife.bind(this);
    }

    @OnClick({R.id.btn_enter, R.id.tv_result})
    public void click(View view) {
        switch (view.getId()) {
            case R.id.btn_enter:
                tv_result.setText("注入成功");
                break;
            case R.id.tv_result:
                Toast.makeText(TestActivity.this, "guin", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

结果截图:

简单实现ButterKnife(运行时注解)

三、总结

这里其实没太多的难点,熟悉注解及相关api即可。但是,正如开头所说,毕竟其效率不太高,后面写一篇关于编译时注解实现butterknife。