zoukankan      html  css  js  c++  java
  • Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39269193,本文出自:【张鸿洋的博客】

    1、概述

    首先我们来吹吹牛,什么叫IoC,控制反转(Inversion of Control,英文缩写为IoC),什么意思呢?

    就是你一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量,那么你就new 出来用呗~~

    IoC的原则是:NO,我们不要new,这样耦合度太高;你配置个xml文件,里面标明哪个类,里面用了哪些成员变量,等待加载这个类的时候,我帮你注入(new)进去;

    这样做有什么好处呢?

    回答这个问题,刚好可以回答另一个问题,很多人问,项目分层开发是吧,分为控制层、业务层、DAO层神马的。然后每一层为撒子要一个包放接口,一个包放实现呢?只要一个实现包不行么~刚好,如果你了解了IoC,你就知道这些个接口的作用了,上面不是说,你不用new,你只要声明了成员变量+写个配置文件,有人帮你new;此时,你在类中,就可以把需要使用到的成员变量都声明成接口,然后你会发现,当实现类发生变化的时候,或者切换实现类,你需要做什么呢?你只要在配置文件里面做个简单的修改。如果你用的就是实实在在的实现类,现在换实现类,你需要找到所有声明这个实现类的地方,手动修改类名;如果你遇到了一个多变的老大,是吧,呵呵~

    当然了,很多会觉得,写个配置文件,卧槽,这多麻烦。于是乎,又出现了另一种方案,得,你闲配置文件麻烦,你用注解吧。你在需要注入的成员变量上面给我加个注解,例如:@Inject,这样就行了,你总不能说这么个单词麻烦吧~~

    当然了,有了配置文件和注解,那么怎么注入呢?其实就是把字符串类路径变成类么,当然了,反射上场了;话说,很久很久以前,反射很慢啊,嗯,那是很久很久以前,现在已经不是太慢了,当然了肯定达不到原生的速度~~无反射,没有任何框架。

    如果你觉得注解,反射神马的好高级。我说一句:Just Do It ,你会发现注解就和你写一个普通JavaBean差不多;反射呢?API就那么几行,千万不要被震慑住~

    2、框架实现

    得进入正题了,Android IOC框架,其实主要就是帮大家注入所有的控件,布局文件什么的。如果你用过xUtils,afinal类的框架,你肯定不陌生~

    注入View

    假设:我们一个Activity,里面10来个View。

    传统做法:我们需要先给这个Activity设置下布局文件,然后在onCreate里面一个一个的findViewById把~

    目标的做法:Activity类上添加个注解,帮我们自动注入布局文科;声明View的时候,添加一行注解,然后自动帮我们findViewById;

    于是乎我们的目标类是这样的:

    @ContentView(value = R.layout.activity_main)
    public class MainActivity extends BaseActivity
    {
    	@ViewInject(R.id.id_btn)
    	private Button mBtn1;
    	@ViewInject(R.id.id_btn02)
    	private Button mBtn2;

    3、编码


    1、定义注解

    首先我们需要两个注解文件:

    package com.zhy.ioc.view.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ContentView
    {
    	int value();
    }
    

    ContentView用于在类上使用,主要用于标明该Activity需要使用的布局文件。

    @ContentView(value = R.layout.activity_main)
    public class MainActivity

    package com.zhy.ioc.view.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ViewInject
    {
    	int value();
    }
    

    在成员变量上使用,用于指定View的Id

    @ViewInject(R.id.id_btn)
    	private Button mBtn1;

    简单说一下注解:定义的关键字@interface ; @Target表示该注解可以用于什么地方,可能的类型TYPE(类),FIELD(成员变量),可能的类型:

    public enum ElementType {
        /**
         * Class, interface or enum declaration.
         */
        TYPE,
        /**
         * Field declaration.
         */
        FIELD,
        /**
         * Method declaration.
         */
        METHOD,
        /**
         * Parameter declaration.
         */
        PARAMETER,
        /**
         * Constructor declaration.
         */
        CONSTRUCTOR,
        /**
         * Local variable declaration.
         */
        LOCAL_VARIABLE,
        /**
         * Annotation type declaration.
         */
        ANNOTATION_TYPE,
        /**
         * Package declaration.
         */
        PACKAGE
    }

    就是这些个枚举。

    @Retention表示:表示需要在什么级别保存该注解信息;我们这里设置为运行时。

    可能的类型:

    public enum RetentionPolicy {
        /**
         * Annotation is only available in the source code.
         */
        SOURCE,
        /**
         * Annotation is available in the source code and in the class file, but not
         * at runtime. This is the default policy.
         */
        CLASS,
        /**
         * Annotation is available in the source code, the class file and is
         * available at runtime.
         */
        RUNTIME
    }

    这些个枚举~

    2、MainActivity

    package com.zhy.zhy_xutils_test;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.Toast;
    
    import com.zhy.ioc.view.ViewInjectUtils;
    import com.zhy.ioc.view.annotation.ContentView;
    import com.zhy.ioc.view.annotation.ViewInject;
    
    @ContentView(value = R.layout.activity_main)
    public class MainActivity extends Activity implements OnClickListener
    {
    	@ViewInject(R.id.id_btn)
    	private Button mBtn1;
    	@ViewInject(R.id.id_btn02)
    	private Button mBtn2;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		
    		ViewInjectUtils.inject(this);
    
    		mBtn1.setOnClickListener(this);
    		mBtn2.setOnClickListener(this);
    	}
    
    	@Override
    	public void onClick(View v)
    	{
    		switch (v.getId())
    		{
    		case R.id.id_btn:
    			Toast.makeText(MainActivity.this, "Why do you click me ?",
    					Toast.LENGTH_SHORT).show();
    			break;
    
    		case R.id.id_btn02:
    			Toast.makeText(MainActivity.this, "I am sleeping !!!",
    					Toast.LENGTH_SHORT).show();
    			break;
    		}
    	}
    
    }
    

    注解都写好了,核心的代码就是ViewInjectUtils.inject(this)了~

    3、ViewInjectUtils

    1、首先是注入主布局文件的代码:

    /**
    	 * 注入主布局文件
    	 * 
    	 * @param activity
    	 */
    	private static void injectContentView(Activity activity)
    	{
    		Class<? extends Activity> clazz = activity.getClass();
    		// 查询类上是否存在ContentView注解
    		ContentView contentView = clazz.getAnnotation(ContentView.class);
    		if (contentView != null)// 存在
    		{
    			int contentViewLayoutId = contentView.value();
    			try
    			{
    				Method method = clazz.getMethod(METHOD_SET_CONTENTVIEW,
    						int.class);
    				method.setAccessible(true);
    				method.invoke(activity, contentViewLayoutId);
    			} catch (Exception e)
    			{
    				e.printStackTrace();
    			}
    		}
    	}

    通过传入的activity对象,获得它的Class类型,判断是否写了ContentView这个注解,如果写了,读取它的value,然后得到setContentView这个方法,使用invoke进行调用;

    有个常量:

    	private static final String METHOD_SET_CONTENTVIEW = "setContentView";

    2、接下来是注入Views

    private static final String METHOD_FIND_VIEW_BY_ID = "findViewById";
    	/**
    	 * 注入所有的控件
    	 * 
    	 * @param activity
    	 */
    	private static void injectViews(Activity activity)
    	{
    		Class<? extends Activity> clazz = activity.getClass();
    		Field[] fields = clazz.getDeclaredFields();
    		// 遍历所有成员变量
    		for (Field field : fields)
    		{
    			
    			ViewInject viewInjectAnnotation = field
    					.getAnnotation(ViewInject.class);
    			if (viewInjectAnnotation != null)
    			{
    				int viewId = viewInjectAnnotation.value();
    				if (viewId != -1)
    				{
    					Log.e("TAG", viewId+"");
    					// 初始化View
    					try
    					{
    						Method method = clazz.getMethod(METHOD_FIND_VIEW_BY_ID,
    								int.class);
    						Object resView = method.invoke(activity, viewId);
    						field.setAccessible(true);
    						field.set(activity, resView);
    					} catch (Exception e)
    					{
    						e.printStackTrace();
    					}
    
    				}
    			}
    
    		}
    
    	}
    获取声明的所有的属性,遍历,找到存在ViewInject注解的属性,或者其value,然后去调用findViewById方法,最后把值设置给field~~~

    好了,把这两个方法写到inject里面就好了。

    public static void inject(Activity activity)
    	{
    		
    		injectContentView(activity);
    		injectViews(activity);
    		
    	}

    本文主要了解了如何打造这么个框架,下一篇,将教大家如何注入事件 ,不要再写什么setXXXListener了~~~


    效果图:



    源码点击下载



    ---------------------------------------------------------------------------------------------------------------------------------------

    最后贴个广告:

    第一次录制视频~~~还望大家支持,共同进步~

    高仿微信5.2.1主界面及消息提醒




    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    Nginx配置IP白名单和黑名单
    MySQL外键的作用和创建
    MySQL命令大全
    Mysql数据类型详解
    常见英文简写
    ubuntu12.04destdrop删除不必要的软件
    Ubuntu Nginx 开机自启动
    再生龙小用------转载
    Clonezilla SE---克隆linux------转载
    linux下制作镜像文件
  • 原文地址:https://www.cnblogs.com/dingxiaoyue/p/4924887.html
Copyright © 2011-2022 走看看