Android插件化开发之动态加载本地皮肤包进行换肤

前言: 本文主要讲解如何用开源换肤框架 android-skin-loader-lib来实现加载本地皮肤包文件进行换肤,具体可自行参考框架原理进行更改!

实现:
1. https://github.com/fengjundev/Android-Skin-Loader 框架地址,下载文件,根据自己需要进行删减得到自己的文件. 我的文件主要有android-skin-loader-lib依赖包文件,android-skin-loader-skin自己的皮肤包文件,其它则可以不要

2.创建项目,导入依赖包和皮肤包文件
注意点 :
a. 依赖包,皮肤包的build.gradle里的版本相关需要和自己app的build.gradle里的一致!
b.依赖包,皮肤包的app name要和自己app的app name一直!
**c.**app里要替换的颜色,图片,在皮肤包目录也要有,且id要一直,才可以找到,并更换!
d.要换肤的界面需继承换肤依赖包里的base里的相关界面元素,当前有activity,fragment,fragmentActivity,在布局文件需添加相关标识,如下:

...
xmlns:skin="http://schemas.android.com/android/skin"
...<TextView...skin:enable="true" ... />

e. 需要在自己App的Application文件中设置初始化:

        SkinManager.getInstance().init(this);SkinManager.getInstance().load();

f.自定义view换肤需要实现IDynamicNewView 接口:

public interface IDynamicNewView {void dynamicAddView(View view, List<DynamicAttr> pDAttrs);
}

3.编译皮肤包文件
因为我们的皮肤包文件是android-skin-loader-skin文件,它就是一个没有Java文件,只有资源文件的项目,所以我们要生成皮肤包文件也是由它生成的,就是一个apk啦,但是我们需要对它修改后缀名,防止用户点击.
当我们制定了皮肤包的相关文件时,修改皮肤包文件里的build.gradle,把编译生成的Apk文件进行重命名,并放到主项目的assets目录下.build.gradle目录如下:

apply plugin: 'com.android.application'def skinName = "BlackFantacy.skin"android {signingConfigs {config {keyAlias 'fengjun'keyPassword 'fengjun'storeFile file('keystore.key')storePassword 'fengjun'}}compileSdkVersion 23buildToolsVersion '25.0.0'defaultConfig {applicationId "com.example.android_skin_laoder_skin"minSdkVersion 15targetSdkVersion 23versionCode 1versionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}
}dependencies {compile fileTree(include: ['*.jar'], dir: 'libs')testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:23.1.1'
}final def TARGET_SKIN_DIR = '../app/src/main/assets/'gradle.projectsEvaluated {assembleRelease.doLast {println("=====================assembleRelease.doLast.begin.=========================")def dir = new File(TARGET_SKIN_DIR)if (!dir.exists()) {dir.mkdirs()}def f = new File(TARGET_SKIN_DIR + skinName)if (f.exists()) {f.delete()}copy {from('build/outputs/apk')into(TARGET_SKIN_DIR)include '*.apk'exclude '**/*-unaligned.apk'rename ('android-skin-loader-skin-release.apk', skinName)}println("=====================assembleRelease.doLast success.=========================")}assembleDebug.doLast {println("=====================assembleDebug.doLast.begin.=========================")def dir = new File(TARGET_SKIN_DIR)if (!dir.exists()) {dir.mkdirs()}def f = new File(TARGET_SKIN_DIR + skinName)if (f.exists()) {f.delete()}copy {from('build/outputs/apk')into(TARGET_SKIN_DIR)include '*.apk'exclude '**/*-unaligned.apk'rename ('android-skin-loader-skin-debug.apk', skinName)}println("=====================assembleDebug.doLast success.=========================")}
}

然后进行编译:

当编译完成时这时我们的主项目assets目录下会出现一个BlackFantacy.skin的文件,这就是我们要换肤的文件:

4.实现换肤
创建Application文件:

package com.example.administrator.replaceappskin;import android.app.Application;import cn.feng.skin.manager.loader.SkinManager;/*** Created by Administrator on 2017/5/19.*/public class ReplaceAppSkinApplication extends Application {public void onCreate() {super.onCreate();initSkinLoader();}/*** Must call init first*/private void initSkinLoader() {SkinManager.getInstance().init(this);SkinManager.getInstance().load();}
}

在注册文件添加指定的application文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.administrator.replaceappskin"><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /><application
        android:name=".ReplaceAppSkinApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

在要实现的activity或者fragment,fragmentActivity继承换肤的基类,我这里是把assets里的换肤文件写入sd卡中,生成自己的目录,方便以后置换,activity代码如下:

package com.example.administrator.replaceappskin;import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import cn.feng.skin.manager.base.BaseActivity;
import cn.feng.skin.manager.listener.ILoaderListener;
import cn.feng.skin.manager.loader.SkinManager;
import cn.feng.skin.manager.util.L;public class MainActivity extends BaseActivity {private static final String DATAPATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "我的主题包";/*** 在DATAPATH中新建这个目录,TessBaseAPI初始化要求必须有这个目录。*/private static final String tessdata = DATAPATH + File.separator + "主题";/*** TessBaseAPI初始化测第二个参数,就是识别库的名字不要后缀名。*/private static final String DEFAULT_LANGUAGE = "BlackFantacy";/*** assets中的文件名*/private static final String DEFAULT_LANGUAGE_NAME = DEFAULT_LANGUAGE + ".skin";/*** 保存到SD卡中的完整文件名*/private static final String LANGUAGE_PATH = tessdata + File.separator + DEFAULT_LANGUAGE_NAME;private TextView titleText;private Button setOfficalSkinBtn;private Button setNightSkinBtn;private boolean isOfficalSelected = true;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initSkinData();initView();}private void initSkinData() {//如果存在就删掉File f = new File(LANGUAGE_PATH);if (f.exists()) {f.delete();}if (!f.exists()) {File p = new File(f.getParent());if (!p.exists()) {p.mkdirs();}try {f.createNewFile();} catch (IOException e) {e.printStackTrace();}}InputStream is = null;OutputStream os = null;try {is = this.getAssets().open(DEFAULT_LANGUAGE_NAME);File file = new File(LANGUAGE_PATH);os = new FileOutputStream(file);byte[] bytes = new byte[2048];int len = 0;while ((len = is.read(bytes)) != -1) {os.write(bytes, 0, len);}os.flush();} catch (IOException e) {e.printStackTrace();} finally {try {if (is != null)is.close();if (os != null)os.close();} catch (IOException e) {e.printStackTrace();}}}private void initView() {titleText = (TextView) findViewById(R.id.title_text);titleText.setText("设置皮肤");setOfficalSkinBtn = (Button) findViewById(R.id.set_default_skin);setNightSkinBtn = (Button) findViewById(R.id.set_night_skin);isOfficalSelected = !SkinManager.getInstance().isExternalSkin();if (isOfficalSelected) {setOfficalSkinBtn.setText("官方默认(当前)");setNightSkinBtn.setText("黑色幻想");} else {setNightSkinBtn.setText("黑色幻想(当前)");setOfficalSkinBtn.setText("官方默认");}setNightSkinBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {onSkinSetClick();}});setOfficalSkinBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {onSkinResetClick();}});}protected void onSkinResetClick() {if (!isOfficalSelected) {SkinManager.getInstance().restoreDefaultTheme();Toast.makeText(getApplicationContext(), "切换成功", Toast.LENGTH_SHORT).show();setOfficalSkinBtn.setText("官方默认(当前)");setNightSkinBtn.setText("黑色幻想");isOfficalSelected = true;}}private void onSkinSetClick() {if (!isOfficalSelected) return;File skin = new File(LANGUAGE_PATH);if (skin == null || !skin.exists()) {Toast.makeText(getApplicationContext(), "请检查" + LANGUAGE_PATH + "是否存在", Toast.LENGTH_SHORT).show();return;}SkinManager.getInstance().load(skin.getAbsolutePath(),new ILoaderListener() {@Overridepublic void onStart() {L.e("startloadSkin");}@Overridepublic void onSuccess() {L.e("loadSkinSuccess");Toast.makeText(getApplicationContext(), "切换成功", Toast.LENGTH_SHORT).show();setNightSkinBtn.setText("黑色幻想(当前)");setOfficalSkinBtn.setText("官方默认");isOfficalSelected = false;}@Overridepublic void onFailed() {L.e("loadSkinFail");Toast.makeText(getApplicationContext(), "切换失败", Toast.LENGTH_SHORT).show();}});}
}

相关的方法和原理可以去看看它的方式,这里不细讲!

效果如下:

Demo下载地址

Android插件化开发之动态加载本地皮肤包进行换肤相关推荐

  1. android 动态换肤框架,GitHub - ss520k/Android-Skin-Loader: 一个通过动态加载本地皮肤包进行换肤的皮肤框架...

    Android-Skin-Loader 更新日志 导入到Android Studio,使用gradle构建皮肤包(见7. 皮肤包是什么?如何生成?)(2015-12-02) 解决Fragment换肤在 ...

  2. Android插件化开发之动态加载三个关键问题详解

    本文摘选自任玉刚著<Android开发艺术探索>,介绍了Android插件化技术的原理和三个关键问题,并给出了作者自己发起的开源插件化框架. 动态加载技术(也叫插件化技术)在技术驱动型的公 ...

  3. Android插件化开发之动态加载技术简单易懂的介绍方式

    转载地方:https://segmentfault.com/a/1190000004062866 基本信息 Author:kaedea GitHub:android-dynamical-loading ...

  4. Android插件化开发之动态加载的类型

    https://segmentfault.com/a/1190000005113493 基本信息 Author:kaedea GitHub:android-dynamical-loading 现在网络 ...

  5. Android插件化开发之动态加载技术系列索引

    动态加载介绍 在Android开发中采用动态加载技术,可以达到不安装新的APK就升级APP功能的目的,可以用来到达快速发版的目的,也可以用来修复一些紧急BUG. 现在使用得比较广泛的动态加载技术的核心 ...

  6. Android插件化开发之动态加载基础之ClassLoader工作机制

    类加载器ClassLoader 早期使用过Eclipse等Java编写的软件的同学可能比较熟悉,Eclipse可以加载许多第三方的插件(或者叫扩展),这就是动态加载.这些插件大多是一些Jar包,而使用 ...

  7. Android 插件化开发——宿主APP加载APK插件

    本篇博客说一下我们的宿主APP怎样加载别的APK文件. 首先需要说一些知识点,我们的Java文件要想在Android环境运行,需要将.java文件通过转为class文件,然后为了能在DVM上面运行,再 ...

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

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

  9. Android插件化开发指南——Hook技术(一)【长文】

    文章目录 1. 前言 2. 将外部dex加载到宿主app的dexElements中 3. 插件中四大组件的调用思路 4. Hook 2.1 对startActivity进行Hook 2.1.1 AMS ...

最新文章

  1. Python Qt GUI设计:QScrollBar类实现窗口水平或垂直滑动条效果(拓展篇—4)
  2. getURLParameters - 网址参数
  3. 一本跳进挨踢生活圈的日记(南京站)
  4. NumPy-快速处理数据
  5. 上传文件到服务器端后进一步推送到sftp服务器
  6. python 3.5 import theano ::hypot error
  7. easyui datagrid中添加右键菜单事件
  8. bzoj 4318 OSU!
  9. nfc(近距离无线通讯技术)
  10. 基本的广告法违禁词、违规词以及敏感词大全
  11. JAVA入门教程-专题视频课程
  12. 美团外卖广告投放系统的设计
  13. 微信小程序上传照片,限制格式,限制大小,公用方法
  14. python爬取b站弹幕分析_Python爬取分析B站动漫《柯南》弹幕,从数据中分析接下来的剧情...
  15. 静止、极轨卫星遥感图像太阳及卫星天顶、方位角的计算
  16. Lua中获取第二天凌晨的剩余时间
  17. pyinstaller打包有pandas和numpy库过程中遇到的bug及处理
  18. Golang 加密方法
  19. 信号处理--常用术语
  20. application实现一个简单的网页计数器

热门文章

  1. nyoj 171 聪明的kk【Java】
  2. 秋招干货|应届生毕业生求职简历模板
  3. “意见反馈”DEMO
  4. 【聊天机器人】您必须了解的最佳聊天机器人框架
  5. 行业词库收集程序已经开源
  6. 新倩女幽魂服务器维修,《新倩女幽魂》2018年3月份新区 四组新春服务器开启
  7. Error: Request failed with status code 403
  8. html图片环绕文字,CSS_如何让文字环绕图片显示?
  9. 重置服务器系统要多久,云服务器需要多久重启一下
  10. x86服务器(HP/DELL/IBM)测试分析(上)