Trong bài hướng dẫn này, chúng ta sẽ cùng tìm hiểu và sử dụng theme DayNight trong ứng dụng Android. Nó có tác dụng kích hoạt chế độ ban đêm (night mode), rất hữu ích để làm dịu mắt người dùng khi bạn có một ứng dụng chứa nhiều nội dung văn bản.
Tích hợp theme DayNight trong Android
Android đã phát hành theme DayNight trong thư viện hỗ trợ phiên bản 23.2.0
. Nhờ theme này, chúng ta có thể dễ dàng chuyển đổi qua lại giữa chế độ sáng (light mode) và tối (dark mode) cho ứng dụng. Ta có thể thực hiện việc này một cách thủ công hoặc để Android tự động phát hiện thời gian trong ngày ngay trên điện thoại.
Theme này giúp cải thiện khả năng đọc và sử dụng của ứng dụng vào ban đêm bằng cách thay thế nền trắng sáng bằng một nền tối hơn. Nhiều ứng dụng reader đã triển khai theme này.
Để bắt đầu, chúng ta hãy tạo một dự án Android Studio mới với một activity trống.
Thêm theme vào styles.xml
Hãy thay thế theme hiện tại trong ứng dụng của chúng ta bằng theme DayNight.
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
Để thiết lập theme DayNight trong ứng dụng, ta sử dụng phương thức AppCompatDelegate.setDefaultNightMode()
. Dưới đây là các tham số được phép sử dụng trong phương thức trên.
MODE_NIGHT_YES
– Bật night mode thủ công.MODE_NIGHT_NO
– Tắt night mode thủ công.MODE_NIGHT_FOLLOW_SYSTEM
– Sử dụng cài đặt của hệ thống để xác định thời gian trong ngày và bật/tắt NightMode tương ứng. Đây là tham số mặc định.MODE_NIGHT_AUTO
– Tham số này sẽ cố gắng tự động phát hiện thời gian từ API vị trí của thiết bị. Nếu quyền truy cập dịch vụ vị trí không được cấp, nó sẽ sử dụng thời gian hệ thống.
Thêm đoạn mã sau vào phương thức onCreate()
.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); //For night mode theme
//AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); //For day mode theme
setContentView(R.layout.activity_main);
}
Theme phải luôn được thiết lập trước khi phương thức setContentView()
được gọi.
AppCompatDelegate là gì?
AppCompatDelegate
là một class đóng vai trò delegate (ủy quyền), cho phép bạn mở rộng các tính năng hỗ trợ của AppCompat cho bất kỳ activity nào. Hãy cùng xem màn hình activity của chúng ta trông như thế nào trong chế độ ban ngày và ban đêm.
TextView sẽ đổi màu chữ thành trắng ở night mode. Lý do là vì TextView ngầm chứa style mặc định có tên ?attr/colorPrimary
, có chức năng tự động chuyển đổi màu sắc dựa trên theme sáng/tối của ứng dụng. Nếu bạn đặt một màu tùy chỉnh như @color/red
cho TextView, màu này sẽ không thay đổi giữa hai chế độ. Màu chữ của Toolbar ở chế độ ban ngày là màu đen. Vậy làm thế nào để đổi màu chữ của Toolbar thành trắng ngay trong styles.xml
?
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- 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:textColorPrimary">@android:color/white</item>
<item name="android:textColorSecondary">@android:color/white</item>
</style>
Để lấy ra loại night mode hiện tại, ta dùng phương thức AppCompatDelegate.getDefaultNightMode()
. Phương thức này trả về một số nguyên tương ứng với mỗi loại đã đề cập ở trên.
Sau khi đã hiểu cách hoạt động cơ bản, chúng ta hãy cùng tạo một ứng dụng có các chức năng sau:
- Tùy chỉnh resource và style trong chế độ ngày/đêm.
- Chuyển đổi theme DayNight từ giao diện người dùng.
- Xem các widget giao diện người dùng khác nhau trông như thế nào trong Night Mode.
Cấu trúc dự án Android có night mode
Ví dụ về code cho Android sử dụng theme DayNight
Dưới đây là code cho file class activity_main.xml
.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="@android:dimen/app_icon_size"
android:text="Welcome to this tutorial."
android:textColor="@color/daynight_textColor"
android:textSize="18sp" />
<ImageView
android:id="@+id/imageView"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:src="@drawable/placeholder" />
<TextView
android:id="@+id/txtNightMode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/switchCompat"
android:layout_centerHorizontal="true"
android:paddingRight="8dp"
android:text="Night Mode"
android:textColor="@color/daynight_textColor" />
<android.support.v7.widget.SwitchCompat
android:id="@+id/switchCompat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@android:dimen/app_icon_size"
android:layout_toRightOf="@+id/txtNightMode"
android:checked="false"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/imageView"
android:layout_alignLeft="@+id/txtNightMode"
android:layout_alignStart="@+id/txtNightMode"
android:text="CLICK ME"
android:textColor="@color/daynight_textColor" />
</RelativeLayout>
- Chúng ta đã đặt một màu chữ và một drawable tùy chỉnh cho ImageView.
- Để thiết lập các màu sắc và drawable khác nhau cho theme ban ngày và ban đêm, ta cần tạo các thư mục riêng cho tài nguyên.
- Tài nguyên cho theme ban ngày nằm trong thư mục mặc định.
- Tài nguyên cho theme ban đêm nằm trong các thư mục có hậu tố -night.
- Vì vậy, chúng ta đã tạo các thư mục
values-night
vàdrawable-night
trong dự án. - Tên file drawable, tên màu, và tên style phải giống hệt nhau trong cả hai thư mục đối với những tài nguyên mà bạn muốn chuyển đổi trong theme DayNight.
- Nếu các tài nguyên trên chỉ được định nghĩa ở một trong hai thư mục, chúng sẽ được sử dụng chung cho cả theme ban ngày và ban đêm.
Code cho styles.xml
trong thư mục values
và values-night
như sau:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- 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:textColorPrimary">@android:color/white</item>
</style>
<style name="MyDialog" parent="Theme.AppCompat.Light.Dialog.Alert"/>
<style name="MySwitch">
<item name="colorControlActivated">@color/switchColor</item>
</style>
</resources>
<resources>
<!-- Base application theme. values-night.xml -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/orange</item>
<item name="colorPrimaryDark">@color/orangeDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorPrimary">@android:color/white</item>
</style>
<style name="MyDialog" parent="Theme.AppCompat.DayNight.Dialog.Alert"/>
<style name="MySwitch">
<item name="colorControlActivated">@color/switchColor</item>
</style>
</resources>
Các style được định nghĩa ở trên được dùng để tùy chỉnh theme DayNight chuẩn. Các định nghĩa tương ứng cho colors.xml
được trình bày như sau.
Dưới đây là code cho class MainActivity.java
.
package com.journaldev.daynightmode;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.SwitchCompat;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (InitApplication.getInstance().isNightModeEnabled()) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
setContentView(R.layout.activity_main);
SwitchCompat switchCompat = findViewById(R.id.switchCompat);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(MainActivity.this, R.style.MyDialog)
.setTitle("Title")
.setMessage("Message")
.show();
}
});
if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES)
switchCompat.setChecked(true);
switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
InitApplication.getInstance().setIsNightModeEnabled(true);
Intent intent = getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
startActivity(intent);
} else {
InitApplication.getInstance().setIsNightModeEnabled(false);
Intent intent = getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
startActivity(intent);
}
}
});
}
}
Trong đoạn code trên, ta sử dụng Switch để chuyển đổi giữa theme ban ngày và ban đêm trong ứng dụng. Chế độ hiện tại sẽ được lưu vào một đối tượng SharedPreferences. Chúng ta cần làm vậy vì theme của một activity chỉ có thể được thiết lập một lần duy nhất. Do đó, khi Switch được gạt, ta cần lưu chế độ mới vào một đối tượng SharedPreferences.
Ta sử dụng Singleton pattern cho class Application. Bằng cách này, cùng một thực thể của class Application
có thể được sử dụng xuyên suốt ứng dụng.
Dưới đây là code cho class InitApplication.java
.
package com.journaldev.daynightmode;
import android.app.Application;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class InitApplication extends Application {
public static final String NIGHT_MODE = "NIGHT_MODE";
private boolean isNightModeEnabled = false;
private static InitApplication singleton = null;
public static InitApplication getInstance() {
if(singleton == null)
{
singleton = new InitApplication();
}
return singleton;
}
@Override
public void onCreate() {
super.onCreate();
singleton = this;
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
this.isNightModeEnabled = mPrefs.getBoolean(NIGHT_MODE, false);
}
public boolean isNightModeEnabled() {
return isNightModeEnabled;
}
public void setIsNightModeEnabled(boolean isNightModeEnabled) {
this.isNightModeEnabled = isNightModeEnabled;
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(NIGHT_MODE, isNightModeEnabled);
editor.apply();
}
}
Đây là nơi chúng ta cập nhật và lấy ra loại night mode từ SharedPreferences
.
Giao diện ứng dụng Android hỗ trợ night mode
Tổng kết
Đó là tất cả những gì bạn cần biết để bắt đầu sử dụng theme DayNight trong ứng dụng Android của mình để hỗ trợ chuyển đổi giao diện sáng-tối. Bạn có thể tải về toàn bộ dự án ví dụ ở trên từ đường dẫn này.