Android App 换肤实现方式

2023-05-16

Android App 换肤的引入意味着给用户提供不同的界面样式,以适应不同用户的审美需求。引入换肤可以让用户更加个性化地使用 App,增强用户对 App 的黏度和使用体验。

Android App 换肤可以满足以下几种场景:

  1. 多主题应用:为了满足用户多样化的审美需求,在应用中提供多种主题切换。

  2. 异形屏适配:同一应用在横竖屏或者不同分辨率设备下可能存在 UI 布局不协调,此时可以通过给不同屏幕类型设备采用特定风格皮肤的方式解决这种问题。

  3. 特殊节日:如圣诞节、情人节等,改变应用的皮肤与窗口布局,为用户提供高度个性化的游戏体验。

  4. 如有必要还能改变App 内部功能菜单、颜色风格等各种元素。

总之,Android App 换肤可以有效提升用户的点赞数、留存率、用户满意度及用户粘性,因此在 App 的开发和设计过程中它是很重要的。

Android App 换肤实现方式有以下几种:

1. 利用主题(Theme)切换

这是一种简单的方式,通过设置不同的主题来达到换肤的效果。您可以在 res/values 目录下新建一个 themes.xml 文件,定义多个主题,并在应用中动态地设置某个主题来改变应用的样式。

示例代码如下:

  • 在 themes.xml 文件中定义两个主题:MyAppTheme1MyAppTheme2
<!-- styles.xml -->
<style name="MyAppTheme1" parent="Theme.AppCompat">
    <item name="colorPrimary">@color/red</item>
    <item name="colorAccent">@color/blue</item>
</style>

<style name="MyAppTheme2" parent="Theme.AppCompat">
    <item name="colorPrimary">@color/yellow</item>
    <item name="colorAccent">@color/green</item>
</style>
  • 在代码中设置主题
// Java
setTheme(R.style.MyAppTheme1);
recreate();

// Kotlin
setTheme(R.style.MyAppTheme1)
recreate()

2. 使用插件化技术

使用插件化技术实现 Android 应用换肤的基本流程如下:

  1. 创建一个独立的 Module,作为换肤插件,其中包含所有换肤需要用到的资源文件(如颜色值、背景图等)。

  2. 在主工程中引入插件化框架(如 ClassLoader、反射等)。

  3. 加载插件资源,使用反射机制从插件 APK 中加载资源并存储在内存中。

  4. 在需要换肤的地方,替换掉原来的资源字段,使其指向新的插件资源。

下面是一个简单的代码示例:

  1. 在插件 Module 中创建一个新的 res/values/colors.xml 文件,定义一个和主题不同的颜色值:
<resources>
    <color name="text_color">#FFFFFFFF</color>
</resources>
  1. 在需要换肤的 Activity 中,定义一个变量来存储插件资源的 Class 对象和资源 ID:
private Class<?> pluginResClass;
private int textColorId;
  1. 加载插件资源,这个过程通常在 Application 的 onCreate 方法中完成:
try {
    // 加载插件 APK 文件
    File pluginApk = new File(getExternalFilesDir(null), "plugin.apk");

    // 创建插件 APK 的 DexClassLoader
    DexClassLoader dexClassLoader = new DexClassLoader(pluginApk.getAbsolutePath(),
        getDir("dex", 0).getAbsolutePath(),
        null,
        getClassLoader());

    // 使用 DexClassLoader 加载资源
    pluginResClass = dexClassLoader.loadClass("com.example.plugin.R$color");

    // 获取插件资源 ID
    textColorId = (Integer) pluginResClass.getDeclaredField("text_color").get(null);

} catch (Exception e) {
    e.printStackTrace();
}
  1. 在需要换肤的地方,替换掉原来的资源字段,使其指向新的插件资源。比如,在 onCreate 方法中修改 TextView 的 text color:
try {
    // 通过反射修改资源
    Field field = R.class.getDeclaredField("color");
    field.setAccessible(true);
    field.set(null, pluginResClass.newInstance());
    
    textView.setTextColor(getResources().getColor(textColorId));

} catch (Exception e) {
    e.printStackTrace();
}

需要注意的是,该代码示例只是实现了一种资源的换肤,实际应用中可能还需要处理更多的资源类型。而且为了实现插件化换肤,需要对 Android 的资源加载机制以及插件化框架有一定的了解和掌握。因此,相比于其他方案,使用插件化换肤的难度更高。

3. 使用自定义 View 来实现

利用自定义 View 来实现换肤功能,例如根据外部传入的参数来动态调整绘制的颜色、背景等,从而实现换肤的效果。

示例代码如下:

  • 定义自定义 View
class MyView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {

    // 定义当前应用的主题
    private var currentTheme = 0

    init {
        // 初始化时获取当前应用的主题
        val ta = context.obtainStyledAttributes(attrs, R.styleable.MyView)
        currentTheme = ta.getInt(R.styleable.MyView_theme, 0)
        ta.recycle()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        when (currentTheme) {
            0 -> canvas?.drawColor(ContextCompat.getColor(context, R.color.red))
            1 -> canvas?.drawColor(ContextCompat.getColor(context, R.color.yellow))
            2 -> canvas?.drawColor(ContextCompat.getColor(context, R.color.green))
        }
    }

    fun setTheme(theme: Int) {
        currentTheme = theme
        invalidate() // 刷新 View
    }
}
  • 在布局文件中使用自定义 View
<com.example.myapp.MyView
    android:id="@+id/my_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:theme="0" />
  • 在代码中调用 setTheme() 方法切换主题
val myView = findViewById<MyView>(R.id.my_view)
myView.setTheme(1)

4.使用第三方库

还可以使用一些比较成熟的开源库,如 Android-Skin-Loader、iTheme等。这些库提供了一套简单易用的 API 接口,开发者只需将资源文件按照特定的命名规则准备好,然后在代码中设置换肤的路径即可。

以 Android-Skin-Loader 为例,示例代码如下:

  • 在 build.gradle 文件中添加依赖
dependencies {
    implementation 'com.github.ximsfei:Android-skin-loader:+'
}
  • 在 BaseActivity 中设置换肤功能
abstract class BaseActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初始化换肤
        SkinCompatManager.withoutActivity(this) 
            .addInflater(SkinMaterialViewInflater()) 
            .loadSkin()
    }
}
  • 在 activity_main.xml 布局文件中增加 namespace
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:skin="http://schemas.android.com/android/skin"
    ...>
  • 指定要替换的属性
<Button
    android:id="@+id/btn_test"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="@color/commonTextColor"
    skin:enable="true"                         // 开启换肤
    skin:attr_background="@color/colorBg">     // 定义要替换的 drawable 的名字及路径
</Button>
  • 实现换肤功能
val path = "需要更新样式的皮肤包路径"
SkinCompatManager.getInstance().loadSkin(path, object : SkinLoaderListenerAdapter() {
    override fun onSuccess() {
        // 应用成功
    }

    override fun onError(error: Int) {
        // 应用失败
    }
})
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android App 换肤实现方式 的相关文章

  • Android 覆盖在软件按钮之上

    我正在尝试编写一个绘制自定义鼠标指针的应用程序 我目前有一个服务 它创建一个扩展 ViewGroup 的类 并使用 WindowManager 系统服务将其显示为带有 FLAG LAYOUT IN SCREEN 设置的 TYPE SYSTE
  • Visual Studio 不允许我在 Android 物理设备上进行调试

    我正在使用 Xamarin 和 Visual Studio 2013 开发 Android 应用程序 我已将 Android 小程序连接到计算机 Sansung Kies 识别了该设备 Adb 也能识别它 因为当我输入 adb device
  • 通过数据绑定将字符串传递到包含的布局不起作用

    我正在尝试使用 Android 数据绑定功能将一个简单的字符串从我的主布局传递到布局 它编译得很好 但传递给包含的值实际上并未传递 即 它没有出现在我的布局中
  • Android KeyBoard.Key 禁用图标 预览特殊键?

    我通过实现 KeyboardView OnKeyboardActionListener 接口来自定义自己的软键盘 按下按键时 将显示预览弹出窗口 我的问题是如何禁用 SHIFT 和 DELETE 等特殊键的预览弹出窗口 我尝试将 andro
  • Android中如何使用洪水填充算法?

    我是Android编程新手 最近尝试编写一个简单的应用程序 仅供练习 在这个中 我想在用户点击时为图像着色 但我不知道如何开始 我读过不同的主题 其中提到使用 洪水填充 算法 我在网上找到了它 但我不知道如何将它放入我的简单应用程序中 我找
  • Android Lollipop prepareAsync() 需要很长时间才能返回

    在 Samsung Galaxy Note 4 上的 Android Lollipop 几周前刚刚从 4 4 4 更新 上 prepareAsync 几乎需要 20 秒来加载实时流 在 4 4 4 上 只需要 2 3 秒 并且没有错误 见下
  • 如何使用闹钟音量和 setAudioAttributes 来播放铃声?

    所以我正在尝试了解音频属性 这是我到目前为止所拥有的 alarm getSound will return a proper URI to pick a ringtone Ringtone tone RingtoneManager getR
  • Android-工具栏中的SearchView

    我只想在我的应用程序中添加 searchview 但我不想搜索任何东西 只是我想要用户输入的查询 到目前为止 我尝试了这段代码 但是当我运行我的应用程序时它崩溃了 Update 我尝试了这个 但即使我的应用程序崩溃了 main menu x
  • 获取可以共享数据的应用程序列表

    此代码显示默认共享对话框 Intent sharingIntent new Intent Intent ACTION SEND sharingIntent setType text html sharingIntent putExtra a
  • 没有调用addToBackStack,片段仍然添加到backstack,为什么?

    我正在制作我的片段更换器助手类 但我遇到了一些问题 我称之为FragmentChanger 它有一个fragmentContainer 这是一个ViewGroup 其中包含我想展示的所有片段 我已经做了我自己的replace Fragmen
  • Android ListView setSelection() 似乎不起作用

    我有一个ListActivity实现onListItemClick 并调用doSomething 类的功能 后者包含l setSelection position where l is the ListView object 现在有一个on
  • 如何从android获取应用程序安装时间

    我尝试了一些方法 但没有成功 请帮助我 PackageManager pm context getPackageManager ApplicationInfo appInfo pm getApplicationInfo app packag
  • Integer.parseInt("0x1F60A") 以 NumberformatException 结束

    我尝试从数据库中获取长字符串内的表情符号代码 格式如下 0x1F60A 所以我可以访问代码 但它将是String 起初 我尝试通过执行以下操作来转换变量tv setText beforeEmo getEmijoByUnicode int e
  • Android模拟器分配内存失败8

    当我尝试从 Eclipse 运行 WXGA800 模拟器时 出现如下错误 Failed to allocate memory 8 This application has requested the Runtime to terminate
  • 在 AppAuth-Android 中注销

    我有一个用JAVA开发的Android应用程序 对于这个应用程序 我使用的是身份服务器4 https github com IdentityServer IdentityServer4作为我的 STS 一切正常 但我找不到任何注销的实现Ap
  • 通过列表视图检查动态生成的复选框时遇到问题

    我知道其他成员已经提出了这个问题 一些成员也给出了解决方案 但问题是我没有找到任何适合我的应用程序的解决方案 我正在创建一个应用程序 其中我有一个屏幕 它将显示动态列表视图 其中包含列表项 复选框和三个文本视图 一个用于候选人姓名 另外两个
  • ACCESS_BACKGROUND_LOCATION 不适用于低于 Q (29) 的 Android 版本

    我的应用程序面向 Android API 28 根据文档 https developer android com preview privacy location target android 10 我应该要求ACCESS BACKGROU
  • “无法实例化活动”错误

    我的一个 Android 应用程序拥有大约 100 000 个用户 每周大约 10 次 我会通过 Google 的市场工具向我报告以下异常情况 java lang RuntimeException Unable to instantiate
  • 更改Android菜单的背景颜色[重复]

    这个问题在这里已经有答案了 我正在尝试将标准浅灰色更改为浅绿色 似乎没有一个简单的方法可以做到这一点 例如 通过 Android 主题 但我找到了一个解决方法 如本页所述 http tinyurl com 342dgn3 http tiny
  • Android ADT Eclipse 插件,parseSDKContent 失败

    我刚刚设置了我的第一个 Android 开发环境 其中包括 日食3 5 Mac OS X 10 5 适用于 x86 mac 的 Android SDK ADT Eclipse 插件 0 9 6 我已将 set PATH 设置为我的 SDK

随机推荐

  • 图片上传到FTP服务器之后,从ftp服务器下载图片到本地会失真

    问题描述 有一个需求是需要把下载的图片上传到ftp服务器 xff0c 但是当把图片上传完之后 xff0c 下载到本地发现图片失真了 xff0c 如图 xff1a 看了网上很多办法比如 xff1a ftpClient setFileType
  • hisat2 cifflinks

    hisat2 p 8 dta cufflinks x hisat2 data script sh 1 PH 1 1 0h2 1 1 fq gz 2 0h2 1 0h2 1 2 fq gz S 0h2 1 sam
  • centOS7上面安装wkhtmltopdf的记录

    Using username terry Last login Sun Feb 16 21 46 27 2020 from 192 168 31 134 terry 64 MiWiFi R4AC srv su 密码 xff1a root 6
  • 一键清理项目下pycharm和Jupyter缓存文件

    前言 在进行python编程开发时 xff0c Jupyter和Pycharm是两个比较常用的开发工具 pycharm在运行py文件时会生成以 pycache 命名的缓存文件夹 xff0c 而jupyter在打开ipynb时会生成以 ipy
  • Spring框架常考知识点总结

    1 Spring常见的注入方式有 xff1a setter注入和构造注入 2 依赖注入的目标是 xff1a 在代码之外管理程序组建间的依赖关系 3 下面关于IOC的理解 xff0c 正确的是 xff1a 控制反转和对象被动的接受依赖类 4
  • MATLAB GUI图形界面设计一个学生管理系统

    设计一个简单的学生成绩管理程序 xff0c 包含如下功能 xff1a 1 可创建不少于100名学生成员 xff1b 2 每名成员的记录包括 xff1a 学号 姓名 专业和5门课程的成绩 xff1b 3 能够实现添加 删除 修改学生成员 xf
  • Java多线程----消费者/生产者模型

    前言 生产者和消费者问题是线程模型中的经典问题 xff1a 生产者和消费者在同一时间段内共用同一个存储空间 xff0c 生产者往存储空间中添加产品 xff0c 消费者从存储空间中取走产品 xff0c 当存储空间为空时 xff0c 消费者阻塞
  • 操作系统之聊天系统的实现

    实验要求 在linux系统用c c 43 43 语言编写一个多用户的聊天室管理系统 主要功能 xff1a 1 能做到3个以上用户之间的聊天 xff1b 2 系统要有用户管理功能 xff1b 3 每个用户能管理自己的权限 xff0c 比如 不
  • Android系统连接WIFI显示网络连接受限

    问题描述 使用Android设备打开设置 xff0c 选择WIFI输入正确密码连接 xff0c 会显示已连接 xff0c 无网络 xff0c 然后变成网络连接受限 xff0c 实际可以使用此WIFI进行上网 问题分析 异常Log D Net
  • Android View绑定框架——ButterKnife & Viewbinding

    ButterKnife 是一个专注于Android系统的View注入框架 以前总是要写很多findViewById来找到View对象 xff0c 有了ButterKnife可以很轻松的省去这些步骤 最重要的一点 xff0c 使用Butter
  • Android 启动前台服务的实现方法与注意事项

    问题描述 Android R平台 收音机启动前台服务 startForegroundService 报错 span class token class name E span span class token class name Andr
  • Android 多窗口适配

    Android 多窗口适配主要是针对支持分屏 自由形态窗口 画中画等多个窗口模式的 Android 设备 以下是 Android 多窗口适配的一些常见技术点 xff1a 支持多窗口 xff1a 在 AndroidManifest xml 文
  • Python保存数据到文件的方法

    IO xff08 Input Output xff09 是指计算机与外部环境之间的数据交换 Python中的IO操作分为文件IO和网络IO两种 文件IO xff1a Python提供了内置的文件对象来实现文件IO操作 文件对象是通过内置的o
  • Windows编程之核心书籍推荐

    一 Windows程序设计 xff08 第5版 珍藏版 xff09 Windows程序设计 xff08 第5版 珍藏版 xff09 这是一本经典的Windows编程圣经 xff0c 曾经伴随着近50万Windows程序员步入编程殿堂 xff
  • Android DocumentFile基本使用

    调用DocumentUI申请权限 xff1a Android R实现访问外部存储的Android data方案 申请权限选择的目录树后 xff0c 后续所以文件操作都需要按照此目录树为基础做文件操作 Uri的构造 xff1a content
  • 最优化算法 - 动态规划算法

    动态规划算法简介 动态规划 xff08 Dynamic programming xff09 是一种在数学 管理科学 计算机科学 经济学和生物信息学中使用的 xff0c 通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法 动态规划常常
  • Android FFmpeg应用简析

    FFmpeg简介 FFmpeg是一个跨平台的自由软件 xff0c 可用于录制 转换和流式传输音频和视频 它包含了非常多的音频 视频编解码库 封装格式库以及工具库 它不仅支持各种常用的音视频格式 xff0c 而且支持一些非常罕见的格式 FFm
  • Android 机器学习模型的轻量级框架 TensorFlow Lite

    TensorFlow Lite 简介 TensorFlow Lite 是一款用于在移动设备 嵌入式设备和物联网设备上运行机器学习模型的轻量级框架 它是 TensorFlow 在移动领域的延伸 xff0c 旨在解决手机等设备上机器学习计算资源
  • 跨越屏幕:桌面PC端的多端开发框架介绍

    目前 xff0c 随着互联网和移动互联网的发展 xff0c 多端开发框架已经成为越来越多开发者更好的选择 主要有以下几个方面的前景 xff1a 跨平台开发需求不断增加 xff1a 由于不同平台和设备的差异性 xff0c 开发人员需要使用不同
  • Android App 换肤实现方式

    Android App 换肤的引入意味着给用户提供不同的界面样式 xff0c 以适应不同用户的审美需求 引入换肤可以让用户更加个性化地使用 App xff0c 增强用户对 App 的黏度和使用体验 Android App 换肤可以满足以下几