android Factory2,android 拦截系统View javapoet+自定义Factory2 实现android无痕埋点
1.前序主要是介绍怎么拦截所有点击事件,具体数据打点之类的可以参考上一篇上篇是通过aop 来实现拦截所有点击 事件,如果开发的时候我们到处乱写setOnClickListener,用切面可能就要扫描所有类,可能比较影响编译效率定义一个简单的activity,包含button我们通过studio->Tools-Layout Inspector 可以看到 , button 被替换成了AppCom
1.前序
主要是介绍怎么拦截所有点击事件,具体数据打点之类的可以参考上一篇
上篇是通过aop 来实现拦截所有点击 事件,如果开发的时候我们到处乱写setOnClickListener,用切面可能就要扫描所有类,可能比较影响编译效率
定义一个简单的activity,包含button
我们通过studio->Tools-Layout Inspector 可以看到 , button 被替换成了AppCompatButton
引用了V7 或者androidX 在setContentView的时候会自动帮我们的View替换成系统V7 或者androidX的View
通过 AppCompatViewInflater createView 可以看到 系统view 都被替换成了AppCompatXX
switch(name) {case "TextView":
view=createTextView(context, attrs);
verifyNotNull(view, name);break;case "ImageView":
view=createImageView(context, attrs);
verifyNotNull(view, name);break;case "Button":
view=createButton(context, attrs);
verifyNotNull(view, name);break;case "EditText":
view=createEditText(context, attrs);
verifyNotNull(view, name);break;
2.拦截View
在Activity onCreate 的时候可以通过 LayoutInflaterCompat.setFactory2 自定义factory,下面就是自定义View的最简单实现,
我们可以自定义View来定义所有字体,或者背景之类的, 我们每拦截一个view,就得重写一个view, 虽然说就这么几个TextView,Button,
LayoutInflaterCompat.setFactory2(
LayoutInflater.from(this),object: LayoutInflater.Factory2 {override fun onCreateView(name: String?, context: Context?, attrs: AttributeSet?): View?{return null}override fun onCreateView(parent: View?, name: String?, context: Context?, attrs: AttributeSet?): View?{ if("Button".equals(name)){return MyButton(context!!,attrs)
}
valdelegate = delegate
if(parent!=null){return delegate.createView(parent, name, context!!, attrs!!)
}else{return null;
}
}
}
)
3.Javapoet
javapoet是一个可以动态生成java代码的开源框架 传送
这里我们用javapoet给我们动态生成所有需要自定义的view,当然,写起来肯定比复制黏贴,重写复杂多了,这里主要是为了展示优雅的切面
javapoet可以用在安卓中自定义路由(类似Arount),自定义注解(类似Dagger,ButterKnift)
我们首先需要 自定义一个注解
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)public@interface AutoView {
Class[] values();
Class[] types();
}
这里有两个方法 values 就是所有需要重写的view, types用于传一些方法, javapoet在安卓里本身是一个javalibrary 这里为了方便直接把一些class传进去
@AutoView(values ={Button.class, TextView.class, LinearLayout.class},
types= {Context.class, AttributeSet.class})public classSelftView {
}
注解解析需要继承 AbstractProcessor
这里主要是完成两部, 拿到所有@AutoView 注解过的类,
自动生成 我们需要的view, 下面写的有点烦,主要直接通过 element.getAnnotation(BindView.class).value();
取会报javax.lang.model.type.MirroredTypeException
这里没啥逻辑,就是拿到注解类的包名,类名
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(AutoView.class);
List list =new ArrayList();
MyViewParam myViewParam=newMyViewParam();
MyViewInfo info;for(Element element : elements) {
Set keys = (Set)element.getAnnotationMirrors().get(0).getElementValues().keySet();for(ExecutableElement e: keys){
AnnotationValue value= element.getAnnotationMirrors().get(0).getElementValues().get(e);if(e.getSimpleName().toString().equals("values")){
String[] values= value.getValue().toString().split(",");for(int i=0;i
String mValue= values[i].substring(0,values[i].lastIndexOf("."));
info=newMyViewInfo();
info.name= mValue.substring(mValue.lastIndexOf(".")+1);
info.superClass=mValue;
list.add(info);
}
}else if(e.getSimpleName().toString().equals("types")){
String[] values= value.getValue().toString().split(",");for(int i=0;i
String mValue= values[i].substring(0,values[i].lastIndexOf("."));if(i==0){
myViewParam.contextClass=mValue;
}else if(i==1){
myViewParam.attributeClass=mValue;
}
}
}
}
System.out.println("name22="+list);
}
下面介绍几个主要方法
TypeName contextType =ClassName.bestGuess(myViewParam.contextClass);
通过完整类名获取类对应的TypeName, javapoet里面类型可以用TypeName包装
MethodSpec.Builder constructor1 =MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(contextType,"context").build());
constructor1.addStatement("super($L)","context");
定义一个构造函数, 并且传入一个参数context 可以对照源码
publicMyButton(Context context)
{
super(context);
}
重写View的onClickListener, 并且包装一个自定义的OnClickListener ,
ClassName clickClass = ClassName.get("com.lin.annotationclick", "MyClickListener");
TypeName clickType=ClassName.bestGuess("android.view.View.OnClickListener");
MethodSpec.Builder onClickListener= MethodSpec.methodBuilder("setOnClickListener")
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(clickType,"l").build());
onClickListener.addStatement("super.setOnClickListener(new $T(l))",clickClass);
这样我们就可以在自定义的OnClickListener里面进行通用事件处理
public voidsetOnClickListener(View.OnClickListener l)
{
super.setOnClickListener(newMyClickListener(l));
}
具体可以参考上面git源码
更多推荐
所有评论(0)