Android开发

WebView基本用法一

2020-06-30  本文已影响0人  唐_夏影

Android常用一个来显示网页的控件WebView

现在Android应用层开发的方向有两种,原生的客户端开发和HTML移动端+原生混合开发(hybridApp)

例如我们的微信小程序就是小程序端由小程序开发者开发,再加上微信这一个外层原生应用组合而成的hybridApp

小程序可以通过微信统一提供的接口来实现原生应用才能实现的读取手机联系人,调用相机等功能。我们自己也可以开发一个类似微信这样的应用,提供约定好的接口供H5网页调用

加载资源


1 > 加载网页

添加联网权限:

<uses-permission android:name="android.permission.INTERNET"/>

添加控件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".simple.test1.Demo1Activity">

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

加载网页

/**
 * WebView的简单使用
 * 1,加载网页
 */
class Demo1Activity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo1)
        mWebView.loadUrl("https://developer.android.google.cn/topic/libraries/data-binding/start")
    }
}

加载完成

加载网络Url.png
2 > 加载其他资源

加载Assets下的离线html文件

在main文件夹下创建assets/index.html

加载assets下的资源.png

index.html下的代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
<p>你好,世界</p>
</body>
</html>

最后加载离线网页

   mWebView?.loadUrl("file:///android_asset/simple.html")//加载网络url

加载assets下的jpg文件

加载网络图片.png
mWebView.loadUrl("file:///android_asset/example.jpg")

如果图片太大没办法显示完全,这个时候拖动界面可以查看图片的各个部分

完全显示图片.png

如果想要图片刚好在屏幕中显示完全,需要设置缩放图片

        mWebView.settings.useWideViewPort = true    //让WebView支持“viewport HTML元标记
        mWebView.settings.loadWithOverviewMode = true  //设置以概述模式加载页
        mWebView.loadUrl("file:///android_asset/example.jpg")

两个属性缺一不可

加载assets下的音乐文件

mWebView.loadUrl("file:///android_asset/music.mp3")
music.png

加载txt文件

mWebView.loadUrl("file:///android_asset/string.txt")
Screenshot_2020-06-29-17-58-44.png

WebView无法加载office形式的文本文件,如果需要查看这些文件,可以使用腾讯的X5WebView

相关方法


常于WebView类相关联的还有以下三个类

WebChromeClient:辅助WebView处理JavaScript的对话框,网站图标,网站title,加载进度

WebViewClient:监听WebView的各种事件和生命周期

WebSettings:进行相关的设置,比如允许Js脚本运行

1 > WebSettings

先来看一下WebSettings的这个允许Js脚本运行是什么意思,举一个例子,创建一个Interval.html文件

<html>
<head>
    <script type="text/javascript">
        var c=0;
        var t;
        function timedCount()
        {
            document.getElementById('txt').value=c;
            c=c+1;
            t=setTimeout("timedCount()",1000)
        }
    </script>
</head>

<body>
<form>
    <input type="text" id="txt"  onClick="timedCount()">
</form>
</body>

</html>
 

这是一个简单的js程序代码,在网页浏览器运行后,点击文本输入框,就会不断的从0开始+1,每1秒叠加文本框中的内容。

我们把这段离线html文件放到我们的Android程序中运行试一下

class DemoActivity : AppCompatActivity() {
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo2)
        mWebView?.loadUrl("file:///android_asset/interval.html")//加载网络url
    }
}

我们会发现我们点击文本框后并没有反应,这是因为WebView默认把JavaScript给禁止掉了,相当于<script></script>这段代码压根就没起作用,自然就不会有效果了,现在修改一下代码

class DemoActivity : AppCompatActivity() {
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo2)
        //让WebView支持javaScript脚步语言
        val webSettings: WebSettings ?= mWebView?.settings
        webSettings?.javaScriptEnabled = true
        mWebView?.loadUrl("file:///android_asset/interval.html")//加载网络url
    }
}

这下程序就可以正常运行了

2 > WebViewClient

负责监听WebView的各种事件和生命周期,先来看以下代码

class DemoActivity : AppCompatActivity() {

    var TAG = "Demo3Activity"
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo3)
        val setting = mWebView.settings
        //设置与Js交互的权限
        setting.javaScriptEnabled = true
        //设置允许Js弹窗
        setting.javaScriptCanOpenWindowsAutomatically = true
        //先载入Js代码
        mWebView.loadUrl("https://developer.android.google.cn/topic/libraries/data-binding/start")
        mWebView.webViewClient = object : WebViewClient() {

            //主程序网页开始加载
            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                super.onPageStarted(view, url, favicon)
                Log.d(TAG, "主程序网页开始加载")
            }

            //网页加载完毕
            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                Log.d(TAG, "网页加载完毕")
            }

            //即将加载的Url资源
            override fun onLoadResource(view: WebView?, url: String?) {
                super.onLoadResource(view, url)
                Log.d(TAG, "onLoadResource:$url")
            }
        }
    }
}

加载网页后的日志情况(省略掉中间部分日志)如下

06-29 20:21:26.075 4303-4303/com.example.webview D: onLoadResource:https://developer.android.google.cn/topic/libraries/data-binding/start
06-29 20:21:27.803 4303-4303/com.example.webview D: 主程序网页开始加载
06-29 20:21:27.807 4303-4303/com.example.webview D: onLoadResource:https://fonts.googleapis.com/css?family=Roboto:300,400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700|Material+Icons
06-29 20:21:27.809 4303-4303/com.example.webview D: onLoadResource:https://www.gstatic.cn/devrel-devsite/prod/vc0d10ef7c6e8aac6c71e2a2051f66f30f3c99a4b52237746839ce4f1fae2b7b4/android/images/lockup.svg
06-29 20:21:32.895 4303-4303/com.example.webview D: 网页加载完毕
06-29 20:21:32.907 4303-4303/com.example.webview D: onLoadResource:https://www.gstatic.cn/devrel-devsite/prod/vc0d10ef7c6e8aac6c71e2a2051f66f30f3c99a4b52237746839ce4f1fae2b7b4/android/images/favicon.png

从日志中可以看出onLoadResource只要是网页需要加载资源,就会被回调,而onPageStarted和onPageFinished则对应网页加载的开始和结束

它还可以帮助我们拦截要跳转的链接,例如我们在查看一个文章的列表,在需要查看文章的具体信息时点击进入,此时应该是要跳转到一个新的链接,这个时候会回调下列方法

    @SuppressLint("NewApi")
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun shouldOverrideUrlLoading(iew: WebView?,request: WebResourceRequest?): Boolean {
          val url = request?.url
          Log.d(TAG, "shouldOverrideUrlLoading:url:$url")
          return false
    }

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
          Log.d(TAG, "shouldOverrideUrlLoading:"+url)
          return false
   }

网上很多人都说方法是1API21(5.0)以后引入的的方法,方法2是是API21以前的方法。但实际测试中发现,在API24及以上回调的才是方法1,其他级别回调的才是方法2

另外一个可以证明的一点是假如你的应用的目标Android版本小于API24,且v6,v4包都是24以下的话,方法1就不存在了

有一种特殊情况是在加载网页的时候,网页自己做了重定向也会回调该方法,用简书来做例子

class DemoActivity : AppCompatActivity() {

    var TAG = "Demo3Activity"
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo3)
        val setting = mWebView.settings
        //设置与Js交互的权限
        setting.javaScriptEnabled = true
        //设置允许Js弹窗
        setting.javaScriptCanOpenWindowsAutomatically = true
        //先载入Js代码
        mWebView.loadUrl("https://www.jianshu.com/p/131e6f0144b3/")
        mWebView.webViewClient = object : WebViewClient() {
            @SuppressLint("NewApi")
            @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
            override fun shouldOverrideUrlLoading(
                view: WebView?,
                request: WebResourceRequest?
            ): Boolean {
                val url = request?.url
                Log.d(TAG, "shouldOverrideUrlLoading:url:$url"
                return false
            }

            override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
                Log.d(TAG, "shouldOverrideUrlLoading:$url")
                return false
            }
        }
    }
}

查看日志

 D/Demo3Activity: shouldOverrideUrlLoading:url:jianshu://notes/131e6f0144b3/

日志可以看出我们访问后简书给我们重定向到了一个奇怪的地址,因为WebView只能加载http或者https的网页,因此页面显示也是异常的

加载简书网页.png

害,这个时候我们只要让他如果返回了奇怪的地址我们就返回true,不让页面重新加载就可以了

    @SuppressLint("NewApi")
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
            val url = request?.url
            Log.d(TAG, "shouldOverrideUrlLoading:url:$url")
            if(request?.url.toString().startsWith("http")){
                  return false
            }
            return true
    }

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
             Log.d(TAG, "shouldOverrideUrlLoading:url:$url")
             if(url.toString().startsWith("http")){
                    return false
             }
            return true
   }

另外要注意的是 WebViewClient 就算你不需要用到里面的方法,也必须进行设置,不然在跳转到新链接时默认为跳转到系统浏览器

mWebView.webViewClient = WebViewClient()

需求:

现在需要你在页面加载完成的时候,获取网页的cookie,然后进行新cookie的设置


@Suppress("DEPRECATION")
class DemoActivity : AppCompatActivity() {
    var TAG = "Demo2Activity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo6)
        val setting = mWebView.settings
        //设置与Js交互的权限
        setting.javaScriptEnabled = true
        //设置允许Js弹窗
        setting.javaScriptCanOpenWindowsAutomatically = true
        //设置新的cookie,设置cookie要在loadrl之前
        CookieSyncManager.createInstance(this@Demo2Activity)
        val cookieManager =
            CookieManager.getInstance()
        cookieManager.setAcceptCookie(true)
        cookieManager.setCookie("https://xiaozhuanlan.com/", "新cookie") 
        CookieSyncManager.getInstance().sync()
        //先载入Js代码
        mWebView.loadUrl("https://xiaozhuanlan.com/")//加载网络url
        mWebView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                //获取网站的cookie
                val cookieManager=CookieManager.getInstance()
                val cookieStr=cookieManager.getCookie(url)
                Log.d(TAG, "cookieStr:$cookieStr")
            }
        }
    }
}
3 > WebChromeClient

辅助WebView处理JavaScript的对话框,网站图标,网站title,加载进度

1> 获取网站图标,获取网站title,获取当前网页加载进度

class DemoActivity : AppCompatActivity() {
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo4)
        val setting = mWebView.settings
        //设置与Js交互的权限
        setting.javaScriptEnabled = true
        //设置允许Js弹窗
        setting.javaScriptCanOpenWindowsAutomatically = true
        mWebView.loadUrl("https://xiaozhuanlan.com/")
        //加载网络url
        mWebView.webChromeClient = object : WebChromeClient() {
            //获取加载网页进度
            override fun onProgressChanged(view: WebView?, newProgress: Int) {
                super.onProgressChanged(view, newProgress)
                Log.d("Demo4Activity", newProgress.toString())
            }
            //获取标题
            override fun onReceivedTitle(view: WebView?, title: String?) {
                super.onReceivedTitle(view, title)
                Log.d("Demo4Activity:", "该网站的标题是:$title")
            }
            //拦截网页的图标
            override fun onReceivedIcon(view: WebView?, icon: Bitmap?) {
                super.onReceivedIcon(view, icon)
                mImageView.setImageBitmap(icon)
            }
        }
    }
}

运行程序查看结果

WebChormeClient.png

查看日志

4458-4458 D: 10
4458-4458 D: 21
4458-4458 D: 该网站的标题是:小专栏 - 专业人士的创作知识社区
4458-4458 D: 46
4458-4458 D: 68
4458-4458 D: 80
4458-4458 D: 100
4458-4458 D: 100

2>处理Js的对话框

编写jsAlert.html文档

<html>
<head>
    <meta http-equiv = "Content-Type" content="text/html;charset=UTF-8">
    <title>测试Js的三种不同对话框</title>
    <script language="JavaScript">
            function alertFun()
            {
            alert("Alert警告对话框!");
            }
            function confirmFun()
            {
            if(confirm("访问百度?"))
            {
                 location.href = "http://www.baidu.com";}
                 else alert("取消访问!");
            }
            function promptFun()
            {
            var word = prompt("Prompt对话框","请输入点什么...:");
            if(word)
            {
                alert("你输入了:"+word)
            }else{
                 alert("呵呵,你什么都没写或者取消了!");}
            }
        </script>
</head>

<body>
<p>三种对话框的使用</p>

<p>Alert对话框</p>
<p>
    <input type="submit" name = "Submit1" value="展示1" onclick="alertFun()"/>
</p>
<p>Confirm对话框</p>
<p>
    <input type="submit" name = "Submit2" value="展示2" onclick="confirmFun()"/>
</p>
<p>Prompt对话框</p>
<p>
    <input type="submit" name = "Submit3" value="展示3" onclick="promptFun()"/>
</p>
</body>
</html>

创建一个新的Actiivty编写,先正常运行一下查看效果

class DemoActivity : AppCompatActivity() {
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main5)
        val setting = mWebView.settings
        //设置与Js交互的权限
        setting.javaScriptEnabled = true
        //设置允许Js弹窗
        setting.javaScriptCanOpenWindowsAutomatically = true
        mWebView?.loadUrl("file:///android_asset/jsAlert.html")//加载网络url
    }
}

然后我们重写三个方法,并返回true拦截三个弹窗

class DemoActivity : AppCompatActivity() {
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main5)
        val setting = mWebView.settings
        //设置与Js交互的权限
        setting.javaScriptEnabled = true
        //设置允许Js弹窗
        setting.javaScriptCanOpenWindowsAutomatically = true
        mWebView?.loadUrl("file:///android_asset/jsAlert.html")//加载网络url
        //拦截Js的三种弹窗
        mWebView.webChromeClient = object : WebChromeClient() {
            //Js弹窗
            override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?
            ): Boolean {
                return true
            }

            //Js确认框
            override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult?
            ): Boolean {
                return true
            }

            //Js弹窗
            override fun onJsPrompt(view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult?
            ): Boolean {
                return true
            }
        }
    }
}

这样我们就拦截了Js自己的弹窗,接着我们来实现原生的弹窗

class DemoActivity : AppCompatActivity() {
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main5)
        val setting = mWebView.settings
        //设置与Js交互的权限
        setting.javaScriptEnabled = true
        //设置允许Js弹窗
        setting.javaScriptCanOpenWindowsAutomatically = true
        mWebView?.loadUrl("file:///android_asset/jsAlert.html")//加载网络url
        //拦截Js的三种弹窗
        mWebView.webChromeClient = object : WebChromeClient() {
            //Js弹窗
            override fun onJsAlert(
                view: WebView?, url: String?, message: String?, result: JsResult?
            ): Boolean {
                onJsAlertJudge(message, result)
                return true
            }

            //Js确认框
            override fun onJsConfirm(
                view: WebView?, url: String?, message: String?, result: JsResult?
            ): Boolean {
                onJsConfirmJudge(message, result)
                return true
            }

            //Js弹窗
            override fun onJsPrompt(view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult?
            ): Boolean {
                onJsPromptConfig(message, defaultValue, result)
                return true
            }
        }
    }

 private fun onJsPromptConfig(message: String?, defaultValue: String?, result: JsPromptResult?) {
        val inflater = LayoutInflater.from(this@Demo5Activity)
        val myView = inflater.inflate(R.layout.prompt_view, null)
        val text = myView.findViewById<TextView>(R.id.text)
        val edit = myView.findViewById<EditText>(R.id.edit)
        text.text = message
        edit.setText(defaultValue)
        val dialog = android.app.AlertDialog.Builder(this)
        dialog.setView(myView)
        dialog.setPositiveButton("确定", DialogInterface.OnClickListener { dialog, which ->
            //获取输入的值
            val value = edit.text.toString()
            result?.confirm(value)
        })
        dialog.setNegativeButton("取消",DialogInterface.OnClickListener { dialog, which ->
            result?.cancel()
        })
        dialog.setCancelable(false)
        dialog.create().show()
    }


    private fun onJsConfirmJudge(message: String?, result: JsResult?) {
        val dialog = android.app.AlertDialog.Builder(this@Demo5Activity)
        dialog.setTitle("Alert")
        dialog.setMessage(message)
        dialog.setPositiveButton("确定", DialogInterface.OnClickListener { _, which ->
            result?.confirm()
        })
        dialog.setNegativeButton("取消", DialogInterface.OnClickListener { dialog, which ->
            result?.cancel()
        })
        dialog.setCancelable(false)
        dialog.create().show()
    }

    private fun onJsAlertJudge(message: String?, result: JsResult?) {
        val alert = AlertDialog.Builder(this)
        alert.setTitle("Alert")
        alert.setMessage(message)
        alert.setPositiveButton("确定", DialogInterface.OnClickListener { dialog, which ->
            result?.confirm() //通知H5已经处理完成
        })
        alert.setCancelable(false)
        alert.create().show()
    }
}

其中输入内容的输入框为prompt_view的代码如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:text="输入框提示文本"
        android:textColor="@color/colorPrimary"
        android:textStyle="bold"
        android:textSize="22sp"
        />

    <EditText
        android:id="@+id/edit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollHorizontally="true"
        android:selectAllOnFocus="true"
        tools:ignore="Autofill" />

</LinearLayout>

运行程序后可以观察到对话框已经都被替换为了原生的对话框

上一篇下一篇

猜你喜欢

热点阅读