Afinal框架内置了四大模块功能:
1. FinalActivity:可通过注解技术为Activity的 View成员变量绑定一个指定id的View,同时也可以为这个View成员注册事件监听器。省去了我们之前使用findViewById()绑定View以及使用setOnXXX()为View注册监听器。尤其是当Activity的View成员比较多的时候,可以为我们省去很多findViewById(),setOnXXX()这样重复的语句,从而减少代码的冗余。
2. FinalDb:通过orm(对象关系映射 Object Relational Mapping)技术,使我们可以一行代码实现对Sqlite数据库的增删改查。且支持一对多,多对一等查询。
3. FinalBitmap:通过使用lru算法管理内存,多线程加载Bitmap,支持缓存并可配置线程数量、缓存大小,缓存路径,无需考虑oom和图片错位现象,可自定义加载动画。
4. FinalHttp:通过HttpClient对http请求进行封装,支持ajax方式加载。
注意事项:使用Afinal快速开发框架需要如下两个权限。
(1)访问网络权限(http请求网络时用)
<uses-permissionandroid:name="android.permission.INTERNET" />
(2)访问sdcard权限(图片缓存时用)
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" />
一、FinalActivity模块设计思路:
FinalActivity模块的作用就是通过注解为Activity的View类型的成员属性绑定一个View,如果这个View需要某事件监听器则同时为其注册事件监听器。所以设计此模块的思路自然是如何设计注解,并且如何实现View的绑定和事件监听器的注册。
第一步:在项目源代码的根目录下定义FinalActivity类,并且设计成抽象类。
设计成抽象类的目的是为了让继承它的Activity类可以使用FinalActivity模块的功能
public abstract class FinalActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
第二步:在项目源代码的annotation/view包下新建注解,注解中要包含View的id和支持的事件处理方法名。
ViewInject注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
/**
* 这是一个注解 ,只能标记在属性字段上,且保留到运行时,说明可以通过反射获取注解的属性值
*/
public @interface ViewInject {
public int id(); //View的id
public String click() default "";//点击事件处理方法名,默认为空串
public String longClick() default "";//长按事件处理方法名,默认为空串
public String itemClick() default "";//条目点击事件处理方法名,默认为空串
public String itemLongClick() default "";//条目长近事件处理方法名,默认为空串
public Select select() default @Select(selected="") ;//这里又设计了一个注解,代表条目选中和取消选中方法名,默认选中方法名为空串
}
Select注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
/**
* 是否被选中的注解
*/
public @interface Select {
public String selected(); //条目选中事件方法名
public String noSelected() default "";//条目取消选中事件方法名,默认为空串
}
第三步:在项目源代码的annotation/view包下设计事件监听总控制类,对外统一使用这个总控制类为View注册事件监听器。(目前Afinal框架只支持点击事件,长按事件,条目点击事件,条目长按事件,条目选中事件,条目取消选中事件)
1、创建EventListener类,实现Afinal支持的几个事件监听器接口,实现各自的抽象方法。
public class EventListener implements View.OnClickListener ,View.OnLongClickListener,AdapterView.OnItemClickListener,AdapterView.OnItemLongClickListener,AdapterView.OnItemSelectedListener{
@Override
public void onClick(View v) {
}
@Override
public boolean onLongClick(View v) {
return false;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
return false;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
}
分析:在设计这个控制类的时候并不知道将来使用者会如何处理各事件,所以这里的实现只是要通过反射获取注册此事件监听器的上下文Activity中用户通过注解指定的事件处理方法,然后回调这个方法。但是反射获取方法是需要方法名的。所以接下来先定义方法名,然后通过这个方法名再反射获取并回调这个方法。
2、定义事件处理方法名,并为其提供set方法,以方便改变其值。
public class EventListener implements View.OnClickListener ,View.OnLongClickListener,AdapterView.OnItemClickListener,AdapterView.OnItemLongClickListener,AdapterView.OnItemSelectedListener{
private String clickMethod;//处理点击事件的方法名
private String longClickMethod; //处理长按事件的方法名
private String itemClickMethod; //处理条目点击事件的方法名
private String itemSelectMethod; //处理条目选中事件的方法名
private String nothingSelectedMethod; //处理条目取消选中事件的方法名
private String itemLongClickMehtod; //处理条目长按事件的方法名
public EventListener click(String method){
this.clickMethod = method;
return this;
}
public EventListener longClick(String method){
this.longClickMethod = method;
return this;
}
public EventListener itemLongClick(String method){
this.itemLongClickMehtod = method;
return this;
}
public EventListener itemClick(String method){
this.itemClickMethod = method;
return this;
}
public EventListener select(String method){
this.itemSelectMethod = method;
return this;
}
public EventListener noSelect(String method){
this.nothingSelectedMethod = method;
return this;
}
@Override
public void onClick(View v) {
}
@Override
public boolean onLongClick(View v) {
return false;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
return false;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
}
3、实现框架层面的各事件处理方法
/**
* 事件处理器实现类
*/
public class EventListener implements OnClickListener, OnLongClickListener, OnItemClickListener, OnItemSelectedListener,OnItemLongClickListener {
private Object handler;
private String clickMethod;
private String longClickMethod;
private String itemClickMethod;
private String itemSelectMethod;
private String nothingSelectedMethod;
private String itemLongClickMehtod;
/**
* 构造器----->传递处理者做为参数
* @param handler
*/
public EventListener(Object handler) {
this.handler = handler;
}
public EventListener click(String method){
this.clickMethod = method;
return this;
}
public EventListener longClick(String method){
this.longClickMethod = method;
return this;
}
public EventListener itemLongClick(String method){
this.itemLongClickMehtod = method;
return this;
}
public EventListener itemClick(String method){
this.itemClickMethod = method;
return this;
}
public EventListener select(String method){
this.itemSelectMethod = method;
return this;
}
public EventListener noSelect(String method){
this.nothingSelectedMethod = method;
return this;
}
@Override
public boolean onLongClick(View v) {
return invokeLongClickMethod(handler,longClickMethod,v);
}
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
return invokeItemLongClickMethod(handler,itemLongClickMehtod,arg0,arg1,arg2,arg3);
}
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
invokeItemSelectMethod(handler,itemSelectMethod,arg0,arg1,arg2,arg3);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
invokeNoSelectMethod(handler,nothingSelectedMethod,arg0);
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
invokeItemClickMethod(handler,itemClickMethod,arg0,arg1,arg2,arg3);
}
@Override
public void onClick(View v) {
invokeClickMethod(handler, clickMethod, v);
}
/**
* 点击事件处理方法(实际上这里是回调了使用注解的类的事件处理方法)
*
*/
private static Object invokeClickMethod(Object handler, String methodName, Object... params){
if(handler == null) return null; //判断如何没有Activity传进来,则不做处理,直接返回
Method method = null;
try{
method = handler.getClass().getDeclaredMethod(methodName,View.class);//获取指定事件处理方法名的Method对象,
if(method!=null)
return method.invoke(handler, params); //回调事件处理方法
else
throw new ViewException("no such method:"+methodName);//这里抛出的是Afinal框架自己定义的一个异常,
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/**
* 长按事件处理方法(实际上这里是回调了使用注解的类的事件处理方法)
*
*/
private static boolean invokeLongClickMethod(Object handler, String methodName, Object... params){
if(handler == null) return false;
Method method = null;
try{
//public boolean onLongClick(View v)
method = handler.getClass().getDeclaredMethod(methodName,View.class);
if(method!=null){
Object obj = method.invoke(handler, params);
return obj==null?false:Boolean.valueOf(obj.toString());
}
else
throw new ViewException("no such method:"+methodName);
}catch(Exception e){
e.printStackTrace();
}
return false;
}
/**
* 条目点击事件处理方法(实际上这里是回调了使用注解的类的事件处理方法)
*
*/
private static Object invokeItemClickMethod(Object handler, String methodName, Object... params){
if(handler == null) return null;
Method method = null;
try{
///onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class,View.class,int.class,long.class);
if(method!=null)
return method.invoke(handler, params);
else
throw new ViewException("no such method:"+methodName);
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/**
* 条目长按事件处理方法(实际上这里是回调了使用注解的类的事件处理方法)
*
*/
private static boolean invokeItemLongClickMethod(Object handler, String methodName, Object... params){
if(handler == null) throw new ViewException("invokeItemLongClickMethod: handler is null :");
Method method = null;
try{
///onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,long arg3)
method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class,View.class,int.class,long.class);
if(method!=null){
Object obj = method.invoke(handler, params);
return Boolean.valueOf(obj==null?false:Boolean.valueOf(obj.toString()));
}
else
throw new ViewException("no such method:"+methodName);
}catch(Exception e){
e.printStackTrace();
}
return false;
}
/**
* 条目选中事件处理方法(实际上这里是回调了使用注解的类的事件处理方法)
*
*/
private static Object invokeItemSelectMethod(Object handler, String methodName, Object... params){
if(handler == null) return null;
Method method = null;
try{
///onItemSelected(AdapterView<?> arg0, View arg1, int arg2,long arg3)
method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class,View.class,int.class,long.class);
if(method!=null)
return method.invoke(handler, params);
else
throw new ViewException("no such method:"+methodName);
}catch(Exception e){
e.printStackTrace();
}
return null;
}
/**
* 条目取消选中事件处理方法(实际上这里是回调了使用注解的类的事件处理方法)
*
*/
private static Object invokeNoSelectMethod(Object handler, String methodName, Object... params){
if(handler == null) return null;
Method method = null;
try{
//onNothingSelected(AdapterView<?> arg0)
method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class);
if(method!=null)
return method.invoke(handler, params);
else
throw new ViewException("no such method:"+methodName);
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
第四步:设计注解消费机,为Activity的View成员属性绑定View
public abstract class FinalActivity extends Activity {
/**
* 为Activity设置视图
*
* @param layoutResID 布局文件的ID
*/
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
//调用注解消费机
initInjectedView(this);
}
/**
* @param view View
* @param params 布局属性
*/
public void setContentView(View view, LayoutParams params) {
super.setContentView(view, params);
//调用注解消费机
initInjectedView(this);
}
/**
* @param view View
*/
public void setContentView(View view) {
super.setContentView(view);
//调用注解消费机
initInjectedView(this);
}
/**
*注解消费机重载方法
*
* @param activity
*/
public static void initInjectedView(Activity activity) {
//重载的方法,带两个参数,第一个参数是Activity类型的,第二个参数是Activity的根View
//参数是Activity,说明只有在Activity中可以使用注解
initInjectedView(activity, activity.getWindow().getDecorView());
}
/**
注解消费机
* @param injectedSource 注入源
* @param sourceView 源View
*/
public static void initInjectedView(Object injectedSource, View sourceView) {
//通过反射获得所有的属性
Field[] fields = injectedSource.getClass().getDeclaredFields();
//判断注入源是否有字段,如果有且个数大于0,则遍历所有的字段
if (fields != null && fields.length > 0) {
for (Field field : fields) {
try {
//其字段可访问,突破Java封装的限制
field.setAccessible(true);
//获得属性的值 ,如果不为null,则不处理这个属性值
if (field.get(injectedSource) != null)
continue;
//获得属性上的ViewInject注解,
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {//不为null说明这个属性上有这个注解
//获得注解的参数值,id
int viewId = viewInject.id();
//通过findViewById()找到View,并赋值给该属性,这个属性的值就是找到的View
field.set(injectedSource, sourceView.findViewById(viewId));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
第五步:完善注解消费机,为Activity的View成员属性注册事件监听器
public abstract class FinalActivity extends Activity {
/**
* 为Activity设置视图
*
* @param layoutResID 布局文件的ID
*/
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
//
initInjectedView(this);
}
/**
* @param view View
* @param params 布局属性
*/
public void setContentView(View view, LayoutParams params) {
super.setContentView(view, params);
initInjectedView(this);
}
/**
* @param view View
*/
public void setContentView(View view) {
super.setContentView(view);
initInjectedView(this);
}
/**
* 注解消费机重载方法
*
* @param activity
*/
public static void initInjectedView(Activity activity) {
//重载的方法,带两个参数,第一个参数是Activity类型的,第二个参数是Activity的根View
//参数是Activity,说明只有在Activity中可以使用注解
initInjectedView(activity, activity.getWindow().getDecorView());
}
/**
注解消费机
* @param injectedSource 注入源
* @param sourceView 源View
*/
public static void initInjectedView(Object injectedSource, View sourceView) {
//通过反射获得所有的属性
Field[] fields = injectedSource.getClass().getDeclaredFields();
//判断注入源是否有字段,如果有且个数大于0,则遍历所有的字段
if (fields != null && fields.length > 0) {
for (Field field : fields) {
try {
//其字段可访问,突破Java封装的限制
field.setAccessible(true);
//获得属性的值 ,如果不为null,则不处理这个属性值
if (field.get(injectedSource) != null)
continue;
//获得属性上的ViewInject注解,
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {//不为null说明这个属性上有这个注解
//获得注解的参数值,id
int viewId = viewInject.id();
//通过findViewById()找到View,并赋值给该属性,这个属性的值就是找到的View
field.set(injectedSource, sourceView.findViewById(viewId));
//为属性绑定事件监听器,这里应该是只支持四种事件监听
setListener(injectedSource, field, viewInject.click(), Method.Click);
setListener(injectedSource, field, viewInject.longClick(), Method.LongClick);
setListener(injectedSource, field, viewInject.itemClick(), Method.ItemClick);
setListener(injectedSource, field, viewInject.itemLongClick(), Method.itemLongClick);
Select select = viewInject.select();
if (!TextUtils.isEmpty(select.selected())) {
setViewSelectListener(injectedSource, field, select.selected(), select.noSelected());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static void setViewSelectListener(Object injectedSource, Field field, String select, String noSelect) throws Exception {
//获得属性字段的值
Object obj = field.get(injectedSource);
//判断属性字段的值类型是否是View类型的
if (obj instanceof View) {
//强制类型转化为AbsLIstView类型,调用setOnItemSelectedListener方法为View注册条目选中事件监听器
//new EventListener(injectedSource).select(select).noSelect(noSelect)这句代码的作用是什么?
((AbsListView) obj).setOnItemSelectedListener(new EventListener(injectedSource).select(select).noSelect(noSelect));
}
}
/**
* @param injectedSource 注入源
* @param field 字段
* @param methodName 事件监听器方法名
* @param method 方法标识
* @throws Exception
*/
private static void setListener(Object injectedSource, Field field, String methodName, Method method) throws Exception {
if (methodName == null || methodName.trim().length() == 0)
return;
//获取属性值
Object obj = field.get(injectedSource);
//由method确定要处理的事件类型
switch (method) {
case Click:
if (obj instanceof View) {
//为其注册事件监听器,new EventListener(injectedSource)创建事件监听器并初始化注入源
((View) obj).setOnClickListener(new EventListener(injectedSource).click(methodName));
}
break;
case ItemClick:
if (obj instanceof AbsListView) {
((AbsListView) obj).setOnItemClickListener(new EventListener(injectedSource).itemClick(methodName));
}
break;
case LongClick:
if (obj instanceof View) {
((View) obj).setOnLongClickListener(new EventListener(injectedSource).longClick(methodName));
}
break;
case itemLongClick:
if (obj instanceof AbsListView) {
((AbsListView) obj).setOnItemLongClickListener(new EventListener(injectedSource).itemLongClick(methodName));
}
break;
default:
break;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* 代表不同事件的枚举类
*/
public enum Method {
Click, LongClick, ItemClick, itemLongClick
}
}
设计思路搞清楚后,然后此框架就变的太容易了,下面是使用此模块的案例:
效果图:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
>
<LinearLayout
android:id="@+id/ll_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<EditText
android:id = "@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入用户名"
/>
<EditText
android:id = "@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入密码"
/>
</LinearLayout>
<Button
android:id = "@+id/btn_login"
android:layout_below="@id/ll_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
/>
</RelativeLayout>
逻辑代码:
public class MainActivity extends FinalActivity {
public static final string TAG = "MainActivity";
@ViewInject(id = R.id.et_username)
private EditText mUserNameEt;
@ViewInject(id = R.id.et_password)
private EditText mPassWordEt;
@ViewInject(id = R.id.btn_login,click = "login")
private Button mLoginBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 处理登录事件的逻辑的方法
* @param view
*/
public void login(View view){
Log.i(TAG,"登录成功!");
}
}
到此Afinal框架的FinalActivity模块就分析完了,下一篇将分析FinalDb模块。