Android 之实现运行时注解控件

一、首先的说一下注解的分类,

      1、运行时注解,代码简单,复杂性低,但是效率稍微低一点

      2、编译时注解、代码多,结构复杂,但是效率高

这里介绍的是运行时注解。

二、还是先说一下思路

    首先创建对应的注解类,并且通过反射 findViewById 方法去实现控件的注解

    方法的注解使用了动态代理模式,去减少代码的量,然后通过反射去调用对应的方法。

    反正特别重要的就是反射反射反射

三、实现(这里主要是添加布局、获取控件、设置点击事件)

    1、添加布局注解

    正常情况下是要调用 setContentView的方法设置Activity的布局文件,如果使用注解的话也是一句话偷笑偷笑

    创建注解文件,设置注解策略为运行时,作用地方为type类型

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

得到当前的类,并且得到注解中的id,然后通过反射 setContentView实现调用

  Class<?> aClass = mContext.getClass();
        //获取到注解类
        ContentView contentView = aClass.getAnnotation(ContentView.class);
        if (contentView != null) {
            //获取到注解的id
            int layoutID = contentView.value();
            try {
                // 通过反射执行注解方法
                Method method = aClass.getMethod("setContentView", int.class);
                method.invoke(mContext, layoutID);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }

2、控件注解

还是先创建注解类

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

首先通过传入的Context得到Class对象

Class<?> aClass = mContext.getClass();

通过DeclaredFields方法当前对象得到所有的成员变量

Field[] declaredFields = aClass.getDeclaredFields();

在遍历获取这个数组,通过每一个成员变量的

getAnnotation方法得到注解,如果注解不为空,则说明有id,是需要的。

然后通过getMethod方法反射findViewById得到该方法,然后反射执行,得到View对象,

然后在把当前值给反射对象

 Class<?> aClass = mContext.getClass();
        //获取到类中所有的成员变量
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //得到成员变量的注解
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            //如果成员变量不为空,说明有注解,则获取id
            if (viewInject != null) {
                //获取到控件的id
                int valueID = viewInject.value();
                try {
                    //通过反射调用findViewById 方法
                    Method method = aClass.getMethod("findViewById", int.class);
                    View view = (View) method.invoke(mContext, valueID);
                    field.setAccessible(true);
                    field.set(mContext, view);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

3、事件注解

实现思路 获取当前类中所有的方法,然后去遍历,然后得到方法的注解,然后判断是不是存在 EventBase,如果有的话就通过反射findViewById方法得到当前控件,再得到当前对象的事件,通过动态代理去实现调用

首先的拿到事件的名字、类型、和方法名,所以创建了辅助注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventBase {
    //对点击事件进行扩展

    //设置监听的方法
    String listenerSetter();

    //事件类型
    Class<?> listenerType();

    //事件被触发后,执行回调的方法名称
    String callBackMethod();
}

然后在创建点击事件的注解(这里以单击为例,别的点击方法我也会把代码展现的)

单击事件

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callBackMethod = "onClick")
public @interface OnClick {
    //那些控件的id,进行点击事件设置
    int[] value();
}

实现事件注解,先获取当前类的所有方法且遍历,

然后获取当前方法的注解

Annotation[] annotations = method.getAnnotations();

在判断当前注解是否有我们设置的三要素,如果没有直接进入下一循环

有的话直接得到三要素

                 String listenerSeter = eventBase.listenerSetter();
                //得到 listenerType--》 View.OnClickListener.class,
                Class<?> listenerType = eventBase.listenerType();
                //callMethod--->onClick
                String callBackMethod = eventBase.callBackMethod();

通过反射得到当前的方法,然后得到他们的控件数组,再去遍历,然后通过反射findViewById得到控件对象

如果对象不为空通过getMethod得到Method对象,然后通过动态代理得到点击事件的对象,然后调用。

        Class<?> clazz = mContext.getClass();
        //获取Activity中所有的方法
        Method[] methods = clazz.getDeclaredMethods();
        //遍历所有方法
        for (Method method : methods) {
        //            获取方法上的所有注解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                //获取注解 anntionType   OnClick  OnLongClck
                Class<?> annotationType = annotation.annotationType();
                //获取注解的注解   onClick 注解上面的EventBase
                EventBase eventBase = annotationType.getAnnotation(EventBase.class);

                if (eventBase == null) {
                    continue;
                }
                 /*
                开始获取事件三要素  通过反射注入进去
                1 listenerSetter  返回     setOnClickListener字符串
                 */
                String listenerSeter = eventBase.listenerSetter();
                //得到 listenerType--》 View.OnClickListener.class,
                Class<?> listenerType = eventBase.listenerType();
                //callMethod--->onClick
                String callBackMethod = eventBase.callBackMethod();
                //方法名 与方法Method的对应关系
                Map<String, Method> methodMap = new HashMap<>();

                methodMap.put(callBackMethod, method);

                try {
                    Method valueMethod = annotationType.getDeclaredMethod("value");
                    int[] viewIDS = (int[]) valueMethod.invoke(annotation);

                    for (int viewID : viewIDS) {
                        Method findViewById = clazz.getMethod("findViewById", int.class);
                        View view = (View) findViewById.invoke(mContext, viewID);
                        if (view == null) {
                            continue;
                        }
                         /*
                        listenerSetter  setOnClickLitener
                        listenerType   View.OnClickListener.class
                         */
                        Method setOnClickListener = view.getClass().getMethod(listenerSeter, listenerType);
                        //使用动态代理实现listenerType接口,类似代码设置控件点击事件
                        ListenerInvocationHandler handler = new ListenerInvocationHandler(mContext, methodMap);
                        Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);
                        setOnClickListener.invoke(view, proxy);
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }

        }

listview的item点击事件

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter ="setOnItemClickListener"
        ,listenerType = AdapterView.OnItemClickListener.class,callBackMethod = "onItemClick")
public @interface OnItemClick {
    int[] value();
}

长按事件

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnLongClickListener",
        listenerType = View.OnLongClickListener.class,callBackMethod = "onLongClick")

public @interface OnLongClick {
    int[] value() default -1;
}

动态代理方法

public class ListenerInvocationHandler implements InvocationHandler{
    //activity   真实对象
    private Context context;
    private Map<String,Method> methodMap;

    public ListenerInvocationHandler(Context context, Map<String, Method> methodMap) {
        this.context = context;
        this.methodMap = methodMap;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name=method.getName();
        //决定是否需要进行代理
        Method metf=methodMap.get(name);

        if(metf!=null)
        {
            return  metf.invoke(context,args);
        }else
        {
            return method.invoke(proxy,args);
        }
    }
}

调用注解实例

@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
    @ViewInject(R.id.test)
    Button buttonTest;

    @ViewInject(R.id.listView)
    ListView listView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.inject(this);
        listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
                new String[]{"aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"}));
    }

    @OnItemClick(R.id.listView)
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
        Toast.makeText(MainActivity.this, "aaaaaaaaaaa" + position, Toast.LENGTH_SHORT).show();
    }

    @OnClick(R.id.test)
    public void testClick(View view) {
        Toast.makeText(MainActivity.this, "aaaaaaaaaaa", Toast.LENGTH_SHORT).show();
    }

    @OnLongClick({R.id.test2, R.id.test})
    public boolean testLongClick(View view) {
        Toast.makeText(MainActivity.this, "bbbbbbbbbbbbbbbbbbbbbbbbbb", Toast.LENGTH_SHORT).show();


        return true;
    }
}

我会在我的资源里保存源码



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值