Android一键换肤原理简述

简介

Android对应用进行换肤操作,首先要生成一个对应的皮肤包,在要换肤的应用中收集需要换肤的控件,获取皮肤包里的资源,一键换肤时遍历View树,对要换肤的控件进行换肤。下面总结为4个步骤

步骤

1 通过Activity.onCreateView()或 实现LayoutInfaltor.Factory2接口 创建收集可换肤控件

​ 这项工作应该在YourActivity.class或者BaseActivity.class或者LifecycleObserver.class中实现。这个步骤的实现自行搜索。

2 启动或触发换肤的时候加载皮肤包APK的资源

SkinManager.class

public void loaderSkinResources(skinPath){
    //反射创建AssestManager并通过路径加载Apk的资源
    //通过这个AssestManager获取Resource并用Map缓存 
    //创建资源管理器(此处不能用:application.getAssets())
    AssetManager assetManager = AssetManager.class.newInstance();
    // 由于AssetManager中的addAssetPath和setApkAssets方法都被@hide,目前只能通过反射去执行方法
    Method addAssetPath = assetManager.getClass().getDeclaredMethod(ADD_ASSET_PATH, String.class);
    // 设置私有方法可访问
    addAssetPath.setAccessible(true);
    // 执行addAssetPath方法
    addAssetPath.invoke(assetManager, skinPath);
    //==============================================================================
    // 如果还是担心@hide限制,可以反射addAssetPathInternal()方法,参考源码366行 + 387行
    //==============================================================================
    // 创建加载外部的皮肤包(123456.skin,实质是APK)文件Resources(注:依然是本应用加载)
    skinResources = new Resources(assetManager, appResources.getDisplayMetrics(), appResources.getConfiguration());

    // 根据apk文件路径(皮肤包也是apk文件),获取该应用的包名。兼容5.0 - 9.0(亲测)
    skinPackageName = application.getPackageManager().getPackageArchiveInfo(skinPath, PackageManager.GET_ACTIVITIES).packageName;
}
  

3 通过当前资源包的resourceId获取皮肤包里的资源

SkinManager.class

    private int getSkinResourceIds(int resourceId) {
        // 优化:如果没有皮肤包或者没做换肤动作,直接返回app内置资源!
        if (isDefaultSkin) return resourceId;

        // 使用app内置资源加载,是因为内置资源与皮肤包资源一一对应(“netease_bg”, “drawable”)
        String resourceName = appResources.getResourceEntryName(resourceId);
        String resourceType = appResources.getResourceTypeName(resourceId);

        // 动态获取皮肤包内的指定资源ID
        // getResources().getIdentifier(“netease_bg”, “drawable”, “com.netease.skin.packages”);
        int skinResourceId = skinResources.getIdentifier(resourceName, resourceType, skinPackageName);
    }

4 通过applyViews(getWindow().getDecorView())遍历

这个是换肤过程

    ...........
	SkinManager.getInstance().loaderSkinResources(skinPath);

    if (themeColorId != 0) {
        int themeColor = SkinManager.getInstance().getColor(themeColorId);
        StatusBarUtils.forStatusBar(this, themeColor);
        NavigationUtils.forNavigation(this, themeColor);
        ActionBarUtils.forActionBar(this, themeColor);
    }

    applyViews(getWindow().getDecorView());

    ..........................


    /**
     * 控件回调监听,匹配上则给控件执行换肤方法
     */
    protected void applyViews(View view) {
        if (view instanceof ViewsMatch) {
            ViewsMatch viewsMatch = (ViewsMatch) view;
            viewsMatch.skinnableView();
        }

        if (view instanceof ViewGroup) {
            ViewGroup parent = (ViewGroup) view;
            int childCount = parent.getChildCount();
            for (int i = 0; i < childCount; i++) {
                applyViews(parent.getChildAt(i));
            }
        }
    }

总结

文中所提及的4个步骤是实现换肤的基本原理,当然还有需要很多的优化,也还有兼容性问题,干货已给出啦~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值