夜间模式切换

对于Android日夜间模式实现的探讨

0x0001

关于 Android 的日间/夜间模式切换相信大家在平时使用 APP 的过程中都遇到过,比如知乎、简书中就有相关的模式切换。实现日间/夜间模式切换的方案也有许多种,趁着今天有空来讲一下日间/夜间模式切换的几种实现方案,也可以做一个横向的对比来看看哪种方案最好。

在本篇文章中给出了三种实现日间/夜间模式切换的方案:

1. 使用 setTheme 的方法让 Activity 重新设置主题;

2. 设置 Android Support Library 中的 UiMode 来支持日间/夜间模式的切换;

3. 通过资源 id 映射,回调自定义 ThemeChangeListener 接口来处理日间/夜间模式的切换。

三种方案综合起来可能导致文章的篇幅过长,请耐心阅读。

0x0002

使用 setTheme 方法

我们先来看看使用 setTheme 方法来实现日间/夜间模式切换的方案。这种方案的思路很简单,就是在用户选择夜间模式时,Activity 设置成夜间模式的主题,之后再让 Activity 调用 recreate() 方法重新创建一遍就行了。

那就动手吧,在 colors.xml 中定义两组颜色,分别表示日间和夜间的主题色:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <color name="nightColorPrimary">#3b3b3b</color>
    <color name="nightColorPrimaryDark">#383838</color>
    <color name="nightColorAccent">#a72b55</color>
</resources>

之后在 styles.xml 中定义两组主题,也就是日间主题和夜间主题:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:textColor">@android:color/black</item>
        <item name="mainBackground">@android:color/white</item>
    </style>

    <style name="NightAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/nightColorPrimary</item>
        <item name="colorPrimaryDark">@color/nightColorPrimaryDark</item>
        <item name="colorAccent">@color/nightColorAccent</item>
        <item name="android:textColor">@android:color/white</item>
        <item name="mainBackground">@color/nightColorPrimaryDark</item>
    </style>

//接下来注意一下,在res文件夹下,找到value然后点击右键,新建一个写接下来的:
</resources>

在主题中的 mainBackground 属性是我们自定义的属性,用来表示背景色:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="mainBackground" format="color|reference"></attr>
</resources>

接下来就是看一下布局 activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    android:background="?attr/mainBackground"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.yuqirong.themedemo.MainActivity">

    <Button
        android:id="@+id/btn_theme"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="切换日/夜间模式" />

    <TextView
        android:id="@+id/tv"
        android:layout_below="@id/btn_theme"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="通过setTheme()的方法" />

</RelativeLayout>

<RelativeLayout>  android:background 属性中,我们使用 "?attr/mainBackground" 来表示,这样就代表着 RelativeLayout 的背景色会去引用在主题中事先定义好的 mainBackground 属性的值。这样就实现了日间/夜间模式切换的换色了。

最后就是 MainActivity 的代码:

public class MainActivity extends AppCompatActivity {

// 默认是日间模式
    private int theme = R.style.AppTheme;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
// 判断是否有主题存储
        if(savedInstanceState != null){
            theme = savedInstanceState.getInt("theme");
            setTheme(theme);
        }
        setContentView(R.layout.activity_main);

        Button btn_theme = (Button) findViewById(R.id.btn_theme);
        btn_theme.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                theme = (theme == R.style.AppTheme) ? R.style.NightAppTheme : R.style.AppTheme;
                MainActivity.this.recreate();
            }
        });
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("theme", theme);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        theme = savedInstanceState.getInt("theme");
    }
}

MainActivity 中有几点要注意一下:

1. 

调用 recreate() 方法后 Activity 的生命周期会调用 onSaveInstanceState(Bundle outState) 来备份相关的数据,之后也会调用 onRestoreInstanceState(Bundle savedInstanceState) 来还原相关的数据,因此我们把 theme 的值保存进去,以便 Activity 重新创建后使用。

2. 

 

我们在 onCreate(Bundle savedInstanceState) 方法中还原得到了 theme 值后,setTheme() 方法一定要在 setContentView() 方法之前调用,否则的话就看不到效果了。

3. 

 

recreate() 方法是在 API 11 中添加进来的,所以在 Android 2.X 中使用会抛异常。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值