wyouflf/xUtils3
XUtils框架
Android Xutils 框架的介绍
xUtils使用详细介绍
维基百科-控制反转IOC
Java之注释(Annotation)
示例代码Github:https://github.com/345166018/AndroidIOC/tree/master/HxIOC
IOC 是原来由程序代码中主动获取的资源,转变由第三方获取并使原来的代码被动接收的方式,以达到解耦的效果,称为控制反转。
import org.xutils.x;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
x.Ext.init(this);
}
}
import org.xutils.x;
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
x.view().inject(this);
}
}
mport org.xutils.view.annotation.ContentView;
import org.xutils.view.annotation.Event;
import org.xutils.view.annotation.ViewInject;
@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {
@ViewInject(R.id.tv_text)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView.setText("修改了TextView的内容");
}
/*可选参数, 默认是View.OnClickListener.class*/
@Event(value = R.id.btn_click1, type = View.OnClickListener.class)
private void onClick1(View view) {
Toast.makeText(this, "点击按钮1", Toast.LENGTH_SHORT).show();
}
@Event(value = {R.id.btn_click2, R.id.btn_click3})
private void onClick23(View view) {
switch (view.getId()) {
case R.id.btn_click2:
Toast.makeText(this, "点击按钮2", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_click3:
Toast.makeText(this, "点击按钮3", Toast.LENGTH_SHORT).show();
break;
}
}
@Event(value = R.id.btn_click4, type = View.OnLongClickListener.class)
private boolean onLongClick4(View view) {
Toast.makeText(this, "长按按钮4", Toast.LENGTH_SHORT).show();
return false;
}
}
接下来手写实现上面的功能。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
int value();
}
@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
}
}
通过反射获取到注解上设置的值,并获取到setContentView,然后将值传递到setContentView方法中。
public class InjectUtils {
public static void inject(Object context) {
injectLayout(context);
}
private static void injectLayout(Object context) {
int layouId = 0;
Class<?> clazz = context.getClass();
ContentView contentView = clazz.getAnnotation(ContentView.class);
if (contentView != null) {
layouId = contentView.value();
}
try {
Method method = context.getClass().getMethod("setContentView", int.class);
method.invoke(context, layouId);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.inject(this);
}
}
这样就完成了一个最简单的布局注入,在MainActivity我们没有使用setContentView方法去设置布局文件,而是通过注解的形式,运行程序后app运行正常,说明达到了布局注入的目的。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
int value();
}
在button上添加注解ViewInject,这里没有使用findViewById方法去获取Button控件,并给button一个点击事件,查看是否真正获取到Button控件。
@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {
@ViewInject(R.id.btn_click)
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
Log.i("hongxue button string",button.toString());
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"点击按钮",Toast.LENGTH_SHORT).show();
}
});
}
}
通过反射执行findViewById
public class InjectUtils {
public static void inject(Object context) {
injectLayout(context);
injectView(context);
}
private static void injectView(Object context) {
Class<?> clazz = context.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields){
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if(viewInject != null){
int valueId = viewInject.value();
try {
//反射执行findViewById
Method method = clazz.getMethod("findViewById",int.class);
View view = (View) method.invoke(context,valueId);
field.setAccessible(true);//1
field.set(context,view);//2
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
...
}
注释1:
在java的反射使用中,如果字段是私有的,那么必须要对这个字段设置 。
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
注释2:将反射findViewById获取到的控件对象添加到对应的成员变量(field)中。
初始化在布局注入中已经说到,在BaseActivity中添加InjectUtils.inject(this);
android 所有的23事件
OnClickListener、OnLongClickListener …
点击事件的三要素
进行订阅 setOnClickListener
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
button.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {
int[] value() default -1;
}
在布局文件中添加了两个按钮,id分别为btn_click2和btn_click3。点击不同的按钮显示不同的信息。
@OnClick({R.id.btn_click2, R.id.btn_click3})
public boolean click(View view) {
if (view.getId() == R.id.btn_click2) {
Log.i("hongxue", " MainActivity btn 2 click");
Toast.makeText(this, "点击了button2", Toast.LENGTH_SHORT).show();
}
else if (view.getId() == R.id.btn_click3) {
Log.i("hongxue", " MainActivity btn 3 click");
Toast.makeText(this, "点击了button3", Toast.LENGTH_SHORT).show();
}
return false;
}
在InjectUtils添加injectEvent方法。
/**
* 事件注入
*/
private static void injectEvent(Object context) {
Class<?> clazz = context.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick == null) {
continue;
}
String listenerSetter = "setOnClickListener";
Class<?> listenerType = View.OnClickListener.class;
//String callBackMethod = "onClick";
try {
int[] viewId = onClick.value();
for (int id : viewId) {
Method findViewById = clazz.getMethod("findViewById", int.class);
View view = (View) findViewById.invoke(context, id);
if (view == null) {
continue;
}
ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(context, method);
Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);
Method onClickMethod = view.getClass().getMethod(listenerSetter, listenerType);
onClickMethod.invoke(view, proxy);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
将injectEvent方法添加到inject方法中。
public static void inject(Object context) {
injectLayout(context);
injectView(context);
injectEvent(context);
}
public class ListenerInvocationHandler implements InvocationHandler {
private Object activity;
private Method activityMethod;
public ListenerInvocationHandler(Object activity, Method activityMethod) {
this.activity = activity;
this.activityMethod = activityMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在这里去调用被注解了的click()
return activityMethod.invoke(activity,args);
}
}
前面已经提到Android有23种事件,所以可以再创建一个注解,用于事件注解上,这样我们就不需要在injectEvent中去添加事件注解的三要素,而是可以在创建事件注解的时候再去设置三要素。
@Retention(RetentionPolicy.RUNTIME)
//该注解在另外一个注解上使用
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventBase {
// setOnClickListener 订阅
String listenerSetter();
// 事件以及他的类型
/**
* 事件监听的类型
*/
Class<?> listenerType();
/**
* 事件处理
*/
String callbackMethod();
}
在@OnClick注解上使用使用@EventBase注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener"
, listenerType = View.OnClickListener.class
, callbackMethod = "onClick")
public @interface OnClick {
int[] value() default -1;
}
/**
* 事件注入
*/
private static void injectEvent(Object context) {
Class<?> clazz = context.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
// OnClick onClick = method.getAnnotation(OnClick.class);
//得到方法上的所有注解
Annotation[] annotations = method.getAnnotations();//1
for (Annotation annotation : annotations) {
// annotation ===OnClick OnClick.class
Class<?> annotionClass = annotation.annotationType();//2
EventBase eventBase = annotionClass.getAnnotation(EventBase.class);//3
//如果没有eventBase,则表示当前方法不是一个处理事件的方法
if (eventBase == null) {
continue;
}
//开始获取事件处理的相关信息,
// 用于确定是哪种事件(onClick还是onLongClick)以及由谁来处理
//订阅
String listenerSetter = eventBase.listenerSetter();
//事件(事件监听的类型)
Class<?> listenerType = eventBase.listenerType();
//事件处理 事件被触发之后,执行的回调方法的名称
String callBackMethod = eventBase.callbackMethod();
// textView.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
//
// }
// });
// int[] value1=OnClick.value();//这就写死了
Method valueMethod = null;
try {
//反射得到ID,再根据ID号得到对应的VIEW
valueMethod = annotionClass.getDeclaredMethod("value");
int[] viewId = (int[]) valueMethod.invoke(annotation);
for (int id : viewId) {
Method findViewById = clazz.getMethod("findViewById", int.class);
View view = (View) findViewById.invoke(context, id);
if (view == null) {
continue;
}
//得到ID对应的VIEW以后
//开始在这个VIEW上执行监听 (使用动态代理)
//需要执行activity上的onClick方法
//activity==context click==method
ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(context, method);
//proxy======View.OnClickListener()对象
Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);
//执行方法 setOnClickListener,new View.OnClickListener()
Method onClickMethod = view.getClass().getMethod(listenerSetter, listenerType);
onClickMethod.invoke(view, proxy);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
再定义一个长按事件的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnLongClickListener",
listenerType = View.OnLongClickListener.class,
callbackMethod = "onLongClick")
public @interface OnLongClick {
int[] value() default -1;
}
@OnLongClick({R.id.btn_click2,R.id.btn_click3})
public boolean longClick(View view) {
if (view.getId() == R.id.btn_click2) {
Log.i("hongxue", " MainActivity btn 2 longlick");
Toast.makeText(this, "长按了button2", Toast.LENGTH_SHORT).show();
}
else if (view.getId() == R.id.btn_click3) {
Log.i("hongxue", " MainActivity btn 3 longClick");
Toast.makeText(this, "长按了button3", Toast.LENGTH_SHORT).show();
}
return true;//1
}
注释1:return true 可以拦截点击事件
示例代码Github:https://github.com/345166018/AndroidIOC/tree/master/HxIOC