Android换肤系列 Activity资源加载

    通常在Activity的onCreate回调方法中调用setContentView方法来设置界面要显示的layout,setContentView方法会对layout文件进行解析和资源的加载。这里以常用的View、ImageView、TextView为例看下加载过程。
这里写图片描述
从上面的步骤能看出,View的初始化过程中资源的加载主要是通过TypeArray类来实现的,而TypeArray类内部又是通过Resources来加载资源。

Step 1. View初始化

   public View(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context);

         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,
                 defStyleAttr, 0);
         Drawable background = null;
         ....
          final int N = a.getIndexCount();
          for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
             switch (attr) {
                 case com.android.internal.R.styleable.View_background:
                     background = a.getDrawable(attr);
                     break;
                  ....
             }
           }
     }

    构造方法里面首先调用Context的obtainStyledAttributes取得TypeArray对象

Step 2. Context.obtainStyledAttributes

   public final TypedArray obtainStyledAttributes(
             AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
         return getTheme().obtainStyledAttributes(
             set, attrs, defStyleAttr, defStyleRes);
     }

    obtainStyledAttributes方法里面通过调用getTheme获取当前上下文的主题,这里的context实际上就是Activity对象,而Activity继承了ContextThemeWrapper,getTheme的具体操作就是在ContextThemeWrapper 中完成的。

   public Resources.Theme getTheme() {
         if (mTheme != null) {
            return mTheme;
        }
        mThemeResource = Resources.selectDefaultTheme(mThemeResource,
                getApplicationInfo().targetSdkVersion);
        initializeTheme();
        return mTheme;
    }
   private void initializeTheme() {
        final boolean first = mTheme == null;
        if (first) {
            mTheme = getResources().newTheme();
            Resources.Theme theme = mBase.getTheme();
            if (theme != null) {
                mTheme.setTo(theme);
            }
        }
        onApplyThemeResource(mTheme, mThemeResource, first);
    }

    最终Theme对象的生成是通过getResources().newTheme()这句代码实现的,这里的getResources()的实现在4.2的版本中Android做了修改:
4.2版本

     private Resources mResources;

     public Resources getResources() {
         if (mResources != null) {
             return mResources;
         }
         if (mOverrideConfiguration == null) {
             mResources = super.getResources();
             return mResources;
         } else {
             Context resc = createConfigurationContext(mOverrideConfiguration);
             mResources = resc.getResources();
             return mResources;
         }
     }

4.2以前的版本是继承至ContextWrapper

     public Resources getResources()
     {
         return mBase.getResources();
     }

    在4.2的版本中添加了mResources变量,用于支持OverrideConfiguration。这样当我们在换肤中替换Resources对象的时候不仅要替换Context对象的mResrouces,对应4.2及其以后的版本Activity对象内的mResrouces对象也要替换。Theme是定义在Resources中的内部类,每一个Theme实例内部都包含一个Resources对象的引用,因此这里的Resources对象也是需要替换的。

Step 3. TypeArray.getDrawable

    public Drawable getDrawable(int index) {
        final TypedValue value = mValue;
        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
            return mResources.loadDrawable(value, value.resourceId);
        }
        return null;
    }

    在TypeArray类的getDrawable方法,首先通过索引index获得资源项的信息TypedValue,接着再调用Resources类的loadDrawable方法进行资源的加载。

TextView的textColor属性的加载过程
Step 1.TextView构造方法

public TextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         final Resources.Theme theme = context.getTheme();
         TypedArray a = theme.obtainStyledAttributes(
                     attrs, com.android.internal.R.styleable.TextViewAppearance, defStyle, 0);
         TypedArray appearance = null;
         int ap = a.getResourceId(
                 com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
         a.recycle();
         if (ap != -1) {
             appearance = theme.obtainStyledAttributes(
                     ap, com.android.internal.R.styleable.TextAppearance);
         }
         if (appearance != null) {
             int n = appearance.getIndexCount();
             for (int i = 0; i < n; i++) {
                 int attr = appearance.getIndex(i);
                 switch (attr) {
                 case com.android.internal.R.styleable.TextAppearance_textColor:
                     textColor = appearance.getColorStateList(attr);
                     break;
                     ....
                 }
             }
         }
}

    TextView在初始化过程中加载颜色资源的过程和View加载背景资源是相似的,TextView调用TypeArray类的getColorStateList方法来获取TextView文本的颜色,getColorStateList中再调用Resources的loadColorStateList加载colorState资源。

ImageView的src属性的加载过程
Step 1.ImageView构造方法

     public More ...ImageView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         initImageView();
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.ImageView, defStyle, 0);
         Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
         if (d != null) {
             setImageDrawable(d);
         }
         ....
     }

    ImageView的src资源的加载和View的background资源的加载是一样的。

    通过上面3种资源的加载,可以看出在View及其子类在初始化的过程中,对于常用的颜色资源、图片资源、selector资源的加载主要是通过Resources的loadDrawable和loadColorStateList两个方法来实现的。那么对于换肤功能可以采用自定义ProxyResources类继承Resources,来实现对资源加载的拦截。首先检查皮肤包SkinResouces中是否存在要当前要查找的资源,如果皮肤包存在时,通过SkinResources加载资源;否则通过默认的defaultResouces中加载资源。对于layout资源和xml文件资源,则默认从defaultResources资源中加载,一般代码开发和布局layout的耦合性比较大,换肤需求中更改布局的情况也比较少。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值