Android快速开发框架之Afinal设计思路分析(一)

来源:互联网 发布:网络雅思课程 编辑:程序博客网 时间:2024/06/06 07:11

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 {@Overrideprotected 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);   }     @Overridepublic 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模块。

1 0
原创粉丝点击