欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android夜間模式最佳實(shí)踐

 更新時(shí)間:2016年02月19日 14:53:39   作者:馬俊  
這篇文章主要介紹了Android夜間模式最佳實(shí)踐,在Android應(yīng)用普遍支持夜間模式的今天,如何優(yōu)雅地實(shí)現(xiàn)夜間模式?感興趣的小伙伴們可以參考一下

由于Android的設(shè)置中并沒(méi)有夜間模式的選項(xiàng),對(duì)于喜歡睡前玩手機(jī)的用戶(hù),只能簡(jiǎn)單的調(diào)節(jié)手機(jī)屏幕亮度來(lái)改善體驗(yàn)。目前越來(lái)越多的應(yīng)用開(kāi)始把夜間模式加到自家應(yīng)用中,沒(méi)準(zhǔn)不久google也會(huì)把這項(xiàng)功能添加到Android系統(tǒng)中吧。

業(yè)內(nèi)關(guān)于夜間模式的實(shí)現(xiàn),有兩種主流方案,各有其利弊,我較為推崇第三種方案:

1、通過(guò)切換theme來(lái)實(shí)現(xiàn)夜間模式。
2、通過(guò)資源id映射的方式來(lái)實(shí)現(xiàn)夜間模式。
3、通過(guò)修改uiMode來(lái)切換夜間模式。

值得一提的是,上面提到的幾種方案,都是資源內(nèi)嵌在Apk中的方案,像新浪微博那種需要通過(guò)下載方式實(shí)現(xiàn)的夜間模式方案,網(wǎng)上有很多介紹,這里不去討論。

下面簡(jiǎn)要描述下幾種方案的實(shí)現(xiàn)原理:

一、通過(guò)切換theme來(lái)實(shí)現(xiàn)夜間模式

首先在attrs.xml中,為需要隨theme變化的內(nèi)容定義屬性

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

其次在不同的theme中,對(duì)屬性設(shè)置不同的值,在styles.xml中定義theme如下

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <!-- 默認(rèn) -->
  <style name="ThemeDefault" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="mainBackground">#ffffff</item>
    <item name="textColor">#000000</item>
  </style>
  <!-- 夜間 -->
  <style name="ThemeNight" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="mainBackground">#000000</item>
    <item name="textColor">#ffffff</item>
  </style>
</resources>

在布局文件中使用對(duì)應(yīng)的值,通過(guò)?attr/屬性名,來(lái)獲取不同theme對(duì)應(yīng)的值。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android="@+id/main_screen"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  android:background="?attr/mainBackground">
  <Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="改變Theme"
    android:onClick="changeTheme"
    android:textColor="?attr/textColor"/>
</LinearLayout>

在Activity中調(diào)用如下changeTheme方法,其中isNightMode為一個(gè)全局變量用來(lái)標(biāo)記當(dāng)前是否為夜間模式,在設(shè)置完theme后,還需要調(diào)用restartActivity或者setContentView重新刷新UI。

public void changeTheme() {
  if (isNightMode) {
    setTheme(R.style.ThemeDefault);
    isNightMode = false;
  } else {
    setTheme(R.style.ThemeNight);
    isNightMode = true;
  }
  setContentView(R.layout.activity_main);
}

到此即完成了一個(gè)夜間模式的簡(jiǎn)單實(shí)現(xiàn),包括Google自家在內(nèi)的很多應(yīng)用都是采用此種方式實(shí)現(xiàn)夜間模式的,這應(yīng)該也是Android官方推薦的方式。

但這種方式有一些不足,規(guī)模較大的應(yīng)用,需要隨theme變化的屬性會(huì)很多,都需要逐一定義,有點(diǎn)麻煩,另外一個(gè)缺點(diǎn)是要使得新theme生效,一般需要restartActivity來(lái)切換UI,會(huì)導(dǎo)致切換主題時(shí)界面閃爍。

不過(guò)也可以通過(guò)調(diào)用如下updateTheme方法,只更新需要更新的部分,規(guī)避閃爍問(wèn)題,只是需要寫(xiě)上一堆updateTheme方法。

private void updateTheme() {
  TypedValue typedValue = new TypedValue();
  Resources.Theme theme = getTheme();
  theme.resolveAttribute(R.attr.textColor, typedValue, true);
  findViewById(R.id.button).setBackgroundColor(typedValue.data);
  theme.resolveAttribute(R.attr.mainBackground, typedValue, true);
  findViewById(R.id.main_screen).setBackgroundColor(typedValue.data);
}

二、通過(guò)資源id映射的方式實(shí)現(xiàn)夜間模式

通過(guò)id獲取資源時(shí),先將其轉(zhuǎn)換為夜間模式對(duì)應(yīng)id,再通過(guò)Resources來(lái)獲取對(duì)應(yīng)的資源。

public static Drawable getDrawable(Context context, int id) {
  return context.getResources().getDrawable(getResId(id));
}

public static int getResId(int defaultResId) {
  if (!isNightMode()) {
    return defaultResId;
  }
  if (sResourceMap == null) {
    buildResourceMap();
  }
  int themedResId = sResourceMap.get(defaultResId);
  return themedResId == 0 ? defaultResId : themedResId;
}

這里是通過(guò)HashMap將白天模式的resId和夜間模式的resId來(lái)一一對(duì)應(yīng)起來(lái)的。

private static void buildResourceMap() {
  sResourceMap = new SparseIntArray();
  sResourceMap.put(R.drawable.common_background, R.drawable.common_background_night);
  // ...
}

這個(gè)方案簡(jiǎn)單粗暴,麻煩的地方和第一種方案一樣:每次添加資源都需要建立映射關(guān)系,刷新UI的方式也與第一種方案類(lèi)似,貌似今日頭條,網(wǎng)易新聞客戶(hù)端等主流新聞閱讀應(yīng)用都是通過(guò)這種方式實(shí)現(xiàn)的夜間模式。

三、通過(guò)修改uiMode來(lái)切換夜間模式

首先將獲取資源的地方統(tǒng)一起來(lái),使用Application對(duì)應(yīng)的Resources,在Application的onCreate中調(diào)用ResourcesManager的init方法將其初始化。

public static void init(Context context) {
  sRes = context.getResources();
}


切換夜間模式時(shí),通過(guò)更新uiMode來(lái)更新Resources的配置,系統(tǒng)會(huì)根據(jù)其uiMode讀取對(duì)應(yīng)night下的資源,同時(shí)在res中給夜間模式的資源添加-night后綴,比如values-night,drawable-night。

public static void updateNightMode(boolean on) {
  DisplayMetrics dm = sRes.getDisplayMetrics();
  Configuration config = sRes.getConfiguration();
  config.uiMode &= ~Configuration.UI_MODE_NIGHT_MASK;
  config.uiMode |= on ? Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO;
  sRes.updateConfiguration(config, dm);
}

至于Android的資源讀取,我們可以參考老羅的博客《Android應(yīng)用程序資源的查找過(guò)程》,分析看看資源是怎么被精準(zhǔn)找到的。這種方法相對(duì)前兩種的好處就是資源添加非常簡(jiǎn)單清晰,但是UI上的更新還是無(wú)法做到非常順滑的切換。

我是怎么找到第三種方案的?

在Android開(kāi)發(fā)文檔中搜索night發(fā)現(xiàn)如下,可以通過(guò)UiModeManager來(lái)實(shí)現(xiàn)

night: Night time
notnight: Day time
Added in API level 8.

This can change during the life of your application if night mode is left in auto mode (default), in which case the mode changes based on the time of day. You can enable or disable this mode using UiModeManager. See Handling Runtime Changes for information about how this affects your application during runtime.

不幸的是必須在駕駛模式下才有效,那是不是打開(kāi)駕駛模式再設(shè)置呢,實(shí)際上是不可行的,駕駛模式下系統(tǒng)UI有變動(dòng),這樣是不可取的。

/**
* Sets the night mode. Changes to the night mode are only effective when
* the car or desk mode is enabled on a device.
*
* The mode can be one of:
* {@link #MODE_NIGHT_NO}- sets the device into notnight
* mode.
* {@link #MODE_NIGHT_YES} - sets the device into night mode.
* {@link #MODE_NIGHT_AUTO} - automatic night/notnight switching
* depending on the location and certain other sensors.
*/
public void setNightMode(int mode)

從源碼開(kāi)始看起,UiModeManagerService.java的setNightMode方法中:

if (isDoingNightModeLocked() && mNightMode != mode) {
  Settings.Secure.putInt(getContext().getContentResolver(), Settings.Secure.UI_NIGHT_MODE, mode);
  mNightMode = mode;
  updateLocked(0, 0);
}

boolean isDoingNightModeLocked() {
  return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;
}

在 isDoingNightModeLocked中判斷了DockState和mCardMode的狀態(tài),如果滿(mǎn)足條件實(shí)際上只修改了mNightMode的值,繼續(xù)跟蹤updateLocked方法,可以看到在updateConfigurationLocked中更新了Configuration的uiMode。

讓我們轉(zhuǎn)向Configuration的uiMode的描述:

/**
* Bit mask of the ui mode. Currently there are two fields:
*
The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
* device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
* {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
* {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION},
* {@link #UI_MODE_TYPE_APPLIANCE}, or {@link #UI_MODE_TYPE_WATCH}.
*
*
The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
* is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
* {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
*/
public int uiMode;

uiMode為public可以直接設(shè)置,既然UiModeManager設(shè)置nightMode只改了Configuration的uiMode,那我們是不是可以直接改其uiMode呢?

實(shí)際上只需要上面一小段代碼就可以實(shí)現(xiàn)了,但如果不去查看UiModeManager的夜間模式的實(shí)現(xiàn),不會(huì)想到只需要更新Configuration的uiMode就可以了。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。

相關(guān)文章

  • OKHttp使用詳解

    OKHttp使用詳解

    OkHttp 是一套處理 HTTP 網(wǎng)絡(luò)請(qǐng)求的依賴(lài)庫(kù),由 Square 公司設(shè)計(jì)研發(fā)并開(kāi)源,目前可以在 Java 和 Kotlin 中使用,這篇文章主要介紹了OKHttp詳解,需要的朋友可以參考下
    2024-01-01
  • Android實(shí)現(xiàn)文字消除效果

    Android實(shí)現(xiàn)文字消除效果

    由于項(xiàng)目和語(yǔ)音識(shí)別相關(guān),有時(shí)候人在不經(jīng)意間交流的無(wú)效音頻會(huì)被識(shí)別出來(lái),并展示于界面,為了美觀,客戶(hù)要求我們將這些無(wú)效的識(shí)別文本用一個(gè)從右到左的動(dòng)畫(huà)給清除,于是便有了下述的技術(shù)實(shí)現(xiàn)。感興趣的朋友可以參考下
    2021-06-06
  • Android實(shí)現(xiàn)手繪功能

    Android實(shí)現(xiàn)手繪功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)手繪功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Kotlin編寫(xiě)Android適配器Adapter

    Kotlin編寫(xiě)Android適配器Adapter

    這篇文章主要為大家詳細(xì)介紹了Kotlin編寫(xiě)Android適配器Adapter的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 使用Android系統(tǒng)提供的DownloadManager來(lái)下載文件

    使用Android系統(tǒng)提供的DownloadManager來(lái)下載文件

    本篇文章主要介紹了使用Android系統(tǒng)提供的DownloadManager來(lái)下載文件,可以將長(zhǎng)時(shí)間的下載任務(wù)交給系統(tǒng),完全由系統(tǒng)管理,有需要的可以了解下。
    2016-11-11
  • Android實(shí)現(xiàn)炫酷輪播圖效果

    Android實(shí)現(xiàn)炫酷輪播圖效果

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)炫酷輪播圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • Android電話(huà)撥號(hào)器實(shí)現(xiàn)方法

    Android電話(huà)撥號(hào)器實(shí)現(xiàn)方法

    這篇文章主要介紹了Android電話(huà)撥號(hào)器實(shí)現(xiàn)方法,可實(shí)現(xiàn)模擬Android電話(huà)撥號(hào)的功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-09-09
  • Android ServiceManager的啟動(dòng)和工作原理

    Android ServiceManager的啟動(dòng)和工作原理

    這篇文章主要介紹了Android ServiceManager的啟動(dòng)和工作原理,幫助大家更好的理解和學(xué)習(xí)使用Android開(kāi)發(fā),感興趣的朋友可以了解下
    2021-03-03
  • Android 之BottomsheetDialogFragment仿抖音評(píng)論底部彈出對(duì)話(huà)框效果(實(shí)例代碼)

    Android 之BottomsheetDialogFragment仿抖音評(píng)論底部彈出對(duì)話(huà)框效果(實(shí)例代碼)

    這篇文章主要介紹了Android 中之BottomsheetDialogFragment仿抖音評(píng)論底部彈出對(duì)話(huà)框效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Android MenuItem 自定義長(zhǎng)按事件的實(shí)現(xiàn)

    Android MenuItem 自定義長(zhǎng)按事件的實(shí)現(xiàn)

    這篇文章主要介紹了Android MenuItem 自定義長(zhǎng)按事件的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08

最新評(píng)論