Android一键换肤原理简述

Posted 怪兽N

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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个步骤是实现换肤的基本原理,当然还有需要很多的优化,也还有兼容性问题,干货已给出啦~~

以上是关于Android一键换肤原理简述的主要内容,如果未能解决你的问题,请参考以下文章

Android一键换肤原理简述

三步实现Android应用内一键换肤,无需重启应用,无需编写多余代码

Android插件化的思考——仿QQ一键换肤,思考比实现更重要!

android之换肤原理解读

今日头条Android岗面试题:说说Android动态换肤实现原理

Android主题换肤 无缝切换