Android换肤系列 Activity资源拦截与替换

    要实现对Android资源加载的拦截和替换,4.4 以下的版本可通过自定义Resources子类重写父类的loadDrawable和loadColorStateList两个方法,在方法中将请求资源替换成皮肤包中的资源。在4.4的系统中重写这两个方法在运行时会收到警告,但并不影响正常运行,但这种方式在Android 5.0 以后就不在适用了。
    另外一种实现资源加载拦截的方式是通过替换Resources内的静态缓存变量为自定义对象来实现资源的拦截。通过前面的Android换肤系列 Resources 可以知道Resources包含以下三个静态变量:sPreloadedDrawables, sPreloadedColorDrawables, sPreloadedColorStateLists。Resources类的loadDrawable和loadColorStateList方法在加载资源前会判断资源是否存在,当sPreloadedDrawables,sPreloadedColorDrawables或者sPreloadedColorStateLists存在缓存资源时,就直接返回缓存内容。

 Drawable loadDrawable(TypedValue value, int id)
            throws NotFoundException {
       ....
        Drawable.ConstantState cs;
        if (isColorDrawable) {
            cs = sPreloadedColorDrawables.get(key);
        } else {
            cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
        }
        ....
  }

  ColorStateList  loadColorStateList(TypedValue value, int id)
            throws NotFoundException {
        ColorStateList csl;
        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
            csl = mPreloadedColorStateLists.get(key);
            if (csl != null) {
                return csl;
            }
        }
        csl = mPreloadedColorStateLists.get(key);
        ....
   }

因此可以通过替换sPreloadedColorDrawables,sPreloadedDrawables,mPreloadedColorStateLists为重写了get方法的自定义类,来加载自定义的资源。由于缓存使用key来作为缓存资源的唯一标识,即在重写的get方法里面需要把key转化成资源id再通过自定义的resources加载资源。

    不同android版本间Resources的静态缓存变量的差异:

      api >= 18 
      private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
      private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists;
      private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables;
      api 16-17   
      private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables;
      private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists;
      private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables;
      api 14-15 
      private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables;
      private static final SparseArray<ColorStateList> mPreloadedColorStateLists;
      private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables;
      api < 14 
      private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables;
      private static final SparseArray<ColorStateList> mPreloadedColorStateLists

不同android版本间Resources的key生成的差异:

drawable key
    api >= 17
    long key = isColorDrawable ? value.data :(((long) value.assetCookie) << 32) | value.data;

    api < 17
    long key = (((long) value.assetCookie) << 32) | value.data;
colorStateList key
    api >= 16
    long key = (((long) value.assetCookie) << 32) | value.data;

    api < 16
    int key = (value.assetCookie << 24) | value.data;

    自定义DrawableLongSparseArray继承LongSparseArray, 重写父类的get方法, 在方法内部将缓存key转换成资源id, 在通过资源id加载皮肤包中的资源, 实现资源拦截替换。

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class DrawableLongSpareArray extends LongSparseArray<Drawable.ConstantState> {

    private LongSparseArray<Integer> resourceIdKeyMap;

    private LongSparseArray<Drawable.ConstantState> originalCache;

    private ProxyResources resources;

    public DrawableLongSpareArray(ProxyResources resources, LongSparseArray<Drawable.ConstantState> originalCache,
            LongSparseArray<Integer> resourceIdKeyMap) {
        this.resources = resources;
        this.originalCache = originalCache;
        this.resourceIdKeyMap = resourceIdKeyMap;
    }

    @Override
    public Drawable.ConstantState get(long key) {
        Integer id;
        if (resources != null && (id = resourceIdKeyMap.get(key)) != null) {
            Drawable dr = resources.loadDrawable(id);
            if (dr != null) {
                return dr.getConstantState();
            } else {
                return null;
            }
        } else {
            return originalCache.get(key);
        }
    }
}

    接下来就是实现id到key之间映射的生成。 可通过重写Resources类的getLayout方法,在inflate布局的时候将界面用到的所有资源注册到resourceIdKeyMap中去, 也可通过自定义LayoutInflater.Factory,注册到LayoutInflater当中以减少资源id注册的代码,过程如下图:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值