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源码

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐