打造自己的 APP「冰与火百科」(四):WebView 交互、夜间模式

给大家介绍一下简单的 WebView 交互、夜间模式以及 Material Design 风格搜索的实现。

WebView 交互

在详情页面我是用 WebView 展示的,我想实现的交互是,点击 WebView 的内容跳转另一个页面。实现过程是,让 HTML 代码调用 JavaScript 代码,再让 JavaScript 代码调用 Android 的代码,下面看看如何实现。

HTML

先看 HTML 代码,假如在文本内容里有一个可以跳转的「凯特琳·徒利」,让他去调用 skip.js 的代码,指定 CatelynTully() 方法:


  凯特琳·徒利
  

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 来展示数据:

binding.webView.loadDataWithBaseURL("file:///android_asset/", htmlData, "text/html", "utf-8", null);

这样就完成了一个简单的 JavaScript 和 Android 的交互,效果如下:

inf_js.gif

夜间模式

关于夜间模式的实现,主要是参考了 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 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 交互、夜间模式)