IoC是什么呢?
我自己也整不明白,我怎么解释,自己百度去吧
今天干啥,那就弄个注解加载布局加载view 设置监听吧
废话不多说,反正我感觉你也不想听,那就上代码吧
首先看界面
@ContentView(layoutRes = R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@InjectViews(viewRes = R.id.tv_test)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InitLayout.inject(this);
textView.setText("来呀打我呀!");
}
@OnClick(viewIds = {R.id.tv_demo})
public void click(View view){
Toast.makeText(getBaseContext(),"click",Toast.LENGTH_LONG).show();
}
@OnLongClick(viewIds = {R.id.tv_test})
public boolean longClic(View view) {
Toast.makeText(getBaseContext(),"longClic",Toast.LENGTH_LONG).show();
return false;
}
}
布局不给了,就是两个文本,自己写
然后再给你看看注解
注解activity 布局
/*
TYPE: 用于描述类、接口(包括注解类型) 或enum声明 Class, interface (including annotation type), or enum declaration
FIELD: 用于描述域 Field declaration (includes enum constants)
METHOD: 用于描述方法 Method declaration
PARAMETER: 用于描述参数 Formal parameter declaration
CONSTRUCTOR: 用于描述构造器 Constructor declaration
LOCAL_VARIABLE: 用于描述局部变量 Local variable declaration
ANNOTATION_TYPE: Annotation type declaration
PACKAGE: 用于描述包 Package declaration
TYPE_PARAMETER: 用来标注类型参数 Type parameter declaration
TYPE_USE: 能标注任何类型名称 Use of a type
RetentionPolicy
SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int layoutRes();
}
注解初始化view
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectViews {
int viewRes() default 0;
}
注解设置事件
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ClickBase(listener = "setOnClickListener" ,callback = "onClick",listenerType = View.OnClickListener.class)
public @interface OnClick {
int[] viewIds();
}
再看这个ClickBase
@Target(ElementType.ANNOTATION_TYPE)//在注解之上
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBase {
//设置事件得方法名称
String listener();
//事件回调的类行
Class<?> listenerType();
//回调的方法名称
String callback();
}
好了前期准备的差不多了,到核心技术了,不要眨眼
public class InitLayout {
public static void inject(Activity activity) {
injectLayout(activity);
injectViews(activity);
injectEvents(activity);
}
private static void injectViews(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
//获取到该方法下的变量(全部,不论访问权限)
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//获取到注解 InjectViews
InjectViews injectViews = field.getAnnotation(InjectViews.class);
if (injectViews != null) {
//获取到注解中的布局ID
int viewid = injectViews.viewRes();
// View view = activity.findViewById(viewid) //这个是方式之一,但是不够档次
try {
//获取指定类 (getMethod 第一个是方法名 第二个是参数的集合 可以用Object[])
Method method = clazz.getMethod("findViewById", int.class);
//执行getMethod获取到的方法
Object view = method.invoke(activity, viewid);
//给属性field赋值
field.set(activity, view);
//设置访问权限
field.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 设置点击事件
*
* @param activity
*/
private static void injectEvents(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
//获取到所有public的方法,包括父类
Method[] methods = clazz.getMethods();
//遍历方法,拿到有自己注解的方法
for (Method method : methods) {
//一个方法有多个注解
Annotation[] annotations = method.getAnnotations();
/*
遍历注解,拿到自己定义的注解
其实也可以这样拿到 某个注解 -》ContentView contentView = clazz.getAnnotation(ContentView.class);
但是这样会写死,只支持方法上一种注解
遍历所有注解,然后拿到每个注解上带有ClickBase注解的 注解 真是妙哉妙哉妙哉
后面只要写各种注解上使用ClickBase注解就可以
*/
for (Annotation annotation : annotations) {
//获取到注解上面的注解
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType != null) {
try {
//拿到方法注解上的注解
ClickBase clickBase = annotationType.getAnnotation(ClickBase.class);
if (clickBase != null) {
//拿到ClickBase的三个重要的值
String callbackStr = clickBase.callback();
String listenerStr = clickBase.listener();
Class<?> listenerType = clickBase.listenerType();
//Aop
ClickListenerHandler handler = new ClickListenerHandler(activity);
//把方法都添加到ClickListenerHandler中
handler.addMethod(callbackStr, method);
//实现代理
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);
/*
获取ids的也是可以这样写
OnClick onClick = (OnClick) annotation;
int[] ids = onClick.viewIds();
但是这样又写死了
*/
// //获取控件id 的方法
Method valueMethod = annotationType.getDeclaredMethod("viewIds");
// //执行获取id 的方法,拿到id列表
int[] ids = (int[]) valueMethod.invoke(annotation);
//遍历控件id列表
for (int id : ids) {
View view = activity.findViewById(id);
//获取到设置监听的方法
Method clickListener = view.getClass().getMethod(listenerStr, listenerType);
clickListener.invoke(view, listener);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
private static void injectLayout(Activity activity) {
//拿到activity的反射
Class<? extends Activity> clazz = activity.getClass();
//拿到反射类的注解
ContentView contentView = clazz.getAnnotation(ContentView.class);
if (contentView != null) {
//拿到注解的值
int layout = contentView.layoutRes();
try {
//获取指定类 (getMethod 第一个是方法名 第二个是参数的集合 可以用Object[])
Method method = clazz.getMethod("setContentView", int.class);
//执行getMethod获取到的方法
method.invoke(activity, layout);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
好了,这个就是核心代码了
发现了没,我们要设置事件的时候,就要引入一个东西 Aop
用注解的方法,去替掉回调的方法,nice 看懂了么,不懂没关系,我是不会多说的,耗子尾汁
先看看InvocationHandler实现类
public class ClickListenerHandler implements InvocationHandler {
//执行方法的对象
private Object target;
//替换掉回调的方法
private HashMap<String, Method> methodHashMap = new HashMap();
public ClickListenerHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
method = methodHashMap.get(name);
if (method != null) {
return method.invoke(target, args);
}
return null;
}
public void addMethod(String methodName,Method method){
methodHashMap.put(methodName,method);
}
}
行了,代码上了,然后扩展吧
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ClickBase(listener = "setOnLongClickListener" ,callback = "onLongClick",listenerType = View.OnLongClickListener.class)
public @interface OnLongClick {
int[] viewIds();
}
剩下的自己去鸟补吧
网友评论