打造自己的 APP「冰與火百科」(四):WebView 互動、夜間模式

NanBox發表於2017-12-25

給大家介紹一下簡單的 WebView 互動和夜間模式在「冰與火百科」中的實現。

WebView 互動

在詳情頁面我是用 WebView 展示的,我想實現的互動是,點選 WebView 的內容跳轉另一個頁面。實現過程是,讓 HTML 程式碼呼叫 JavaScript 程式碼,再讓 JavaScript 程式碼呼叫 Android 的程式碼,下面看看如何實現。

HTML

先看 HTML 程式碼,假如在文字內容裡有一個可以跳轉的「凱特琳·徒利」,讓他去呼叫 skip.js 的程式碼,指定 CatelynTully() 方法:

<body>
  <a href="javascript:void(0)" onclick="CatelynTully()">凱特琳·徒利</a>
  <script type="text/javascript" src="skip.js"></script>
</body>
複製程式碼

JavaScript

這個 skip.js 檔案我是放在客戶端的,放在 assets 目錄下,程式碼如下:

function CatelynTully(){
	javascript:Android.goDetail('Catelyn_Tully');
}
複製程式碼

意思就是去呼叫 Android 的 goDetail(String id) 方法。

Android

在客戶端新增 goDetail 方法,我把 JavaScript 和 Java 互動的程式碼寫在一個類裡,記得給方法加上 @JavascriptInterface 註解:

public class Js2Java {

    private Context mContext;

    public Js2Java(Context context) {
        this.mContext = context;
    }

    @JavascriptInterface
    public void goDetail(String id) {
        // 根據 id 跳轉頁面
    }
}
複製程式碼

來到顯示 WebView 的頁面,新增以下程式碼讓 WebView 支援 JavaScript:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new Js2Java(this), "Android");
複製程式碼

使用 loadDataWithBaseURL 來展示資料:

webView.loadDataWithBaseURL("file:///android_asset/", htmlData, "text/html", "utf-8", null);
複製程式碼

這樣就完成了一個簡單的 JavaScript 和 Android 的互動,效果如下:

打造自己的 APP「冰與火百科」(四):WebView 互動、夜間模式

夜間模式

關於夜間模式的實現,主要是參考了 D_clock愛吃蔥花 大神的這篇文章,簡單說一下實現過程如下:

  1. 在 styles 中新增「DayTheme」和「NightTheme」兩個主題;
  2. 在佈局檔案中使用類似 android:background="?attr/colorBackground" 來設定顏色,使其跟隨當前主題顏色;
  3. 編寫 DayNightHelper,利用 SharePreferences 儲存、獲取當前模式;
  4. 在頁面 setContentView 之前,判斷當前模式,並通過 setTheme 設定當前模式;
  5. 將螢幕內容轉為 Bitmap,對其執行一個漸隱動畫,實現切換時漸變的效果;
  6. 監聽模式切換,通過 TypedValue 和 Theme.resolveAttribute 在程式碼中獲取 Theme 中的顏色,重新設定控制元件的顏色。

更詳細的內容可以檢視原文,下面再補充幾個控制元件的顏色設定方法。

Toolbar

假設已經拿到了切換後的顏色 color,修改 Toolbar 的背景顏色和字型顏色:

toolbar.setBackground(color);
toolbar.setTitleTextColor(color);
複製程式碼

除了這兩項,Toolbar 上可能還有操作按鈕,像我這裡左邊的選單和右邊的搜尋按鈕。它們的顏色可以這樣設定:

// 選單按鈕
Drawable navigationIcon = toolbar.getNavigationIcon();
if (navigationIcon != null) {
    navigationIcon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
// 搜尋按鈕
Menu toolbarMenu = toolbar.getMenu();
Drawable searchIcon = toolbarMenu.getItem(0).getIcon();
if (searchIcon != null) {
    searchIcon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
複製程式碼

TabLayout

對於 TabLayout,涉及到的顏色有背景顏色、文字顏色(選中和未選中)、指示條:

tabLayout.setBackgroundResource(color);
tabLayout.setTabTextColors(normalColor, selectedColor);
tabLayout.setSelectedTabIndicatorColor(color);
複製程式碼

NavigationView

NavigationView 存在一個頭部,需要的話可以可以這樣修改頭部的背景和字型顏色:

View navigationHeader = navigationView.getHeaderView(0);
if (isDay) {
    navigationHeader.setBackgroundResource(R.drawable.side_nav_bar_day);
} else {
    navigationHeader.setBackgroundResource(R.drawable.side_nav_bar_night);
}
TextView tvHeader = (TextView) navigationHeader.findViewById(R.id.text_view);
tvHeader.setTextColor(color);
複製程式碼

接下來是目錄部分的背景、字型顏色及圖表顏色:

navigationView.setBackgroundResource(color);
navigationView.setItemTextColor(color);
navigationView.setItemIconTintList(color);
複製程式碼

RecyclerView

通過遍歷所有的 ChildView,對每一項進行顏色設定:

for (int position = 0; position < recyclerView.getChildCount(); position++) {
    ViewGroup childView = (ViewGroup) binding.recyclerView.getChildAt(position);
    // 設定顏色
}
複製程式碼

但要注意的是,RecyclerView 的內部使用 Recycler 和 RecyclerViewPool 實現了快取,有可能出現當前使用的 item 顏色改變了,但是快取裡的沒有變化。

解決方法是清理快取,呼叫 Recycler 和 RecyclerViewPool 的 Clear() 方法,但前者無法直接呼叫,只能通過反射實現:

Class<RecyclerView> recyclerViewClass = RecyclerView.class;
try {
    Field declaredField = recyclerViewClass.getDeclaredField("mRecycler");
    declaredField.setAccessible(true);
    Method declaredMethod = Class.forName(RecyclerView.Recycler.class.getName()).getDeclaredMethod("clear");
    declaredMethod.setAccessible(true);
    declaredMethod.invoke(declaredField.get(binding.recyclerView));
    RecyclerView.RecycledViewPool recycledViewPool = recyclerView.getRecycledViewPool();
    recycledViewPool.clear();
} catch (Exception e) {
    e.printStackTrace();
}
複製程式碼

StatusBar

在 SDK 21 以上,允許我們修改狀態列的顏色:

if (Build.VERSION.SDK_INT >= 21 {
    TypedValue typedValue = new TypedValue();
    Resources.Theme theme = getTheme();
    theme.resolveAttribute(R.attr.color, typedValue, true);
    getWindow().setStatusBarColor(
            ContextCompat.getColor(mContext, typedValue.resourceId));
}
複製程式碼

夜間模式的實現就到此,在重新設定顏色的部分比較繁瑣,但這是我目前看到效果比較好的實現方式。效果如下:

打造自己的 APP「冰與火百科」(四):WebView 互動、夜間模式

專案地址

相關文章