自定义注解,告别findViewById,你只需要这样做

简介: 当然了,为了解决不必要的findViewById,互联网的世界里也涌现了很多出色的第三方,如ButterKnife,AndroidAnotations,还有XUtils,等等,这些第三方不可否认,是特别的优秀,功能也是非常的强大,使用起来也是非常的简单,但是,也有一定的负面影响。

传统的项目中,为了从XML文件找到各个控件,findViewById,是不得不去写的代码,以致于太多的控件,太多的findViewId,使我们的代码变得繁琐,获取,强转,千篇一律的重复着某种机制,其实内心也是蛮崩溃的。


当然了,为了解决不必要的findViewById,互联网的世界里也涌现了很多出色的第三方,如ButterKnife,AndroidAnotations,还有XUtils,等等,这些第三方不可否认,是特别的优秀,功能也是非常的强大,使用起来也是非常的简单,但是,也有一定的负面影响,显而可见,这些第三方,不仅仅有注解功能,还有联网,请求数据库等等其它很多功能,而我们只需要一个注解功能,这不等于,我需要一个苹果,你一下给了我一车水果,本来我的胃(内存)就小,这不无形中增加我胃的容量,这不是撑死我吗?其实说的通俗点就是,第三方很多冗余的代码,会占去我们的内存,基于这样的一个原因,不就是一个注解功能吗,我们何不自己实现呢?


下面我们就开始一步步实现吧:


实现注解Activity中的layout


首先我们定义一个接口,用来设置资源layout:


@Target(ElementType.TYPE)//ElementType.TYPE只能在类中使用此注解@Retention(RetentionPolicy.RUNTIME)// @Retention(RetentionPolicy.RUNTIME) 注解可以在运行时通过反射获取一些信息@Documentedpublic@interfaceFindViewByIdLayout {
intvalue();
}


定义一个工具类,用来初始化布局文件和所在的Activity:


publicclassViewUtils {
/*** 保存传入的activity*/privatestaticClass<?>activityClass;
/*** 初始化activity和所有注解** @param obj 你需要初始化的activity*/publicstaticvoidinject(Objectobj) {
activityClass=obj.getClass();
injectContent(obj);
    }
// 初始化activity布局文件privatestaticvoidinjectContent(Objectobj) {
FindViewByIdLayoutannotation=activityClass                .getAnnotation(FindViewByIdLayout.class);
if (annotation!=null) {
// 获取注解中的对应的布局id 因为注解只有个方法 所以@XXX(YYY)时会自动赋值给注解类唯一的方法intid=annotation.value();
try {
// 得到activity中的方法 第一个参数为方法名 第二个为可变参数 类型为 参数类型的字节码Methodmethod=activityClass.getMethod("setContentView",
int.class);
// 调用方法 第一个参数为哪个实例去调用 第二个参数为 参数method.invoke(obj, id);
            } catch (Exceptione) {
e.printStackTrace();
            }
        }
    }
}


在所在的Activity里进行调用:


@FindViewByIdLayout(R.layout.activity_main)
publicclassMainActivityextendsAppCompatActivity {
@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.inject(this);
    }
}


实现各个view的注解:


和上面的注解layout步骤一致,首先我们也是定义一个注解View的接口:


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interfaceFindViewById {
/*** 保存view控件的id** @return view控件id*/intvalue();
}


在上面的ViewUtils工具类中实现注解View的方法:


/*** 初始化activity中的所有view控件*/privatestaticvoidinjectView(ObjectactivityOrFragment) {
// 对象所有的属性Field[] declaredFields=null;
// 健壮性if (activityClass!=null) {
// 获取du所有的属性 包含私有 保护 默认 共开 但不包含继承等// getFields可以获取到所有公开的包括继承的 但无法获取到私有的属性declaredFields=activityClass.getDeclaredFields();
    }
// 健壮性if (declaredFields!=null) {
// 遍历所有的属性变量for (Fieldfield : declaredFields) {
// 获取属性变量上的注解FindViewByIdannotation=field.getAnnotation(FindViewById.class);
// 如果此属性变量 包含FMYViewViewif (annotation!=null) {
// 获取属性id值intid=annotation.value();
Objectobj=null;
try {
// 获取activity中方法obj=activityClass.getMethod("findViewById",
int.class).invoke(activityOrFragment, id);
// 设置属性变量 指向实例// 如果修饰符不为公共类 这里注意了 当activity// 控件变量为private的时候 我们去访问会失败的 要么打破封装系 要么变量改为public//如 private TextView tv 这种情况 如果不打破封装会直接异常if (Modifier.PUBLIC!=field.getModifiers()) {
// 打破封装性field.setAccessible(true);
                    }
// 这里相当于 field= acitivity.objfield.set(activityOrFragment, obj);
                } catch (Exceptione) {
// TODO Auto-generated catch blocke.printStackTrace();
                }
            }
        }
    }
}


在ViewUtils 工具类inject方法里,添加上述方法,具体使用如下:


@FindViewByIdLayout(R.layout.activity_main)
publicclassMainActivityextendsAppCompatActivity {
@FindViewById(R.id.tv)
privateTextViewmText;
@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.inject(this);
mText.setText("AbnerMing");
    }
}


注解点击事件,和上述步骤一致,写接口,实现其注解方法:


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public@interfaceOnClick {
/*** 保存所有需要设置点击事件控件的id** @return*/int[] value();
}


在上面的ViewUtils工具类中实现注解点击事件的方法:


/*** 初始化所有控件的点击事件 只需要某方法上写上对应注解和id即可** @param*/privatestaticvoidinijectOnClick(ObjectactivityOrFragment) {
//获得所有方法Method[] methods=null;
methods=activityClass.getMethods();
// 遍历所有的activity下的方法for (Methodmethod : methods) {
// 获取方法的注解OnClickfmyClickView=method                .getAnnotation(OnClick.class);
// 如果存在此注解if (fmyClickView!=null) {
// 所有注解的控件的idint[] ids=fmyClickView.value();
// 代理处理类ViewUtils.MInvocationHandlerhandler=newViewUtils.MInvocationHandler(activityOrFragment,
method);
// 代理实例 这里也可以返回     new Class<?>[] { View.OnClickListener.class }中的接口类//第一个参数用于加载其他类 不一定要使用View.OnClickListener.class.getClassLoader() 你可以使用其他的//第二个参数你所实现的接口ObjectnewProxyInstance=Proxy.newProxyInstance(
View.OnClickListener.class.getClassLoader(),
newClass<?>[]{View.OnClickListener.class}, handler);
// 遍历所有的控件id 然后设置代理for (inti : ids) {
try {
Objectview=null;
//如果对象是activityview=activityClass.getMethod("findViewById",
int.class).invoke(activityOrFragment, i);
if (view!=null) {
Methodmethod2=view.getClass().getMethod(
"setOnClickListener",
View.OnClickListener.class);
method2.invoke(view, newProxyInstance);
                    }
                } catch (Exceptione) {
e.printStackTrace();
                }
            }
        }
    }
}
staticclassMInvocationHandlerimplementsInvocationHandler {
//这里我们到时候回传入activityprivateObjecttarget;
// 用户自定义view 的点击事件方法privateMethodmethod;
publicMInvocationHandler(Objecttarget, java.lang.reflect.Methodmethod) {
super();
this.target=target;
this.method=method;
    }
@OverridepublicObjectinvoke(Objectproxy, Methodmethod, Object[] args)
throwsThrowable {
// 调用用户自定义方法的点击事件 让activity调用中开发者设定的方法returnthis.method.invoke(target, args);
    }
}


在ViewUtils 工具类inject方法里,添加上述方法,具体使用如下:


@OnClick(R.id.tv)
publicvoidonClick(Viewview) {
Toast.makeText(this, "AbnerMing", Toast.LENGTH_LONG).show();
}


相关文章
|
2月前
|
存储 Android开发 开发者
Android项目架构设计问题之定义RecyclerView的ViewHolder如何解决
Android项目架构设计问题之定义RecyclerView的ViewHolder如何解决
37 0
|
2月前
|
Java Android开发 Kotlin
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
30 0
|
Java API Spring
傻瓜,自定义注解你会写了吗?(1)
傻瓜,自定义注解你会写了吗? (1)
傻瓜,自定义注解你会写了吗?(1)
|
安全 Android开发 Kotlin
viewBinding的使用记一次重构项目的过程
最近放寒假了,终于有空做项目了,想着把之前的一些项目重构一下,碰巧重构到 view Binding 这块,之前都是用 kotlin 的那个扩展,其实刚开始做项目的时候这个就已经废弃了,由于当时自己有点懒,没去学习这个新的代替方案,所以就成为一个历史遗留的问题,参考官方文档
177 0
傻瓜,自定义注解你会写了吗?(2)
傻瓜,自定义注解你会写了吗?(2)
|
存储 缓存 Kotlin
策略模式应用 | 每当为 RecyclerView 新增类型时就很抓狂
App 界面愈发复杂,元素越来越多,将不同类型的元素组织成 RecyclerView 就可以超越屏幕的限制。常用的RecyclerView在使用时有诸多痛点。这一篇尝试让扩展列表数据类型变得简单。
178 0
|
设计模式 缓存 NoSQL
专治不会看源码的毛病--spring源码解析AOP篇
总结一下要形成的习惯:   1>有空时隔一段时间要做几道算法题,C语言和JAVA都可以,主要是训练思维。 2>定期阅读spring的源码。因为spring是框架,重设计,能够培养大局观。   3>阅读底层的书籍,如linux方面,虚拟机方面,这是内功。越高级的语言只是招式。   4>不要忘记做了一半的东西,如搜索引擎方面,redis方面,可以过一段时间再做,因为到时候自己的境界有提升,深入程度也会有所增加。      下面是今天的正题。我也很菜,看源码也很费力,所以都会从最容易的入手。先了解其原理,再去看源码。看源码看熟了,以后再遇到问题,就可以通过源码去了解原理了。
专治不会看源码的毛病--spring源码解析AOP篇
|
算法 UED
了解AppBarLayout应该从这几个方面入手
了解AppBarLayout应该从这几个方面入手
|
Java Android开发 编译器
04.手写ButterKnife(ButterKnife源码阅读)
源码地址:https://github.com/renzhenming/MyButterknife 相信大多数安卓人员开发中都在使用ButterKnife进行代码生成。
980 0
|
Web App开发 前端开发 索引