自定义轻量级android控件注解工具(大家都在用)
金剪刀(
GoldenScissors
)注解工具,是本人写的一个轻量级的android开发页面注解工具,代码量少,非常轻量级.之所以起这
个名字,主要是他jar
包非
常小,源码也非常少,使用方便,性能高,基本能满足大部分开发者的需求.字面意思,剪刀嘛也象征着锋利,
快速.
其中此注解工具用到的主要的技术是反射,并且是暴力反射.
接下来我主要介绍一下此注解工具怎么使用以及代码实现的原理,水平有限,如有不妥或错误,请同仁纠正之!
本工具涵盖三个最主要的注解类型,也是我们开发最常用到的,如下
@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();
}