Android 换肤功能的实现(Apk插件方式)
一、概述
由于Android 没有提供一套统一的换肤机制,我猜可能是因为国外更注重功能和体验的原因
所以国内如果要做一个漂亮的换肤方案,需要自己去实现。
目前换肤的方法大概有三种方案:
(1)把皮肤资源文件内置于应用程序Apk的资源目录下,这种方案最简单,但是导致apk安装包比会比比较大,而且不好管理
(2)将皮肤资源文件打包成zip的资源文件方式提供,该方法也比较多被采用。
(3)将皮肤图片资源以独立的Apk安装包的方式提供,做成插件化的方式。便于管理。
本文主要讨论第三种实现。
二、效果演示
首先看看实现的效果吧:

三、换肤功能的实现
现在把 皮肤资源apk叫做皮肤Apk,把需要换肤的应用程序叫做主程序APK吧。
基本原理主要是:
(1)新建一个Android项目-MySkin,把皮肤资源文件放在把项目的资源目录下,改包名为:com.czm.myskin
(2)新建一个主程序Apk应用Android项目-MySkinDemo,通过皮肤Apk的包名,获取其Context:
方法如下:
mSkinContext= this.getApplicationContext().createPackageContext("com.czm.myskin",
Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
为什么要用 Context.CONTEXT_IGNORE_SECURITY,且看api文档吧:
public static final int CONTEXT_IGNORE_SECURITY Added in API level 1
Flag for use with createPackageContext(String, int): ignore any security restrictions on the Context being requested, allowing it to always be loaded. For use with CONTEXT_INCLUDE_CODE to allow code to be loaded into a process even when it isn't safe to do so. Use with extreme care! Constant Value: 2 (0x00000002)
public static final int CONTEXT_INCLUDE_CODE Added in API level 1
Flag for use with createPackageContext(String, int): include the application code with the context. This means loading code into the caller's process, so that getClassLoader() can be used to instantiate the application's classes. Setting this flags imposes security restrictions on what application context you can access; if the requested application can not be safely loaded into your process, java.lang.SecurityException will be thrown. If this flag is not set, there will be no restrictions on the packages that can be loaded, but getClassLoader() will always return the default system class loader. Constant Value: 1 (0x00000001)
拿到皮肤Apk的context后,我们就可以拿到里面的皮肤资源文件和图片了
当然了,这里为了实现运行在同一个进程,需要将皮肤Apk-MySkin 的 android:sharedUserId 这个属性配置为 主程序MySkinDemo的包名:即:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.czm.myskin"
android:sharedUserId="com.czm.myskindemo"
>
至于android:sharedUserId 这个的作用和意义,还是看官方api文档吧:
android:sharedUserId
The name of a Linux user ID that will be shared with other applications. By default, Android assigns each application its own unique user ID. However, if this attribute is set to the same value for two or more applications, they will all share the same ID — provided that they are also signed by the same certificate. Application with the same user ID can access each other's data and, if desired, run in the same process.
(3)为了让用户无感知,需要安装后皮肤APk后,让自己不可以打开,且不生成桌面图标,
如下图:

其实这里有个小窍门就是 不设置其
category的 Launcher : 即 把
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
这个过滤器去掉即可
如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.czm.myskin"
android:sharedUserId="com.czm.myskindemo"
> <application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
>
</activity>
</application> </manifest>
到此为止,Apk插件换肤功能方案已经完成实现。
下面是主程序的完整实例代码:(这里以换 2张背景图片为例)
package com.czm.myskindemo; import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button; import java.util.List; public class MainActivity extends Activity { private Button mButton;
private Context mSkinContext;
private int[] mResId;
private int mCount = 0;
private View mTopbar;
private View mBottomBar;
private List<View> mSkinWidgetList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initSkinContext();
setListener();
}
private void initSkinContext() {
mResId = new int[]{
R.drawable.bg_topbar0,
R.drawable.bg_topbar1,
R.drawable.bg_topbar2,
};
try {
mSkinContext= this.getApplicationContext().createPackageContext("com.czm.myskin",
Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
mTopbar = findViewById(R.id.tv_topbar);
mBottomBar = findViewById(R.id.tv_bottombar);
} private void setListener() {
mButton = (Button)findViewById(R.id.btn_install_skin);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Drawable drawable = mSkinContext.getResources().getDrawable(mResId[mCount]);
mTopbar.setBackground(drawable);
mBottomBar.setBackground(drawable);
mCount++;
if(mCount >2){
mCount = 0;
}
}
});
}
}
其对于的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.czm.myskindemo.MainActivity"
tools:showIn="@layout/activity_main"> <TextView
android:id="@+id/tv_topbar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:background="#000"
android:gravity="center"
android:textColor="#FFF"
android:text="Top Bar" />
<TextView
android:id="@+id/tv_bottombar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:textColor="#FFF"
android:gravity="center"
android:background="#000"
android:text="Bottom Bar" />
<Button
android:id="@+id/btn_install_skin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Install Skin"/>
</RelativeLayout>
四、源码下载:
源码下载:http://www.demodashi.com/demo/14679.html
真题园网:http://www.zhentiyuan.com
Android 换肤功能的实现(Apk插件方式)的更多相关文章
- Android QMUI实战:实现APP换肤功能,并自动适配手机深色模式
Android换肤功能已不是什么新鲜事了,市面上有很多第三方的换肤库和实现方案. 之所以选择腾讯的QMUI库来演示APP的换肤功能,主要原因: 1.换肤功能的实现过程较简单.容易理解: 2.能轻松适配 ...
- Android实现apk插件方式换肤
换肤思路: 1.什么时候换肤? xml加载前换肤,如果xml加载后换肤,用户将会看见换肤之前的色彩,用户体验不好. 2.皮肤是什么? 皮肤就是apk,是一个资源包,包含了颜色.图片等. 3.什么样的控 ...
- Flex AIR应用换肤功能(Android和IOS)
说明 换肤功能,即将整个应用的皮肤都进行更换,其实质,是动态加载swf文件的过程,而这些swf文件则有css文件编译而来. 关于换肤功能,在android和ios系统的实现方式是不同的.主要原因,是因 ...
- 一种简单的实现:Android一键换肤功能
现在的APP开发,通常会提供APP的换肤功能,网上流传的换肤代码和实现手段过于复杂,我把原作者的代码重新整理抽取出来,转换成Eclipse项目,重新整理成正确.可直接运行的项目. 代码运行结果如图. ...
- Android一键换肤功能:一种简单的实现
Android一键换肤功能:一种简单的实现 现在的APP开发,通常会提供APP的换肤功能,网上流传的换肤代码和实现手段过于复杂,这里有一个开源实现,我找了一大堆,发现这个项目相对较为简洁:htt ...
- .NET vs2010中使用IrisSkin2.dll轻松实现winForm窗体换肤功能
IrisSkin2.dll是一款很不错的免费皮肤控件,利用它可以轻松的实现winForm窗体换肤! 网上很多朋友说在VS2010中不能使用IrisSkin2.dll,我这里提供一个取巧的办法. Iri ...
- 利用CSS预处理技术实现项目换肤功能(less css + asp.net mvc4.0 bundle)
一.背景 在越来越重视用户体验的今天,换肤功能也慢慢被重视起来.一个web系统用户可以选择一个自己喜欢的系统主题,在用户眼里还是会多少加点分的.我们很开心的是easyui v1.3.4有自带defau ...
- 【转】Javascript+css 实现网页换肤功能
来源:http://www.php100.com/html/webkaifa/DIV_CSS/2008/1014/2326.html Html代码部分: 1.要有一个带id的样式表链接,我们要通过操作 ...
- 用js来实现页面的换肤功能(带cookie记忆)
用js来实现页面的换肤功能 js实现换肤功能的实现主要是通过利用js控制CSS来实现的.大致的实现原理是这样的, 1.先定义一个页面基本样式style.css来确定div的宽高等属性,使得整个页面的D ...
随机推荐
- android studio 使用的一些注意,一些报错的解决方法(原创)
NDK 编译无法通过 注意看 build.gradle 里面的 有些是 ndk-build windows 上用 ndk-build.cmd Summary: gradle calls ndk-bui ...
- Java面试题:n=2\n1*2*5*6\n--3*4\n\nn=3\n1*2*3*10*11*12\n--4*5*8*9\n----6*7\n如何实现如上结构的数据
今天学长在面试的时候遇到了一道题,然后让大家做一做. 在不看下面的答案之前,悠闲的朋友们一起来抖动一下大脑吧! 以下是我的想法: import java.util.Scanner;public cla ...
- 使用asp.net MVC4中的Bundle遇到的问题及解决办法
背景 之前有过使用MVC3的经验,也建过MVC4的基本样例看过,知道有bundle这么一个方法. 近日想建个网站使用MVC4,但是我觉得在基本样例上改不好,有太多无用的东西,所以就建了一个空白的MVC ...
- python 网络编程(三)---TCP 服务器端客户端实现
客户端 客户端主要有4个步骤: 1)创建一个socket以连接服务器. socket = socket.socket(family, type),family参数代表地址家族,可为AF_INET(包括 ...
- unix 文件属性
在unix下提到文件属性,不得不提的一个结构就是stat,stat结构一般定义如下: struct stat { dev_t st_dev; /* ID of device containing fi ...
- 【OpenGL】画立方体
编写一个程序,该程序运行时可以用鼠标的一个按键调整立方体的方向,用另一个按键平移立方体,用第三个按键缩放立方体. 这是题目,我的程序不一定完全按照这个来.初学OpenGL,对那一堆坐标系表示十分混乱, ...
- android AudioRecord 与 AudioTrack的录音加回放的使用
http://stackoverflow.com/questions/32682952/audiotrack-audiotack-not-playing-fully-recorded-audio
- 细谈Linux和windows差异之图形化用户接口、命令行接口
相信来看本博文的朋友,肯定是已经玩过linux好段时间了,才能深刻理解我此番话语. 这是在Windows下的命令行接口 这是windows下的用户接口 就是它,explorer.ext,可以去尝试.把 ...
- 电脑突然死机,系统日志记录事件ID=6008
刚才正在写代码,在一次保存之后,正要刷新看下效果,电脑突然关机,没有任何提示或延迟.我的笔记本电池是一直插上的,也连接着电源. 重新开机之后,找到系统日志查看.只有这一条错误记录:非正常关机,事件60 ...
- C++学习笔记(八):函数重载、函数指针和函数对象
函数重载 函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数.重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于 ...