做了两年多Android应用开发了,期间也实现过好几种主题切换的方式。趁着最近有空,总结一下。
所谓的主题切换,就是能够根据不同的设定,呈现不同风格的界面给用户,也就是所谓的换肤。
1、将主题包(图片与配置)存到SD卡上(可通过下载或手动放入指定目录),在代码里强制从本地文件创建图片与配置文字大小、颜色等信息。
2、Android平台独有的主题设置功能,在values文件夹中定义若干种style,在Activity的onCreate中使用setTheme方法设置主题。
3、将主题包做成APK的形式,使用远程Context的方式访问主题包中的资源。
4、类似小米的深度主题,修改framework中Resources类获取资源的流程,将资源重定向到主题包中。
对于第一种,由于这种方法比较传统,不受限于编程语言,任何平台都可以实现。就不多介绍了。
而第四种,由于涉及的知识比较多,我会在后续专门写一篇文章来描述。
这里,我将主要通过两个小例子来说明第二与第三种方案的实现方式。
我将其分别命名为内置主题与APK主题。
1.内置主题
1.1定义属性
要想根据主题的不同,设置不同属性,我们至少需要定义下属性的名字吧。要不然系统怎么知道去哪找啊!
定义属性,是在values下进行的。
本例中,我在attrs.xml里定义了几种属性。
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="colorValue" format="color" /> <attr name="floatValue" format="float" /> <attr name="integerValue" format="integer" /> <attr name="booleanValue" format="boolean" /> <attr name="dimensionValue" format="dimension" /> <attr name="stringValue" format="string" /> <attr name="referenceValue" format="reference" /> </resources>
从上面的xml文件的内容可以看到,attr里可以定义各种属性类型,如color、float、integer、boolean、dimension(sp、dp/dip、px、pt...)、reference(指向本地资源)等等。
1.2定义主题
接着,我们需要在资源文件中定义若干套主题。并且在主题中设置各个属性的值。
本例中,我在styles.xml里定义了SwitchTheme1与SwitchTheme2。
<style name="SwitchTheme1" parent="@android:style/Theme.Black"> <item name="colorValue">#FF00FF00</item> <item name="floatValue">0.35</item> <item name="integerValue">33</item> <item name="booleanValue">true</item> <item name="dimensionValue">76dp</item> <!-- 如果string类型不是填的引用而是直接放一个字符串,在布局文件中使用正常,但代码里获取的就有问题 --> <item name="stringValue">@string/hello_world</item> <item name="referenceValue">@drawable/hand</item> </style> <style name="SwitchTheme2" parent="@android:style/Theme.Wallpaper"> <item name="colorValue">#FFFFFF00</item> <item name="floatValue">1.44</item> <item name="integerValue">55</item> <item name="booleanValue">false</item> <item name="dimensionValue">76px</item> <item name="stringValue">@string/action_settings</item> <item name="referenceValue">@drawable/ic_launcher</item> </style>
1.3在布局文件中使用
定义好了属性,我们接下来就要在布局文件中使用了。
为了使用主题中的属性来配置界面,我定义了一个名为activity_theme_switch.xml布局文件。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="@string/theme_text" /> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/textView1" android:text="@string/theme_color" /> <TextView android:id="@+id/themeColor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/themeText" android:layout_below="@+id/themeText" android:text="TextView" android:textColor="?attr/colorValue" /> <TextView android:id="@+id/textView5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignTop="@+id/theme_image" android:text="@string/theme_image" /> <TextView android:id="@+id/themeText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:text="?attr/stringValue" /> <ImageView android:id="@+id/theme_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/themeColor" android:layout_below="@+id/themeColor" android:src="?attr/referenceValue" /> </RelativeLayout>
从这个布局文件中可以看到,我在id为themeColor、themeText及theme_image的控件上,分别使用了?attr/colorValue、?attr/stringValue与?attr/referenceValue来引用主题中的颜色值、字符串以及图片。
1.4设置主题及布局文件
布局文件与主题都写好了,接下来我们就要在Activity的onCreate方法里使用了。
为了显示两种主题的效果,我写了一个DemoStyleThemeActivity。根据useThemeBlack来设置不同的主题。没打开一次DemoStyleThemeActivity,useThemeBlack就会取反。这样,只要打开两次,就能看到不同的效果了。代码如下:
import android.app.Activity; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Bundle; public class DemoStyleThemeActivity extends Activity { private static boolean useThemeBlack = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (useThemeBlack) setTheme(R.style.SwitchTheme1); else setTheme(R.style.SwitchTheme2); useThemeBlack = !useThemeBlack; setContentView(R.layout.activity_theme_switch); } }
当然,要想主题生效,有一点非常重要:“setTheme一定要在setContentView之前被调用”。要不然,界面都解析完了,再设置主题也不会触发重新创建界面。
更严重的是,要是默认主题里没那些属性,解析布局文件时候是会挂的啊!这点在配置多个不同style时要主题,属性可以多,但一定不能少。
以上代码的执行结果如下:
1.5在代码中获取主题资源
当然,有的人就要问了,如果我想在代码里获取主题中的资源该怎么办啊?毕竟整数、浮点数、布尔值这些东西在布局文件里没地方用啊。
很简单,要想在代码中获取当前设置的主题的属性值,我们只需要使用Context.obtainStyledAttributes就行了。
还记得1.1里定义的那些属性名吧,Android在编译R.java时为每一个属性都分配了一个标识。我们只需要将这些标识的数组传给obtainStyledAttributes就行了。
然后,我们就能获得一个TypedArray对象。通过它的getXXX方法就能获取到各种属性值了。
代码如下:
import android.app.Activity; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Bundle; public class DemoStyleThemeActivity extends Activity { private static boolean useThemeBlack = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (useThemeBlack) setTheme(R.style.SwitchTheme1); else setTheme(R.style.SwitchTheme2); useThemeBlack = !useThemeBlack; setContentView(R.layout.activity_theme_switch); TypedArray a = obtainStyledAttributes(new int[] { R.attr.colorValue, R.attr.floatValue, R.attr.integerValue, R.attr.booleanValue, R.attr.dimensionValue, R.attr.stringValue, R.attr.referenceValue }); System.out.println("colorValue="+a.getColor(0, Color.BLACK)); System.out.println("floatValue="+a.getFloat(1, 99.99f)); System.out.println("integerValue="+a.getInt(2, Integer.MAX_VALUE)); System.out.println("booleanValue="+a.getBoolean(3, false)); System.out.println("dimensionValue="+a.getDimensionPixelSize(4, 66)); System.out.println("stringValue="+a.getString(5)); // Drawable打印了也没什么用,就不输出了 //System.out.println("referenceValue="+a.getDrawable(6)); } }
命令行输出结果如下:
2.APK主题
内置主题虽然可以很方便地修改界面,并且不需要怎么改代码。但它有一个比较致命的缺陷,就是一旦程序发布后,应用所支持的主题风格就固定了。要想增加更多的效果,只能发布新的版本。
为了解决这种问题,便有了APK主题方案。
APK主题方案很像之前提到的第一种将主题包保存到SD卡上的方案很像。
它们的共同点是,都需要在代码中手动设置图片、文字、颜色等信息。
主要的区别就是,第一种方案需要我们自行编写资源解析的方法。而通过APK主题方案,可以让Android系统帮我们搞定。
APK主题方案的基本思路是:在Android中,所有的资源都是基于包的。资源以id进行标识,在同一个应用中,每个资源都有唯一标识。但在不同的应用中,可以有相同的id。因此,只要获取到了其他应用的Context对象,就可以通过它的getRsources获取到其绑定的资源对象。然后,就可以使用Resources的getXXX方法获取字符串、颜色、dimension、图片等。
要想获取其他应用的Context对象,Android已经为我们提供好了接口。那就是android.content.ContextWrapper.createPackageContext(String packageName, int flags)方法。
实例代码如下:
import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.os.Bundle; import android.widget.ImageView; import android.widget.TextView; public class DemoRemoteThemeActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_theme); TextView text = (TextView) findViewById(R.id.remoteText); TextView color = (TextView) findViewById(R.id.remoteColor); ImageView image = (ImageView) findViewById(R.id.remote_image); try { String remotePackage = "com.xxx.themepackage"; Context remoteContext = createPackageContext(remotePackage, CONTEXT_IGNORE_SECURITY); Resources remoteResources = remoteContext.getResources(); text.setText(remoteResources.getText(remoteResources.getIdentifier("application_name", "string", remotePackage))); color.setTextColor(remoteResources.getColor(remoteResources.getIdentifier("delete_target_hover_tint", "color", remotePackage))); image.setImageDrawable(remoteResources.getDrawable(remoteResources.getIdentifier("ic_launcher_home", "drawable", remotePackage))); } catch (NameNotFoundException e) { e.printStackTrace(); } } }
首先,我通过createPackageContext获取到了包名为com.xxx.themepackage的应用的上下文。
然后,通过Resources的getIdentifier方法获取到相应资源名在该应用中的id。当然,有的人也可以通过使用自身应用的id的方式,不过这有一个前提,那就是同名资源在主题包应用与当前应用中的id相同。这貌似可以通过修改编译流程来实现,就像framework里的public.xml那样。不过这不在本文的讨论范畴内。
最后,就是通过Resources的getXXX方法获取资源了。
具体效果如下图所示。
3.高级应用
其实对于内置主题与APK主题来说,两者各有利弊。
内置主题实现简单,配置方便。但要想增加新的视效,就需要重新发布一次软件版本。对于目前的大部分用户来说,软件更新太频繁容易降低用户信赖感。
而APK主题虽然扩展性很高,但由于该方案需要在代码中设置所有的可变资源,软件实现周期较长,写代码时容易出错。而且第一版耗时较旧,一旦界面布局改变,需要较长的时间进行代码的编写。
因此,我们可以将以上两种结合起来使用,在代码中预置几种内置主题。
这些内置的主题,主要在界面风格上有所区别,如整体色调。
我们可以在这些内置主题中设置不同的文字颜色。
而对于图片,则在APK主题包中提供。
另外,在每个主题包中保存一个内置主题风格选择属性,这样不同的主题包就可以设置界面显示不同的风格。
比如说,我们的应用中内置两种主题,一种是暗色背景,一种是两色背景(就像Android自带的Holo.Dark与Holo.Light)。在这两种主题中分别设置相应的文字颜色。
而每个主题包中都存储一个字符串,如theme_type。这样,当显示页面时,我们先根据主题包中的theme_type来决定使用哪个主题来setTheme。剩下的图片则直接从主题包中获取。
这样,可以减少对代码的修改,方便主题包的制作。
相关推荐
文章探讨了几种主流的主题切换方案: 1. **主题包方式**:将主题元素制作成主题包,存储在外部存储卡上,应用读取并创建主题效果。如XBMC,其皮肤系统高度自定义但实现成本高,不适合快速开发场景。 2. **Android...
### Android 8.0系统界面切换时闪屏问题解析及解决方案 #### 一、问题背景与现象 在Android 8.0系统中,开发者可能会遇到一个常见问题:当应用程序进行界面切换时出现短暂的闪屏现象。这种现象不仅影响用户体验,...
总结,"Android Studio 护眼主题打包"是一个结合了UI定制、文件打包和资源共享的过程。通过个性化的设计和打包,开发者可以创造出更符合自己视觉习惯的工作环境,提高工作效率,同时也能将这份关怀分享给更多的同行...
总结来说,Android Studio为开发者提供了多种工具和组件来实现界面切换,从简单的Intent使用到复杂的Navigation Component和Fragment管理。开发者可以根据项目需求选择合适的策略,提升应用的用户体验。通过熟练掌握...
总结,实现Android App皮肤更换方案涉及资源管理、主题切换、动态加载等多个方面。通过创建和管理不同皮肤的资源文件,结合源码中的主题管理器,以及利用各种工具辅助处理,可以打造出具有个性化皮肤功能的应用。在...
### Android UI 解决方案:QMUI Android 开源框架解析 #### 概述 QMUI Android 是一款专为提高 Android 平台 UI 开发效率而设计的开源库。它由腾讯团队开发并维护,在 GitHub 上公开发布,旨在帮助开发者减少基础...
在Android应用开发中,创建一个具有吸引力且用户体验良好的界面是至关重要的。动态主题库,如“Colorful”,为开发者提供了一种...对于那些希望在应用中实现动态主题切换功能的开发者,Colorful是一个值得考虑的选择。
2. **主题化API**:Android 13引入了主题API,允许应用根据用户的系统主题进行自适应调整,包括颜色方案和图标样式,提供更个性化的用户体验。 3. **多语言支持优化**:系统对多语言支持进行了改进,开发者可以为...
总结来说,"pranavpandey-dynamic-support-b92a209"是一个专为Android应用开发设计的库,提供内置主题引擎,支持动态主题切换,兼容多版本Android系统,并且可能具有丰富的功能和良好的性能表现。开发者可以利用这个...
MIUI作为小米公司推出的基于Android系统的操作系统,在主题风格设计上有着独特的思考和技术实现方案。本篇将深入探讨MIUI主题风格的设计思路及其技术实现。 #### 二、“主题”的概念及重要性 1. **主题的概念**:...
在Android开发中,Activity...如果你对Android开发有更多的兴趣,可以深入学习相关的教程和总结,例如《Android开发入门与进阶教程》和《Android编程之activity操作技巧总结》,这些资源都能帮助你进一步提升开发技能。
总结来说,Android动态换肤是一种提升用户体验的技术,通过宿主工程加载插件Apk中的资源,结合反射和类加载器,实现在运行时更换应用界面风格。在实施过程中,需关注性能、安全、兼容性及用户体验等多个方面。
总结来说,主题和样式的使用能够使Android应用的UI设计更加灵活和模块化,同时提高代码的可读性和可维护性。熟练掌握这一技巧,将有助于提升开发效率,创建出更加美观、一致的用户界面。在实际开发中,应不断探索和...
下面将详细探讨这个主题,包括核心的开发步骤、所需的技术知识、可能遇到的问题以及解决方案。 首先,"android天气预报开发"意味着我们需要熟悉Android SDK,它是开发Android应用的基础。这涉及到Java或Kotlin编程...
总结,该策划方案全面覆盖了Android音乐播放器从需求分析到数据库设计的全过程,旨在构建一个满足用户需求、具有良好用户体验的音乐播放器。开发过程中,应充分考虑Android平台的特性,结合源代码实现,确保软件的...
4. **夜间模式监听**:监听系统的夜间模式变化,例如注册BroadcastReceiver,当系统夜间模式状态改变时,触发应用的主题切换。 四、代码实现 以下是一个简单的夜间模式切换的代码示例: ```java public void ...
这个主题基于“Android属性动画的一些实例”,我们将深入探讨这一关键的Android UI动画技术。 属性动画是Android 3.0(API级别11)引入的新特性,与视图动画(View Animation)相比,它提供了更多灵活性和控制力。...
4. **换肤功能实现**:“GL音乐播放器--代码实现_四_--实现换肤功能”展示了如何在应用中实现皮肤更换,这通常涉及到资源的动态加载和主题切换。开发者可以从中学习到如何根据用户选择动态改变应用的主题色和图标,...
总结,"Android模拟器访问本地Web应用"这一主题涵盖了从创建和配置模拟器,到测试和调试Web应用的全过程。通过学习这些知识点,开发者可以更加高效地在Android平台上进行Web应用的开发和测试工作。提供的"MHT"文件...