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

版权声明:本文为博主原创文章,转载请注明出处。

相关文章推荐

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

本文转载自其它博客作为笔记研究 ,感谢原文作者!

android自定义dialog实现app换肤功能

1.先是写自定义的MyDialog类 package com.oracle.cary.myphoneprotector1; import android.app.Dialog; import ...

Android通过sharedUserId机制实现APK间共享数据,App的换肤可以通过此机制实现

在被调用的apk的Manifest.xml中,为被外部调用的apk指定一个唯一的Filter(不唯一时,系统会提示用户选择),如下: ...

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

android 换肤和夜间模式切换的问题多种实现总结

ASP.NET2.0中轻松实现网站换肤(App_Themes的使用)

轉:ASP.NET2.0中轻松实现网站换肤(App_Themes的使用)1.简介   新建App_Themes文件夹:    项目 - 右键 - 添加 - 添加ASp.net文件夹 - 主题利用The...

加载自定义属性实现app换肤功能

在各大app中的换肤换主题的功能实现。博主的理解就是一种当用户点击更换主题按钮,从服务器下载主题。这种就是插件化加载。另一种就是自定义多套的属性,当用户点击的时候,就通过反射机制,在达到更换主题皮肤的...

让你的App,一键换肤吧

**Skin集成文档** 此框架无需启动应用即可一键换肤(支持background,string ,color,dimen),使用简单只需4步搞定,暂时无其它副作用。有BUG欢迎issue 支持An...

Android插件化开发实现动态换肤

今晚实在不想coding,于是想着整理点知识点,那么简单整理了下插件化开发实现动态更换皮肤。插件化开发大家应该不陌生或多或少用过或听过,插件化开发在项目业务拓展、模块化等方面有不小优势,当然实现一个完...

Android插件换肤功能实战

Android App实现换肤有很多方式,有的是通过内置资源的方式,有的是通过设置相同签名并且AndroidManifest.xml中配置相同android:sharedUserId使得两个apk运行...

Android换肤_MultipleTheme

MultipleTheme 真正的支持无缝换肤/夜间模式的Android框架,配合theme和换肤控件框架可以做到无缝切换换肤(无需重启应用和当前页面)。 该应用框架可以实现无缝换肤/切换...
  • Wshiduo
  • Wshiduo
  • 2017年02月05日 16:35
  • 252
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android app换肤详解
举报原因:
原因补充:

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