Android 换肤
1). 换肤思路
在源应用APP中,下载皮肤包,使得对应的文件资源得以应用。使用DexClassLoader加载资源包,使用反射来获取资源ID.
2). 创建SkinChange工程
app是应用Module,spinone和spintwo是皮肤插件工程
3). app Module
- BaseActivity
/** * Activity基类 * Created by mazaiting on 2018/6/27. */ public abstract class BaseActivity extends AppCompatActivity { /**资源管理器*/ protected AssetManager mAssetManager; /**资源*/ protected Resources mResources; /**主题*/ protected Theme mTheme; /** * 加载资源 * @param dexPath dex路径 */ protected void loadResources(String dexPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath",String.class); addAssetPath.invoke(assetManager, dexPath); mAssetManager = assetManager; } catch (Exception e) { e.printStackTrace(); } Resources superRes = super.getResources(); superRes.getDisplayMetrics(); superRes.getConfiguration(); mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); mTheme = mResources.newTheme(); mTheme.setTo(super.getTheme()); } @Override public AssetManager getAssets() { return mAssetManager == null ? super.getAssets() : mAssetManager; } @Override public Resources getResources() { return mResources == null ? super.getResources() : mResources; } @Override public Theme getTheme() { return mTheme == null ? super.getTheme() : mTheme; } }
- MainActivity
public class MainActivity extends BaseActivity { private static final String TAG = "MainActivity"; private TextView mTextView; private ImageView mImageView; private ClassLoader mClassLoader; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.tv_show); mImageView = findViewById(R.id.iv_show); } /** * 切换主题1 */ public void changeThemeOne(View view) { // 获取缓存路径 String fileDir = getCacheDir().getAbsolutePath(); // 获取文件路径 String filePath = fileDir + File.separator + "spinone-release.apk"; mClassLoader = new DexClassLoader(filePath, fileDir, null, getClassLoader()); loadResources(filePath); setContentOne(); } /** * 切换主题2 */ public void changeThemeTwo(View view) { // 获取缓存路径 String fileDir = getCacheDir().getAbsolutePath(); // 获取文件路径 String filePath = fileDir + File.separator + "spintwo-release.apk"; mClassLoader = new DexClassLoader(filePath, fileDir, null, getClassLoader()); loadResources(filePath); setContent(); } /** * 设置主题内容 */ private void setContent() { try { Class clazz = mClassLoader.loadClass("com.mazaiting.UiUtil"); // 设置TextView内容 Method method = clazz.getMethod("getTextString", Context.class); String string = (String) method.invoke(null, this); mTextView.setText(string); // 设置ImageView背景 method = clazz.getMethod("getImageDrawable",Context.class); Drawable drawable = (Drawable) method.invoke(null,this); mImageView.setBackground(drawable); } catch (Exception e) { e.printStackTrace(); } } /** * 设置主题1 */ private void setContentOne() { int stringId = getTextStringId(); int drawableId = getImgDrawableId(); Log.d(TAG, "stringId: " + stringId + ", drawableId: " + drawableId); } /** * 获取图片ID * @return */ private int getImgDrawableId() { try { // "com.mazaiting.spinone.R$color" -- spinone module 下的R.color.img Class clazz = mClassLoader.loadClass("com.mazaiting.spinone.R$color"); Field field = clazz.getField("img"); int resId = (int) field.get(null); return resId; } catch (Exception e) { e.printStackTrace(); } return 0; } /** * 获取字符串ID * @return */ private int getTextStringId() { try { // "com.mazaiting.spinone.R$string" -- spinone module下的R.string.text Class clazz = mClassLoader.loadClass("com.mazaiting.spinone.R$string"); Field field = clazz.getField("text"); int resId = (int) field.get(null); return resId; } catch (Exception e) { e.printStackTrace(); } return 0; } }
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.mazaiting.skinchange.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:onClick="changeThemeOne" android:text="主题1" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:onClick="changeThemeTwo" android:text="主题2" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout android:paddingTop="50dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tv_show" android:text="@string/text" android:layout_width="match_parent" android:layout_height="wrap_content"/> <ImageView android:id="@+id/iv_show" android:background="@color/img" android:layout_width="200dp" android:layout_height="200dp"/> </LinearLayout> </android.support.constraint.ConstraintLayout>
-
资源文件
4). 插件Module
spinone和spintwo中同样使用一个类,包的全路径名与MainActivity中setContent方法中的Class clazz = mClassLoader.loadClass("com.mazaiting.UiUtil");
中com.mazaiting.UiUtil
相同。并在插件Module中使用相同的资源文件名进行替换。
package com.mazaiting; /** * Created by mazaiting on 2018/6/27. */ public class UiUtil { /** * 获取字符串 */ public static String getTextString(Context ctx) { return ctx.getResources().getString(R.string.text); } /** * 获取图片 */ public static Drawable getImageDrawable(Context ctx) { return ctx.getResources().getDrawable(R.color.img); } public static int getTextStringId(){ return R.string.text; } public static int getImageDrawableId(){ return R.color.img; } }
5). 运行
安装app Module 至手机,spinone和spintwo Module打包为apk文件,使用命令安装至手机,此处必须将spinone-release.apk文件push到/data/data/<包名>/cache/
目录下
adb push C:\Users\mazaiting\Desktop\release\s pinone-release.apk /data/data/com.mazaiting.skinchange/cache
示例效果:
6). 残留的问题
- 每个插件包都包含一个UiUtil文件,造成代码冗余(主题2的加载)
- 主工程无法获取插件包的应用包名,从而实现动态加载。(主题1的加载)(能想到的解决办法:插件的文件名即包名)
代码下载
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android Glide加载四周圆角图
Android Glide加载四周圆角图 需要引入: implementation 'jp.wasabeef:glide-transformations:3.3.0' 上层Java代码: package zhangphil.test; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.widget.ImageView; import com.bumptech.glide.Glide; import jp.wasabeef.glide.transformations.RoundedCornersTransformation; import static com.bumptech.glide.request.RequestOptions.bitmapTransform; public class GlideActivity extends AppCompatActivity ...
- 下一篇
Android菜鸟学习js笔记 一
一、var,const,let区别? var 变量,const 常量,let块级变量。
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19