之前我们介绍了如何获取 WebView 选中的文本,今天讲讲如何获取选区的位置。

与获取选中文本类似,WebView 也没有提供相应的方法,所以同样需要通过 JS 辅助。

为了方便,可以直接在『Android 获取 WebView 选中文本』一文中的例子上直接改造。

首先是 JS 函数:

object JsFunction {
    private val GET_SELECTION = """
        javascript:(function getSelectedText() {
            var txt;
            var top = 0, bottom = 0, left = 0, right = 0;
            if (window.getSelection) {
                var sel = window.getSelection();
                txt = sel.toString();
                var range = sel.getRangeAt(0);
                var rect = range.getBoundingClientRect();
                top = rect.top;
                bottom = rect.bottom;
                left = rect.left;
                right = rect.right;
            } else if (window.document.getSelection) {
                var sel = window.document.getSelection();
                txt = sel.toString();
                var range = sel.getRangeAt(0);
                var rect = range.getBoundingClientRect();
                top = rect.top;
                bottom = rect.bottom;
                left = rect.left;
                right = rect.right;
            } else if (window.document.selection) {
                var range = window.document.selection.createRange();
                txt = range.text;
                var rect = range.getBoundingClientRect();
                top = rect.top;
                bottom = rect.bottom;
                left = rect.left;
                right = rect.right;
            }
            var json = {};
            json.selection = txt;
            json.left = left;
            json.top = top;
            json.right = right;
            json.bottom = bottom;
            json.windowWidth = window.innerWidth;
            json.windowHeight = window.innerHeight
            return json;
        })()
    """.trimIndent()
}

在之前的函数上,我们增加了选区上下左右四个方向的边距,用于计算选区的位置,大概思路就是获取到选区的 Rect,然后再获取其坐标,思路与 Android 是类似的。

需要注意的是,JS 只能获取到其相对于网页窗口的坐标,与 Android 端的坐标并不是一个概念,所以同样还要返回窗口的大小用于换算。

返回的内容比较多,我们将其封装成 JSON 返回。

然后定义一个数据类用于解析返回的数据:

object JsFunction {
    ...
    data class WebViewSelection(
        val selection: String,
        val left: Double,
        val top: Double,
        val right: Double,
        val bottom: Double,
        val windowWidth: Double,
        val windowHeight: Double
    )
}

解析数据可以用 Gson 或其他工具封装一下:

object JsFunction {
    ...
    fun getSelection(webView: WebView, callback: (WebViewSelection?) -> Unit) {
        webView.evaluateJavascript(GET_SELECTION) {
            callback.invoke(Gson().fromJson(it, WebViewSelection::class.java))
        }
    }
}

WebView 这边执行:

class MyWebView(context: Context, attrs: AttributeSet? = null) : WebView(context, attrs) {
    ...
    private fun getWebSelection() {
        JsFunction.getSelection(this) {
            if (it == null) return@getSelection
            val left = (it.left / it.windowWidth * width).toInt()
            val right = (it.right / it.windowWidth * width).toInt()
            val top = (it.top / it.windowHeight * height).toInt()
            val bottom = (it.bottom / it.windowHeight * height).toInt()
            ...
        }
    }
}

因为坐标不一致,所以要重新计算坐标,简单的比例乘除问题,不用过多解释。

接下来就可以按照自己的需求做相应的逻辑了。