您现在的位置是:首页 > 文章详情

Android 换肤

日期:2018-06-26点击:366
1). 换肤思路

在源应用APP中,下载皮肤包,使得对应的文件资源得以应用。使用DexClassLoader加载资源包,使用反射来获取资源ID.

2). 创建SkinChange工程
img_aca2e16688fb509ee504f0cedb29130a.png
图1.png

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> 
  • 资源文件


    img_6969f2ef772a319b1f981a7477a2c700.png
    图2.png
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 

示例效果:


img_53b13ed3c2518f504b152f0928d174e0.gif
图4.gif
6). 残留的问题
  • 每个插件包都包含一个UiUtil文件,造成代码冗余(主题2的加载)
  • 主工程无法获取插件包的应用包名,从而实现动态加载。(主题1的加载)(能想到的解决办法:插件的文件名即包名)
代码下载
原文链接:https://yq.aliyun.com/articles/663290
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章