简论
每个APP几乎都会有个登陆功能,也自然需要某些按钮点击后验证是否登陆,没有登陆则跳转登陆界面, 较为庞大的项目可能每个模块之间的跳转都不同,我们传统的写法就是实现不同的OnClickListener类,然后将其实例set进去,又或者在BaseActivity中扩展其他方法,功能上是没问题,不过这样的写法让人有点头疼, 同界面可能这个需要验证那个不需要验证那么就需要两套方式来解决,后期改动对于公司新人也不是很友好。所以,可以整个类似拦截器一样的东西, 在保证原始写法的同时,使用某些方法,或者注解的形式标注是否需要验证, 这种效果笔者感觉还是比较理想。
反射
要说到拦截原生点击事件, 第一个想法就是用到反射了
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
View的点击触摸等手势事件都会被存放在一个ListenerInfo对象中,于是我们可以通过反射获取ListenerInfo对象然后再次反射获取到OnClickListener,然后重新为这个View设置一个点击事件, 这个点击事件的执行为先判断是否登陆,没有登陆去登陆,否则执行获取到的OnClickListener,就是这么简单
Object info = ReflectUtils.reflect(v).field("mListenerInfo").get();
final View.OnClickListener oriClick = ReflectUtils.reflect(info).field("mOnClickListener").get();
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!loginChunk.verifyLogin()) {
loginChunk.goLogin();
} else if (oriClick != null) {
oriClick.onClick(view);
}
}
});
同样适用于第三方库
BaseRecyclerViewAdapterHelper
final BaseQuickAdapter.OnItemClickListener itemClickListener = ReflectUtils.reflect(adapter).field("mOnItemClickListener").get();
if (itemClickListener == null)
return;
adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
if (!loginChunk.verifyLogin())
loginChunk.goLogin();
else if (itemClickListener != null) {
itemClickListener.onItemClick(adapter, view, position);
}
}
});
性能
反射确实会带来一些性能上问题, 原本7ms可以打开的界面反射后可能会10ms甚至更久, 但是设定点击事件与ui的刷新毫无关联,所以我们完全可以放在子线程中进行。
换种更加简洁的方式实现,定义一个注解
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginDunk {
}
从一个实例中获取标注注解的View,
Field[] fields = instance.getClass().getDeclaredFields();
List<View> signViews = new ArrayList<>();
for (Field field : fields) {
if (field.getAnnotation(LoginDunk.class) == null)
continue;
if (field.getType() != View.class)
continue;
field.setAccessible(true);
signViews.add((View) field.get(instance));
}
回去到View后执行之前的步骤即可
GitHub已经为您做好了封装非侵入式实现点击校验登陆跳转 如果能助到您,希望留个star,谢谢?