WebView搭配Material Design新控件使用

2017-02-25 Mystery0 M 更多博文 » 博客 » GitHub »

Android WebView Material Design

原文链接 https://mystery00.github.io/2017/02/25/WebView%E6%90%AD%E9%85%8DMaterial-Design%E6%96%B0%E6%8E%A7%E4%BB%B6%E4%BD%BF%E7%94%A8/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


说在前面

这两天在帮同学的网站写客户端,要求的功能是点击菜单中的某一项显示一个网页,之前的一个客户端是直接调用Intent跳转默认浏览器来进行访问。这次想做成在应用中访问的,也就是使用WebView来显示,虽然学了安卓快一年了,却从来没有写过WebView,过程中遇到了一些问题,故此记录。


效果展示

正如gif所示,WebView往下滑隐藏Toolbar,往上滑显示Toolbar,往左滑后退,往右滑前进。


WebView下滑隐藏ToolBar,上滑显示ToolBar

使用了Matarial Disign的小伙伴应该很清楚,Google提供了CoordinatorLayout来使控件能够响应滚动事件,比如说当滚动RecyclerView的时候,只需要几行代码就能够让ToolBar跟随着滚动,并且非常流畅,所以我打算的就是通过CoordinatorLayout来实现这个效果。 我google了一些博客,有些人是通过重写WebViewonScrollChanged方法来实现,这样的话虽然可以进行很强的自定义,但是我觉得却太复杂了,对于我的这个应用来说,需要实现的功能很简单,不用这么复杂。

导包我想各位应该都清楚的,所以我就不说版本了

布局代码如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.weily.mutour.activity.WebActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_web"/>

</android.support.design.widget.CoordinatorLayout>

这个代码各位应该非常眼熟,如果用的是Android Studio,同时新建Activity的时候选择Basic Activity,会自动新建activity_xxx.xml以及content_xxx.xml,而这就是activity的那个文件的源码,注意源码中相比较于默认的源码,我在ToolBar的标签那里添加了一个app:layoutscrollFlags属性。各位如果清楚CoordinatorLayout的应该都知道是什么意思,所以在这里我就不多说了,如果不懂的话可以参考这篇博客。 接下来是content的源码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.weily.mutour.activity.WebActivity"
    tools:showIn="@layout/activity_web">

    <WebView
        android:id="@+id/web"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</android.support.v4.widget.NestedScrollView>

前几天我在研究为什么SnackBar没有动画效果的时候(具体可参考我的上一篇博客),也顺便纠结了一下为什么我写的登陆界面不能在弹出键盘的时候自动伸缩,没有特意去找原因,猜测应该是布局中的控件的原因,同样的属性,同样的父布局,但是子布局中有RecyclerView的时候就能够自动伸缩,登陆界面没有RecyclerView,同时也没有其他的可伸缩的布局(比如NestedScrollView,ListView等),或许是因为这个原因。 这个方法的来源是这篇博客:Toolbar在WebView滚动下的显示和隐藏的实现 而这篇博客中的代码将WebView放在了LinearLayout中,LinearLayout的父布局是v4包中AppBarLayout,如果在IDE中直接添加这个控件不会报错,但是会报一个警告。

看起来是因为这个控件的原因,博客的评论中也提到这个包找不到。 同时评论中有人提到将AppBarLayout换成NestedScrollView就行了,然后删掉LinearLayout。 当然,这也是我的处理办法。 写完之后就能够在滑动WebView的时候实现ToolBar下滑隐藏,上滑显示的效果了。


WebView右滑后退,左滑前进

本来上面的代码就能够满足要求了,但是我在调试的时候发现一个问题,由于我调试嵌套的并不是一个单独的网页,网页中还有其他的链接,点击也能够正常跳转,但是却存在无法后退的问题,或许是没有找到。 为了解决这个问题,我首先想到的方法是使用手势。 经常我们在使用一些常用app的时候,能够通过手势执行一些快速操作,比如说Telegram在聊天界面往右滑会有一个动画效果,同时界面回到聊天列表。 所以说手势的使用还是挺普遍的。

监听WebView触摸事件

看到这个标题,我想一定能很快写出监听的代码

@Override
    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                start = event.getX();
                break;
            case MotionEvent.ACTION_UP:
                float end = event.getX();
                float distance = end - start;
                if (distance > 400)
                {
                    webView.goBack();
                    break;
                }
                if (distance < -400)
                {
                    webView.goForward();
                    break;
                }
                break;
        }
        return super.onTouchEvent(event);
    }

完全正确的代码,但我调试的时候却发现了问题,无论我怎么滑动都没有反应,然后我使用log发现这个方法根本没有执行,所以无效。 google了一下原因,得到这么一个答案:

一般我们用于接收GestureDetector对象的方法是OnTouchevent();,而在View组件占用了屏幕空间之后,这个方法就无效了,只有换成 dispatchTouchEvent方法才有效!

原文链接:Webview中无法触发手势方法(ontouchevent,onfling...)的解决方法 于是将onTouchEvent换成dispatchTouchEvent,完美解决。


说在后面

这两天我算是真正体会到了Google的强大,比如说上一篇博客中的SnackBar没有动画,我使用google直接搜索“SnackBar 无动画”,并没有什么有价值的文章,甚至完全没有一个像我一样的问题,然后试着使用英语搜索“snackbar no animation”,才找到大量的解决方法。至于百度嘛,我用中文还是英语,搜索出来的结果并没有什么区别,当然,也和我用中文google差不多的样子。


参考列表

如何监听webview的滚动事件 Android WebView的前进、后退、与刷新 Webview中无法触发手势方法(ontouchevent,onfling...)的解决方法 Toolbar在WebView滚动下的显示和隐藏的实现 可折叠的Toolbar—CollapsingToolbarLayout