- 原文链接 : Supporting multiple themes in your Android app (Part 2)
- 译者 : chaossss
- 校对者: Mr.Simple
- 状态 : 完成
在 上一篇博文 中,我们创建了一个明亮风格的主题,并且为实现使用多种主题作了一些前期的准备,而今天呢,我打算在这篇博文中接着上一篇博文继续为大家讲解,而我今天要讲的内容大概是以下三个部分:使 Android 应用能够使用多种主题,创建一个灰暗风格的主题,以及允许 Android 应用在运行时自由地切换不同的主题。
在理想的情况下,如果我们把主题的设置看作是一项配置,那么我们应该能够在类似 "theme-qualifier" 的目录下指定我们想要的特定主题,例如:values-dark 就是我们想要的灰暗风格主题;而values-light 则是明亮风格的主题。但很遗憾,在这篇博文所要讲述的实现方法里,这种方法并没有成为实现方式之一。
那么我们要怎么为不同的主题指定相应的资源文件呢?如果我们有了解过 appcompat 是怎么使用资源文件的话,对 Android 系统是如何管理和使用资源文件会有一个粗略的认识。毫无疑问,Materialistic 中使用的方法就是类似于 Android 系统使用的方法。
values/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- original theme attributes -->
...
</style>
<style name="AppTheme.Dark" parent="Theme.AppCompat">
<item name="colorPrimary">@color/colorPrimaryInverse</item>
<item name="colorPrimaryDark">@color/colorPrimaryDarkInverse</item>
<item name="colorAccent">@color/colorAccentInverse</item>
<item name="android:textColorPrimary">@color/textColorPrimaryInverse</item>
<item name="android:textColorSecondary">@color/textColorSecondaryInverse</item>
<item name="android:textColorPrimaryInverse">@color/textColorPrimary</item>
<item name="android:textColorSecondaryInverse">@color/textColorSecondary</item>
...
</style>
values/color.xml
<!-- original color palette -->
...
<!-- alternative color palette -->
<color name="colorPrimaryInverse">...</color>
<color name="colorPrimaryDarkInverse">...</color>
<color name="colorAccentInverse">...</color>
在上面的操作中我们创建了一个名叫 AppTheme.Dark 的灰暗风格主题,此外,为了保持 style 和 color 的一致性,我们的 AppTheme.Dark 主题衍生于 appcompat 的 Theme.AppCompat 主题(一个 Android 自带的灰暗风格主题)。然而,由于我们的两个主题(明亮风格和灰暗风格)衍生于不同的基础主题,因此这两个主题之间并不能够进行属性的共享(在JAVA中,类只能进行单继承)。
这两个主题理应有一些恰当的属性值,能同时用于设置基本的 Android 和 appcompat的主题属性,例如:在灰暗风格中,android:textColorPrimary 应该被设置为明亮的,而在明亮风格中,android:textColorPrimary则应该是灰暗的。按照常用的命名习惯,我们在这里将用相反的后缀来区分可替代的主题颜色。
在某些情况下,一种颜色能同时在明亮风格和灰暗风格的主题中被使用,这当然是喜闻乐见的情况,但是在大部分主题中这并不能够实现。所以我希望你在设计主题的过程中,通过在 AndroidManifest.xml 中短暂地切换你应用里正在使用的可替代主题,以此确定你的主题是否需要添加其他的 colors/style 文件来满足你的主题设计需求。
到了现在,我相信我们都能很轻松地为我们的 App 设计出美如画的灰暗风格主题,但这里还存在一些小麻烦,例如:用于美化 action bar 菜单选项的 drawables 资源文件。灰暗风格的 action bar 需要用明亮的颜色修饰它的菜单选项,反之亦然。为了让 Android 能够在不同的App主题下区分不同的 drawables 资源文件,我们创建了能够指定正确资源文件的 自定义属性 引用,并且在不同的主题下提供了不同的 drawable 引用,将其值赋给特定的自定义属性。(温婉如妻,appcompat 库贴心地为我们准备了类似 colorPrimary 的自定义属性值)
values/attrs.xml
<attr name="themedMenuStoryDrawable" format="reference" />
<attr name="themedMenuCommentDrawable" format="reference" />
...
values/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- original theme attributes -->
...
<item name="themedMenuStoryDrawable">@drawable/ic_subject_white_24dp</item>
<item name="themedMenuCommentDrawable">@drawable/ic_mode_comment_white_24dp</item>
</style>
<style name="AppTheme.Dark" parent="Theme.AppCompat">
<!-- alternative theme attributes -->
...
<item name="themedMenuStoryDrawable">@drawable/ic_subject_black_24dp</item>
<item name="themedMenuCommentDrawable">@drawable/ic_mode_comment_black_24dp</item>
</style>
menu/my_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@id/menu_comment"
android:icon="?attr/themedMenuCommentDrawable" />
<item android:id="@id/menu_story"
android:icon="?attr/themedMenuStoryDrawable" />
<item android:id="@id/menu_share"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider" />
</menu>
根据你实际的主题设计需要,类似的实现也能被用于为大多数自定义属性指定相应的资源值。但这个方法存在一个问题:根据实际的需要从 drawable 资源文件中解析相应的属性值,并应用于主题的方法在API 21之前的版本似乎都不可行。举例来说明这个问题吧:如果你有一个 layer-list 中包含了各种你所需要的 color 的 drawable 资源文件,在API 21之前的版本中,这些 color 的值都应该是固定的,而不是能够在App运行过程中不断变化的。这个问题在 Google I/O 2014 大会上有被提出,并要求给出相应的解决办法。(详情参见 Click Me!)。
此外,为了避免在不同的主题中重复使用相同的资源文件,我们可以利用 drawable 的 tint 属性解决这个需求。虽然这个属性可以在API 21之后的版本中使用。但 Dan Lew 在他的博客中为我们介绍了怎么在所有的 API 版本中使用 tint 属性。但就个人偏好来说,如果可以的话,我会更倾向于选择不受 View 逻辑影响的 Java 实现,所以我选择为每一个主题提供不同的 drawable 资源文件。
现在我们已经有两个可以使用的主题了,接下来我们需要做的就是让用户能够在使用 App 时能够自如地根据他们的个人偏好切换不同的主题。要实现这个功能,我们可以通过使用 SharedPreferences 来实现,通过改变 pref_dark_theme 的值去存储当前被选择的主题并决定我们的 App 将要被切换成什么主题。但从实际情况来考虑,主题切换后,App 所有 Activity 的 View 在被创建之前都应该被改变,所以我们只需要在 onCreate()方法中实现我们的逻辑。
BaseActivity.java
public abstract class BaseActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean("pref_dark_theme"), false)) {
setTheme(R.style.AppTheme_Dark);
}
super.onCreate(savedInstanceState);
}
}
在这里,因为我们的 App 已经使用了默认的明亮风格主题,所以我们只需要检查默认的引用是否被重载,是否被用于重载灰暗风格的主题。为了默认的引用能够被所有 Activity共享,其中的逻辑已经在 "base" Activity中被写好了。
值得注意的是,这个方法只能被用于改变没有处在 back stack 中的 Acitivity 的主题。而那些已经在 back stack 中的 Activity,仍然会显示为之前的主题,因为当我们结束当前 Activity,返回到上一个 Activity,只会触发 onResume() 方法,而不是我们期望的 onCreate()方法。因此,考虑到实际的产品功能设计需求,我们当然要解决这些“过时”的 Activity 了,我在这里为大家提供了两种解决办法,都挺简单的:一方面,我们可以清空我们的 back stack;另一方面,一旦 preference 被改变,我们就在 back stack 中按照顺序让所有 Acitivty 出栈后重新加载,将所有 Activity 的主题改变后再重新入栈。在这里为了简便,我们选择的实现方法是:当主题被改变,我们就简单地清空 back stack,然后重启当前的 Activity。
public class SettingsFragment extends PreferenceFragment {
...
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (!key.equals("pref_dark_theme")) {
return;
}
getActivity().finish();
final Intent intent = getActivity().getIntent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
getActivity().startActivity(intent);
}
};
}
...
虽然结束得有些突然,但我们今天的讲解就到此结束啦。现在我们的 App 拥有了两个这么优雅的主题,就算是挑剔的文艺小清新也不会嫌弃我们的 App 很 low 了吧!如果你想要了解整个 Materialistic 的具体实现,或者是这个功能的源码,可以来我的 GitHub 上获取哦~
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。