自定义轻量级android控件注解工具--金剪刀(自己命名的)(大家都在用)

时间:2022-06-01 19:04:10

自定义轻量级android控件注解工具(大家都在用自定义轻量级android控件注解工具--金剪刀(自己命名的)(大家都在用))

金剪刀(

GoldenScissors

)注解工具,是本人写的一个轻量级的android开发页面注解工具,代码量少,非常轻量级.之所以起这


个名字,主要是他jar

包非

常小,源码也非常少,使用方便,性能高,基本能满足大部分开发者的需求.字面意思,剪刀嘛也象征着锋利,


快速.

   其中此注解工具用到的主要的技术是反射,并且是暴力反射自定义轻量级android控件注解工具--金剪刀(自己命名的)(大家都在用).
接下来我主要介绍一下此注解工具怎么使用以及代码实现的原理,水平有限,如有不妥或错误,请同仁纠正之!


本工具涵盖三个最主要的注解类型,也是我们开发最常用到的,如下

@LayoutViewCut 绑定activity布局

@ViewCut 绑定控件资源 省去繁琐的findViewById

@OnClickCut 控件点击事件

使用方法:

项目源码已托管到本人github上,地址: https://github.com/jiangzhengyan/GoldenScissorsViewInject/  请多多关注哦

(1) 把libs下面的GoldenScissors.jar导进自己的项目中或者把library下的源码防盗自己的项目中

(2) 在onCreate方法中绑定此工具

 GoldenScissors.cut(this); 

(3) 根据需要,按照以下的方式去使用

1,@LayoutViewCut 注解绑定布局,(省去setContentView)

在继承activity的类位置添加注解(如 @LayoutViewCut(R.layout.activity_main)),例如

@LayoutViewCut(R.layout.activity_main)
public class MainActivity extends Activity {
@ViewCut({R.id.tv_1, R.id.tv_2})
private TextView tv1, tv2;

       

2,@ViewCut,可以代替findviewbyid找控件,在成员变量上面添加

        1,@ViewCut(R.id.tv_1).//单个控件

        2,@ViewCut({R.id.tv_1})  //单个控件

        3,@ViewCut({R.id.tv_1, R.id.tv_2}) //多个控件

        如下

    @ViewCut({R.id.tv_1, R.id.tv_2})
private TextView tv1, tv2;

@ViewCut(R.id.tv_3)
private TextView tv3;

@ViewCut({R.id.tv_4})
private TextView tv4;

@ViewCut({R.id.tv_5, R.id.tv_6, R.id.tv_7, R.id.tv_8,})
private TextView tv5, tv6, tv7, tv8;

   

3, @OnClickCut 点击事件

注:方法的的命名参数名必须为View,方法名只要符合基本命名规则就行

 @OnClickCut({R.id.tv_1, R.id.tv_2, R.id.tv_3, R.id.tv_4, R.id.btn})
private void click(View view) {
switch (view.getId()) {
case R.id.tv_1:
Toast.makeText(this, "点击了" + "tv1", Toast.LENGTH_SHORT).show();
break;
case R.id.tv_2:
Toast.makeText(this, "点击了" + "tv2", Toast.LENGTH_SHORT).show();
break;
case R.id.tv_3:
Toast.makeText(this, "点击了" + "tv3", Toast.LENGTH_SHORT).show();
break;
case R.id.tv_4:
Toast.makeText(this, "点击了" + "tv4", Toast.LENGTH_SHORT).show();
break;
case R.id.btn:
Toast.makeText(this, "点击了" + "按钮 5", Toast.LENGTH_SHORT).show();
break;

}


}

源代码解析


1, GoldenScissors这个类里面定义了一个cut方法,里面包含了三个工具最主要的三个处理注解逻辑的方法.

上源码
public static void cut(final Activity activity) {

bindLayout(activity);//布局
bindFields(activity);//控件
bindMethod(activity);//点击

}

首先要介绍一个自定义的异常(InjectException), 因为本项目要用到:就是直接继承异常的一个父类(RuntimeException)
public class InjectException extends RuntimeException {
private static final long serialVersionUID = -8782914729012957108L;

public InjectException() {

}

public InjectException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}

public InjectException(String detailMessage) {
super(detailMessage);
}

public InjectException(Throwable throwable) {
super(throwable);
}

}


下面介绍主要的三个地方法: 

布局
/**
* 绑定布局
*
* @param activity
* 所在对象
*/
private static void bindLayout(Activity activity) {
final Class<? extends Context> clazz = activity.getClass();

LayoutViewCut layoutViewCut = clazz.getAnnotation(LayoutViewCut.class);
if (layoutViewCut != null) {
int layoutResId = layoutViewCut.value();
try {
Method method = clazz.getMethod("setContentView",
new Class[] { Integer.TYPE });
method.invoke(activity, layoutResId);
} catch (Throwable th) {
throw new InjectException(new Throwable(th.getMessage() + " : "
+ th));
}
}
}

控件
/**
* 绑定多个成员变量
*
* @param activity
* 所在对象
*/
private static void bindFields(Activity activity) {

final Class<? extends Context> clazz = activity.getClass();
Field[] declaredFields = clazz.getDeclaredFields();

ArrayList<Integer> filedResIdList = new ArrayList<Integer>();
ArrayList<String> filedNameList = new ArrayList<String>();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
ViewCut injectFiled = field.getAnnotation(ViewCut.class);
if (injectFiled != null) {

String filedName = field.getName();
if (!filedNameList.contains(filedName)) {
filedNameList.add(filedName);
}
int[] resIds = injectFiled.value();

for (int x = 0; x < resIds.length; x++) {
int k = resIds[x];
if (!filedResIdList.contains(k)) {
filedResIdList.add(k);
}
}
}
}

int declaredFieldsLength = filedNameList.size();
int resIdsLength = filedResIdList.size();

if (declaredFieldsLength > resIdsLength) {
throw new InjectException(
new Throwable(
"DeclaredFileds' counts cannot be more than inject ResIds' counts"));
} else if (declaredFieldsLength < resIdsLength) {
throw new InjectException(
new Throwable(
"Inject ResIds' counts cannot be more than DeclaredFileds' counts"));
}

for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
ViewCut injectFiled = field.getAnnotation(ViewCut.class);
if (injectFiled != null) {

int resId = filedResIdList.get(i);
View view = activity.findViewById(resId);
field.setAccessible(true);
try {
field.set(activity, view);
} catch (Throwable th) {
throw new InjectException(
new Throwable(
"Inject failed ,maybe your context is null or recorrect"));
}

}
}
}



点击事件
/**
* 绑定方法,即点击事件
*
* @param activity
* 所在对象
*/
private static void bindMethod(final Activity activity) {
final Class<? extends Context> clazz = activity.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (final Method method : methods) {
OnClickCut injectMetheds = method.getAnnotation(OnClickCut.class);
method.setAccessible(true);
if (injectMetheds != null) {
int[] resIds = injectMetheds.value();
for (int i = 0; i < resIds.length; i++) {
final View view = activity.findViewById(resIds[i]);
view.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
try {
method.invoke(activity, view);
} catch (Throwable th) {
throw new InjectException(
new Throwable(
"Inject method failed,please checked you methed inject names"));
}

}
});
}

}

}
}



定义的三个@interface如下
@LayoutViewCut 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LayoutViewCut {

int value();

}

@ViewCut

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewCut {

int[] value();

}




@OnClickCut
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClickCut {

int[] value();
}