您的位置:首页 > 移动开发 > Android开发

Android 中注解view (仿butterknife)

2016-06-09 12:40 441 查看

Android中使用注解来给view绑定事件

自定义注解实现View注入,就不需要再写

Button button = (Button) findViewById(R.id.test_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

}
});


元注解

@Target

@Target表示Annotation可用在什么地方。其中的ElementType取值如下:

TYPE:类,接口或者是enum声明
FIELD:域声明(包括enum实例)
METHOD:方法声明
PARAMETER:参数声明
CONSTRUCTOR:构造器声明
LOCAL_VARIABLE:局部变量声明
ANNOTATION_TYPE:注解类型声明
PACKAGE:包声明


@Retention

@Retention表示在什么级别保存该注解信息。其中RetentionPolicy取值如下:

SOURCE:只在源码中保留,该注解将会被编译器丢掉
CLASS:注解在class文件中可用,但是会被VM丢弃
RUNTIME:VM会在运行时保留注解,这时可以通过反射读取注解信息。


@Documented

@Documented表示在Javadocs中包含这个注解。

@Inherited

@Inherited表示允许子类继承父类中的注解。

自定义注解

ContentView,注解Activity中的layout

package com.ljd.msh.inject;

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

/**
* @author sv-004
*/

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


Inject: 注解实现Activity中View组件的注入.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author sv-004
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
int value();
}


OnClick:注解实现View的事件注入.

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

/**
* @author sv-004
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
int[] value();
}


注解处理器

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

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

/**
* @author sv-004
*/
public class MSHInject {

private static Class<?> clazz;

public static void inject(Activity activity){

//获取activity的Class类
clazz = activity.getClass();
injectContent(activity);
injectView(activity);
injectEvent(activity);
}

public static void unInject(){
clazz = null;
}

/**
* 对ContentView注解惊醒解析
* @param activity
*/
private static void injectContent(Activity activity){

//取的Activity中的ContentView注解
ContentView contentView = clazz.getAnnotation(ContentView.class);
if (contentView != null){

//取出ContentView注解中的值
int id = contentView.value();
try {

//获取Activity中setContentView方法,执行setContentView方法为Activity设置ContentView
//在这一步中我们也可以直接使用 activity.setContentView(id) 来设置ContentView
clazz.getMethod("setContentView",Integer.TYPE).invoke(activity,id);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

/**
* 对InjectView注解进行解析
* @param activity
*/
private static void injectView(Activity activity){

Field[] fields = clazz.getDeclaredFields();
for (Field field : fields){
Inject inject = field.getAnnotation(Inject.class);
if (inject != null){
int id = inject.value();
try {
//这一步中同样也能够使用 Object view = activity.findViewById(id) 来获取View
Object view = clazz.getMethod("findViewById",Integer.TYPE).invoke(activity,id);
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

}
}

/**
* 对OnClick注解进行解析
* @param activity
*/
private static void injectEvent(Activity activity){

Method[] methods = clazz.getMethods();

for (Method method : methods) {
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null){
int[] ids = onClick.value();
MyInvocationHandler handler = new MyInvocationHandler(activity,method);

//通过Java中的动态代理来执行View.OnClickListener
Object listenerProxy = Proxy.newProxyInstance(
View.OnClickListener.class.getClassLoader(),
new Class<?>[] { View.OnClickListener.class }, handler);
for (int id : ids) {

try {
Object view = clazz.getMethod("findViewById",Integer.TYPE).invoke(activity,id);
Method listenerMethod = view.getClass()
.getMethod("setOnClickListener", View.OnClickListener.class);
listenerMethod.invoke(view, listenerProxy);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

}
}
}
}

static class MyInvocationHandler implements InvocationHandler {

private Object target = null;
private Method method = null;

public MyInvocationHandler(Object target,Method method) {
super();
this.target = target;
this.method = method;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

return this.method.invoke(target,args);
}
}
}


MainActivity

package com.ljd.msh;
/**
* @author sv-004
* */
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.ljd.annotation.R;
import com.ljd.msh.inject.ContentView;
import com.ljd.msh.inject.Inject;
import com.ljd.msh.inject.OnClick;
import com.ljd.msh.inject.MSHInject;

@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

@Inject(R.id.test_text)
TextView textView;

@Inject(R.id.test_btn)
Button button;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

MSHInject.inject(this);
textView.setText("hello word");
button.setText("test");
}

@OnClick({R.id.test_btn,R.id.test_text})
public void onClick(View view) {
switch (view.getId()){
case R.id.test_btn:
Toast.makeText(this,"test onClick",Toast.LENGTH_SHORT).show();
break;
case R.id.test_text:
Toast.makeText(this,"hello word",Toast.LENGTH_SHORT).show();
break;

}

}

@Override
protected void onDestroy() {
super.onDestroy();
MSHInject.unInject();
}
}


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/activity_vertical_margin"
tools:context="com.ljd.msh.MainActivity">

<Button
android:id="@+id/test_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/test_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</LinearLayout>


参考资料

Java注解Annotation基础

InvocationHandler中invoke()方法的调用问题

butterknife
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: