android app换肤详解

原创 2017年10月12日 14:34:30

前言

现在很多APP中都会有换肤功能,看着很神奇,一键点击app大换样,那么这篇就来简单阐释一下换肤是如何实现的

效果图

这里写图片描述

图1

这里写图片描述

图2

换肤实现方式?

软件换肤从功能上可以划分三种:

1) 软件内置多个皮肤,不可由用户增加或修改;

最低的自由度,软件实现相对于后两种最容易。

2) 官方提供皮肤供下载,用户可以使用下载的皮肤;

用户可选择下载自己喜欢的皮肤,有些玩家会破解皮肤的定制方法,自己做皮肤使用,或者传到网上给大家用。

3) 官方提供皮肤制作工具或方法,用户可自制皮肤。

这种方式使用户有参与感,自由度较高。用户可根据自己的喜好定制软件的皮肤。有些软件官网提供皮肤定制的工具或者方法,我建议最好有可视化带向导的工具。用户只要自己找一些图片、修改文字的字体替换就可以了。用户可以上传自制的皮肤,提供其他用户下载,还可以赚得一些虚拟货币或者奖品什么的。这种一般都是打包为.zip格式的。扩展名可由各公司自定义,有制作工具的话直接导出来最方便。

首先我们要弄清楚换肤的定义,软件皮肤包括图标字体布局交互风格等,换肤就是换掉皮肤包括的部分或所有资源。

前面提到的三种皮肤,从软件实现上来看,它们的本质区别是皮肤是否内置到应用程序中。对于内置的实现比较简单,只要在开发应用的过程中设计几套皮肤供用户选择。这里用到的知识不超过Android基础,不详细讲解。

这里主要介绍外置方式

采用压缩包方式(zip/apk)

首先需要了解Context原理android资源管理机制分析,这两篇blog

换肤分为几个步骤

1.触发换肤逻辑

2.提前下载/触发时下载 皮肤资源包(apk,zip)

3.使用AssetManager创建 皮肤资源包Resource 对象

4.通知宿主app进行换肤动作。(需提前收集需要换肤的界面)

根据上面的步骤,写的demo

package com.nuoyuan.utils.changetheme;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.AsyncTask;

import com.nuoyuan.utils.base.SkinConfig;

import java.lang.reflect.Method;

/**
 * Author: weichyang
 * Date:   2017/10/12
 * Description: 解析皮肤资源包
 */
public class SkinPackageManager   
{  
  private static SkinPackageManager mInstance;  
  private Context mContext;
  /** 
   * 当前资源包名 
   */  
  public String mPackageName;  

  /** 
   * 皮肤资源 
   */  
  public Resources mResources;

  private SkinPackageManager(Context mContext)  
  {  
    this.mContext=mContext;  
  }  

  public static SkinPackageManager getInstance(Context mContext)  
  {  
    if(mInstance==null)  
    {  
      mInstance=new SkinPackageManager(mContext);  
    }  

    return mInstance;  
  }  


  /** 
   * 异步加载皮肤资源 
   * @param dexPath 
   *        需要加载的皮肤资源 
   * @param callback 
   *        回调接口 
   */  
  public void loadSkinAsync(String dexPath,final loadSkinCallBack callback)  
  {  
    new AsyncTask<String,Void,Resources>()
    {  

      protected void onPreExecute()   
      {  
        if(callback!=null)  
        {  
          callback.startloadSkin();  
        }  
      };  

      @Override  
      protected Resources doInBackground(String... params)   
      {  
        try {  
          if(params.length==1)  
          {  
            String dexPath_tmp=params[0];  
            PackageManager mPm=mContext.getPackageManager();
            PackageInfo mInfo=mPm.getPackageArchiveInfo(dexPath_tmp,PackageManager.GET_ACTIVITIES);
            mPackageName=mInfo.packageName;

            AssetManager assetManager = AssetManager.class.newInstance();
            Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
            addAssetPath.invoke(assetManager, dexPath_tmp);  

            Resources superRes = mContext.getResources();  
            Resources skinResource=new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());  
            SkinConfig.getInstance(mContext).setSkinResourcePath(dexPath_tmp);
            return skinResource;  
          }  
          return null;  
        } catch (Exception e) {  
          return null;  
        }   

      };  

      protected void onPostExecute(Resources result)   
      {  
        mResources=result;  

        if(callback!=null)  
        {  
          if(mResources!=null)  
          {  
            callback.loadSkinSuccess();  
          }else  
          {  
            callback.loadSkinFail();  
          }  
        }  
      };  

    }.execute(dexPath);  
  }

  /**
   * Author: weichyang
   * Date:   2017/1
   * Description:加载资源的回调接口 
   */
  public static interface loadSkinCallBack  
  {  
    public void startloadSkin();  

    public void loadSkinSuccess();  

    public void loadSkinFail();  
  }  



}  

分析下核心代码

PackageManager mPm=mContext.getPackageManager();
            PackageInfo mInfo=mPm.getPackageArchiveInfo(dexPath_tmp,PackageManager.GET_ACTIVITIES);
            mPackageName=mInfo.packageName;

            AssetManager assetManager = AssetManager.class.newInstance();
            Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
            addAssetPath.invoke(assetManager, dexPath_tmp);  

            Resources superRes = mContext.getResources();  
            Resources skinResource=new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());  

通过传递的资源apk 路径,用 PackageManager 获取到 资源apk 包名,因为 addAssetPath()为隐藏方法,采用反射进行调用,传递资源路径。然后拼装参数的构建皮肤资源apk的Resources 对象并设置为成员变量返回。

以上步骤就完成了 资源apk Resource的生成,有了Resources 也就有了调用资源文件的权限。

    //进行主题更新
    @Override
    public void updateTheme() {
        try {
          // 获取资源apk Resources对象
            Resources mResource = SkinPackageManager.getInstance(this).mResources;
           //调用colors 方式
            int id1 = mResource.getIdentifier("colorPrimary", "color", "theme.nydialog.com.theme");
            btn_main.setBackgroundColor(mResource.getColor(id1));
            int id2 = mResource.getIdentifier("colorPrimary", "color", "theme.nydialog.com.theme");
            main_view.setBackgroundColor(mResource.getColor(id2));
//调用图片资源,注意这里采用Drawable方式
            linearLayout.setBackground(mResource.getDrawable(mResource.getIdentifier("bg", "drawable","theme.nydialog.com.theme")));
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }

具体细节问题请查看

Nydialog https://github.com/yatou252303/Nydialog

引用
Android应用如何实现换肤功能
http://www.cnblogs.com/suiyc/archive/2011/05/27/2059778.html

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/o279642707/article/details/78214581

Android开发之APP换肤简介(一)

本文转载自其它博客作为笔记研究 ,感谢原文作者!
  • itluochen
  • itluochen
  • 2016-08-11 11:59:17
  • 2311

android app换肤(更换主题)

activity重启换肤和不重启换肤
  • yeyuehei
  • yeyuehei
  • 2016-10-13 17:19:21
  • 899

Android中插件开发篇之----应用换肤原理解析

一、前言今天又到周末了,感觉时间过的很快呀.这周媳妇生气了,所以就不能和她happy了,那只能写blog了。那么今天就来看看应用的换肤原理解析。在之前的一篇博客中我说道了Android中的插件开发篇的...
  • jiangwei0910410003
  • jiangwei0910410003
  • 2015-08-15 13:09:09
  • 23010

安卓一键换肤的详解

现在市面上众多阅读类App都提供了两种主题:白天or夜间。  上述两幅图片,正是两款App的夜间模式效果,所以,依据这个功能,来看看切换主题到底是怎么实现的(当然现在github有好...
  • u013378580
  • u013378580
  • 2016-10-21 10:33:52
  • 767

Android一键换肤功能:一种简单的实现

 Android一键换肤功能:一种简单的实现 现在的APP开发,通常会提供APP的换肤功能,网上流传的换肤代码和实现手段过于复杂,这里有一个开源实现,我找了一大堆,发现这个项目相对较为简洁:h...
  • zhangphil
  • zhangphil
  • 2016-05-19 17:03:41
  • 4329

Icon Font --Android图标也能轻松换肤

长期以来,android碎片化极为严重,各种屏幕尺寸,分辨率,可以说是android开发者最为头疼的问题,当然,google也提出了各种适配方案,提供了各种分辨率的drawable文件夹,单位转换(如...
  • djk_dong
  • djk_dong
  • 2015-09-10 14:12:30
  • 1054

android之换肤原理解读

如下是解读demo的链接,自行下载 https://github.com/fengjundev/Android-Skin-Loader 由于是开源的,而且对于想了解换肤功能的童鞋这个demo实...
  • zhongwn
  • zhongwn
  • 2016-10-22 17:21:59
  • 3499

Android 换肤原理分析和总结

Android 换肤资源的概括一个apk文件,实质为为zip文件,而对于Android来说,应用的安装的过程,其实就是一个复制过程,将第三方应用apk文件复制到/data/app目录,只不过中间涉及一...
  • zhi184816
  • zhi184816
  • 2016-12-02 17:25:31
  • 4300

一周总结 app换肤和夜间模式问题

android 换肤和夜间模式切换的问题多种实现总结
  • zhang_d_t
  • zhang_d_t
  • 2017-03-06 10:54:16
  • 246

android 换肤(1)——插件式无缝换肤(解析鸿洋大神的换肤流程)

对于app换肤,这是一个常见而又常用的功能。虽然我做的项目中还没涉及到换肤,但是还是想研究下。于是,下载了鸿洋大神的换肤demo来研究。 先看效果图:(尊重鸿洋大神的代码,效果图上原创) 鸿洋...
  • yehui928186846
  • yehui928186846
  • 2016-06-02 19:35:30
  • 2556
收藏助手
不良信息举报
您举报文章:android app换肤详解
举报原因:
原因补充:

(最多只允许输入30个字)