简介
Inversion of Control,英文缩写为IOC,字面翻译:控制反转。什么意思呢?就是一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量,那么你就new 出来用呗!IOC的原则是:NO,我们不要new,这样耦合度太高,你配置个xml文件,里面标明哪个类,里面用了哪些成员变量,等待加载这个类的时候,我帮你注入(new)进去。具体可以参考https://www.jianshu.com/p/3968ffabdf9d
我们这里要简单实现一个BufferKnife
- 实现setContentView功能
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.init(this);
}
}
ContentView代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
int value();
}
InjectUtils中的initLayout方法
/**
* 设置布局文件
*
* @param context
*/
private static void initLayout(Object context) {
int layoutId = 0;
Class<?> aClass = context.getClass();
ContentView contentView = aClass.getAnnotation(ContentView.class);
if (null != contentView) {
layoutId = contentView.value();
try {
Method method = context.getClass().getMethod("setContentView", int.class);
method.invoke(context, layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
这样就实现了一个布局
- 实现findViewById布局文件
@ViewInject(R.id.text)
TextView textView;
ViewInject 文件
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
int value();
}
InjectUtils中的initView方法
/**
* 控件
*
* @param context
*/
private static void initView(Object context) {
Class<?> aClass = context.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
ViewInject annotation = declaredField.getAnnotation(ViewInject.class);
if (null != annotation) {
int value = annotation.value();
try {
Method method = aClass.getMethod("findViewById", int.class);
View view = (View) method.invoke(context, value);
declaredField.setAccessible(true);
declaredField.set(context, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
这样就实现了TextView textView = findViewById(R.id.text)功能
- 给textview设置点击事件
我们要实现这个方法
// textView.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// Toast.makeText(MainActivity.this, "被点击", Toast.LENGTH_SHORT).show();
// }
// });
mainActivity中的方法
@OnClick({R.id.text})
public void Myclick(View view) {
Toast.makeText(MainActivity.this, "被点击", Toast.LENGTH_SHORT).show();
}
OnClick 注解实现
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,callbackMethod = "onClick")
public @interface OnClick {
int[] value() default -1;
}
EventBase 注解实现,这里是给注解添加注解,用来区别点击事件
@Retention(RetentionPolicy.RUNTIME)
//该注解在另外一个注解上使用
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventBase {
// setOnClickListener 订阅
String listenerSetter();
// 事件以及他的类型
/**
* 事件监听的类型
* @return
*/
Class<?> listenerType();
/**
* 事件处理
* @return
*/
String callbackMethod();
}
inittClick实现方式
/**
* 点击事件
*
* @param context
*/
private static void inittClick(Object context) {
Class<?> aClass = context.getClass();
//得到所有的方法
Method[] methods = aClass.getMethods();
//Myclick 方法
for (Method method : methods) {
Log.d(TAG, "--method--" + method.getName());
//得到方法上的所有注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
//interface com.zkq.myioc.animation.OnClick
Class<?> annotionClass = annotation.annotationType();
Log.e(TAG, "--annotionClass--" + annotionClass);
EventBase eventBase = annotionClass.getAnnotation(EventBase.class);
if (null == eventBase) {
continue;
}
Log.e(TAG, "--annotation--" + annotation.toString() + "---" + annotionClass);
//开始获取事件处理的相关信息,
// 用于确定是哪种事件(onClick还是onLongClick)以及由谁来处理
//订阅
String listenerSetter = eventBase.listenerSetter();
//事件(事件监听的类型)
Class<?> listenerType = eventBase.listenerType();
//事件处理 事件被触发之后,执行的回调方法的名称
String callBackMethod = eventBase.callbackMethod();
Method valueMethod = null;
try {
valueMethod = annotionClass.getDeclaredMethod("value");
int[] viewId= (int[]) valueMethod.invoke(annotation);
for (int id : viewId) {
Log.d("zkq","id--"+id);
Method findViewById=aClass.getMethod("findViewById",int.class);
View view= (View) findViewById.invoke(context,id);
if(view==null)
{
continue;
}
//得到ID对应的VIEW以后
//开始在这个VIEW上执行监听 (使用动态代理)
//需要执行activity上的onClick方法
//activity==context click==method
ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(context, method);
//proxy======View.OnClickListener()对象
Object proxy= Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);
//执行方法 setOnClickListener,new View.OnClickListener()
Method onClickMethod = view.getClass().getMethod(listenerSetter, listenerType);
onClickMethod.invoke(view, proxy);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
ListenerInvocationHandler实现方式,实现动态代理
public class ListenerInvocationHandler implements InvocationHandler {
private Object activity;
private Method activityMethod;
public ListenerInvocationHandler(Object activity, Method activityMethod) {
this.activity = activity;
this.activityMethod = activityMethod;
}
/**
*按钮点下去就执行这个方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return activityMethod.invoke(activity,args);
}
}
这样就实现了点击事件
总结
这里讲解一下EventBase这个注解的注解
如果我们想要实现
// textView.setOnLongClickListener(new View.OnLongClickListener() {
// @Override
// public boolean onLongClick(View v) {
// return false;
// }
// });
我们就只需要将
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,callbackMethod = "onClick")
public @interface OnClick {
int[] value() default -1;
}
中的EventBase修改成如下,和setOnLongClickListener中的方法相对应,就行了
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnLongClickListener",listenerType = View.OnLongClickListener.class,callbackMethod = "onLongClick")
public @interface OnLongClick {
int[] value() default -1;
}
在mainActivity中调用
@OnLongClick({R.id.text_long})
public boolean MyLongClick(View view){
Toast.makeText(MainActivity.this, "长按被点击", Toast.LENGTH_SHORT).show();
return true;
}
demo下载地址demo下载