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换肤简介(一)

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

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

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

【Android-View】浅谈Android换肤的几种实现方式

Android平台api没有特意为换肤提供一套简便的机制,这可能是外国的软件更注重功能和易用,不流行换肤。系统不提供直接支持,只能自行研究。 换肤,可以认为是动态替换资源(文字、颜色、字体大小、...
  • tantion
  • tantion
  • 2017年02月16日 10:14
  • 2007

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

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

Android换肤功能实现与换肤框架QSkinLoader使用方式介绍

框架地址:https://github.com/qqliu10u/QSkinLoader 效果图 如果想要看框架使用,请直接看第三部分。一、综述此框架脱胎于项目需要实现夜间模式的需求,在上一篇文章...
  • u013478336
  • u013478336
  • 2016年11月08日 15:48
  • 6068

Android主题换肤 无缝切换

转载地址:http://www.jianshu.com/p/af7c0585dd5b 今天再给大家带来一篇干货。 Android的主题换肤 ,可插件化提供皮肤包,无需Activity的重...
  • l_215851356
  • l_215851356
  • 2017年03月25日 10:16
  • 284

Android Theme-安卓样式换肤实践方案

所谓的主题切换,就是能够根据不同的设定,呈现不同风格的界面给用户,也就是所谓的换肤。 本文主要总结了目前作者所知的两种实现方案.概述一直以来,认为App换肤是件很酷的东西,不同用户可以自己打造喜欢的颜...
  • chang_xing
  • chang_xing
  • 2016年03月31日 18:05
  • 1473

Android动态换肤(一、应用内置多套皮肤)

动态换肤在很多android应用中都有使用,用户根据自己的喜好设置皮肤主题,可以增强用户使用应用的舒适度。 Android换肤可以分为很多种,它们从使用方式,用户体验以及项目框架设 计上...
  • u010163442
  • u010163442
  • 2016年04月07日 10:42
  • 2614

android之换肤原理解读

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

安卓一键换肤的详解

现在市面上众多阅读类App都提供了两种主题:白天or夜间。  上述两幅图片,正是两款App的夜间模式效果,所以,依据这个功能,来看看切换主题到底是怎么实现的(当然现在github有好...
  • u013378580
  • u013378580
  • 2016年10月21日 10:33
  • 666
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android app换肤详解
举报原因:
原因补充:

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