内嵌式WebView中锚链接失效的解决方案

现在Hybrid App越来越多,因此WebView的使用率变得越来越高,这篇文章探讨一下网页中锚链接效果的问题。

一、锚的基本概念和用法

熟悉HTML网页的朋友对命名锚(named anchors)应该十分了解,它的实现也很简单,可以按如下语法定义一个锚:

<a name="tips">目标位置</a>

要跳转该锚的话只需要定义一个指向该锚的链接即可,如:

<a href="#tips">点击跳转到锚点的位置</a>

二、问题的由来

鉴于锚在网页中的实现非常简单,一般来说Web端可以直接实现该效果,而不需要Android客户端做Native实现,但这是有条件的:WebView具有特定高度,且小于网页的总高度。可以想象一下在PC浏览器上,打开一个很长的网页,当电脑一屏展示不了所有内容时,右侧则会出现一个滚动条,点击锚链接时会跳到网页的特定位置。在AndroidWebView上只要满足上述条件,不需本地实现也可达到锚的效果。

那么当不满足条件的时候,问题来了。往往我们不会固定WebView的高度,任其展示所有网页的内容,这样锚点链接点击就会失效。举一个具体的例子如下图:


假定布局最外层为ScrollView,内部从上到下为TitleBar,其他控件,WebView(图中蓝色背景部分,图片为WebView的部分内容),WebView下面还有其他控件,WebView没有设置具体高度,让其展示所有的网页内容。如果网页很长,滑动最外层的ScrollView就可以展示所有内容。如果网页上有锚点链接,比如上图的Button,原本打算点击Button,即跳转到WebView中图片的所在位置,但实际上无法达到锚链接效果的,因为网页已经都展开了。

三、解决方案

1.从设计上看,此种设计不尽合理,Android中嵌入式的WebView最好仅作为展示,轻量级的交互,尽可能不涉及WebView高度的运算,因为WebView上下都有其他控件,且WebView内网页的加载需要时间。从设计角度来解决的话方法很多,相信产品部门会有一百种方法让你活不下去。

2.产品部门说老板设计的,那就做呗。最简单的,WebView设置特定高度,这样可以让锚点起到效果,但是会导致WebView可以滑动,外边的ScrollView也可以滑动,从而导致滑动冲突的问题,此问题好解决,但这样视觉效果差,用户体验也比较差。

3.既然Web网页本身的实现无法生效了,那么只能靠Native端来处理了。原理就是,Native端监听WebView中的Button点击,然后将ScrollView滚动到合适的位置。感觉有点毁三观啊,本来网页上可以轻松处理的事情,偏偏要搞的这么复杂,都找不到一个合适的比喻来形容这件事情。要实现锚链接效果的话,那就不需要在Button上指定锚链接了,改为使用既定的交互协议(参考《关于WebView的一些用法探讨),当客户端点击Button时,监听协议,同时返回目标锚点位置到网页顶端的距离,如上图中的h2。客户端自身可以获得某个View在屏幕或父窗口中的绝对位置,因此可以得到h3TitleBarStatusBar的高度也可以获取,因此h1的距离也可以得到。

大致的流程如下:

(1)点击Button按钮,客户端监听到既定协议

(2)通过协议获得h2h2是网页中通过javascript来计算的

(3)客户端本地计算h1

(4)将ScrollView移动到(0, h1 + h2)的位置,以达到锚链接跳转的效果,具体实现为myScrollView.smoothScrollTo(0, h1 + h2)

下面说一下获取h3的方法,可以通过WebViewgetLocationInWindow(int[] position)得到view在父窗口中的x,y坐标,是以像素为单位的。还可以通过getLocationOnScreen(int[] position)来获取,这两个方法的区别是参照物不同,后者是以屏幕为参照的,具体对比可参考下图(此图具体作者不详):

内嵌式WebView中锚链接失效的解决方案_第1张图片

值得注意的是当Activity全屏显示时,上面两个方法得到的值是相同的,至于TitleBarStatusBar的高度就很容易计算了。另一点需要注意的是网页中JS获取的高度可能跟客户端获取的高度单位不一致,注意转换。

四、总结

随着混合式开发技术的发展,网页在移动客户端中的比重越来越大,因此Web端与Native端的交互、Web端的兼容等成为特别需要重视的问题,相关技术也需要我们掌握,同时需要了解更多Web开发的概念和技能,来做出更好的App



你可能感兴趣的:(android,webView,锚,锚链接)