WebView如何使用loadDataWithBaseUrl跳转到锚点?

8

我的Android应用程序使用WebView来显示一堆我“即时生成”的HTML代码。以下代码是用于加载HTML代码的:

    StringBuilder builder = new StringBuilder();

    // HTML
    builder.append("<html><head><link rel=\"stylesheet\" href=\"file:///android_asset/style.css\" type=\"text/css\">");
    builder.append("</link></head><body>");
    builder.append(getThreadBody());
    builder.append("</body></html>");

    webview.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "utf-8", null);

这一切都运行得非常顺利。请注意,我没有加载实际的HTML文件,而只是创建了一个代表某些(希望有效的)HTML的字符串,并将其加载到WebView中。
无论如何,我生成的HTML(在“getThreadBody”方法中的部分)包含命名锚点,例如像这样;
<div>
    <a name="949823">Anchor 1</a>
    <div>All kinds of stuff</div>

    <a name="895984">Anchor 2</a>
    <div>...</div>
</div>

现在在某些情况下,我希望WebView在加载HTML时立即导航到其中一个锚点。据我所知,WebView支持导航(在这种情况下为滚动)到命名锚点,但问题在于没有人点击任何超链接来访问这些锚点,我希望WebView在加载时滚动(如果在加载HTML和滚动之间有短暂的延迟也没问题,我的重点是它不需要用户交互)。
我认为可以通过使用WebView的loadUrl方法并提供一个带有锚点的URL来实现此行为,例如:
webview.loadUrl("file:///android_asset/page.html#895984", ...)

然而,由于我没有将HTML保存到任何文件中,因此我无法使用这种方法……当然,将HTML保存到临时文件可能是一种解决方法,但我希望将其保留为最后的选择,肯定有更简单的方法吧?
我该如何实现呢?谢谢!
已解决 以下是可行的代码。我发现需要短暂延迟来让页面加载,然后再执行javascript,否则成功与否是有点50/50的……
    StringBuilder builder = new StringBuilder();

    // HTML
    builder.append("<html><head><link rel=\"stylesheet\" href=\"file:///android_asset/style.css\" type=\"text/css\">");
    builder.append("</link>");
    builder.append("<script>");
    builder.append("function scrollAnchor(id) {");
    builder.append("window.location.hash = id;}");
    builder.append("</script>");
    builder.append("</head><body>");
    builder.append(getThreadBody());
    builder.append("</body></html>");

    webContents.loadDataWithBaseURL("file:///android_asset/", builder.toString(), "text/html", "utf-8", null);

    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            String id = "895884";
            webContents.loadUrl("javascript:scrollAnchor(" + id + ");");
        }
        }
    }, 250);
5个回答

6
如何通过JavaScript控制它?我没有尝试过,但这可能是一个线索。
builder.append(getThreadBody());
builder.append("<script>window.location.hash="949823";</script>");
builder.append("</body></html>");

请记得为WebView启用JavaScript。

----附加答案----

我看到你使用TimerTask来加载JavaScript,这样可以工作,但我认为还有另一种更好的方法。WebView有一个名为onPageFinished的回调函数,在WebView加载网页完成时会触发该函数。您可以在此处注入您的JS代码。

webContents.setWebViewClient(new WebViewClient(){

    @Override
    public void onPageFinished(WebView view, String url) {
          String id = "895884";
          webContents.loadUrl("javascript:scrollAnchor(" + id + ");");
    }

});

希望这对你有用!

只需注意,使用此功能时,Webview 的 URL 不会更改。webview.getUrl() 返回加载内容时使用的相同 URL。 - Codebeat
你也可以使用JavaScript来处理hashchange事件,方法是使用hashchange事件:$j(window).bind('hashchange', function(e) { 在这里执行你的操作 } - Codebeat
@wayne_bai 我使用了你的答案,它可以工作,但是它会跳回到顶部。有什么想法可能出了问题吗? - makkhay gurung

6
首先,您不需要使用JavaScript。如果您使用以下标签加载内容:

webContents.loadDataWithBaseURL("some://dummy/url",...);

您可以使用锚点简单地滚动到指定位置。

webContents.loadUrl("some://dummy/url#anchor");

其次,在onPageFinished中这样做而没有额外的控制会导致无限循环!当loadUrl完成时,onPageFinished会一遍又一遍地调用。

1
这对我没用... 当我运行"webContents.loadUrl("some_dummy_url#anchor");"时,我得到一个空白页... 有什么想法吗? - JannGabriel
我做了:.loadDataWithBaseURL("app:html", ...);。就像他说的,只是一些虚拟的URL。 - 476rick
这段代码是优胜者,因为它避免了过多的JavaScript,同时也没有将标签居中;它试图将其置于顶部。在WebView中,使用scrollTo()和数学计算来居中元素也行不通。 - Phlip

1
如果您有外部操作(例如:onclick事件),请尝试此代码。
 public static void scrollToAnchor(String id) {
        sWebView.loadUrl("javascript:document.getElementById(\"" + id + "\").scrollIntoView()");
 }

1
如果您在布局中将webview放在nestedScrollView内部,嵌套会导致滚动事件无法正常工作。一旦您将其移出,它将在不需要重新加载或添加JavaScript的情况下正常工作。
此外,如果您想保留nestedScrollView并且不介意用户无法滚动到该部分,可以将fillViewPort=true添加到nestedScrollView中,并将android:layout_height="wrap_content"添加到webview中。
它会看起来像这样:
<androidx.coordinatorlayout.widget.CoordinatorLayout>

  <com.google.android.material.appbar.AppBarLayout>
  ...
  </com.google.android.material.appbar.AppBarLayout>

  <androidx.core.widget.NestedScrollView
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:fillViewPort=true
   app:layout_behavior="@string/appbar_scrolling_view_behavior">

     <WebView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/> 

  </androidx.core.widget.NestedScrollView>


</androidx.coordinatorlayout.widget.CoordinatorLayout>

0

如果你从 assets 加载 HTML:

 webView.loadUrl("file:///android_asset/htmls/mypage.html#anchor");

你可以跳转到网页内的锚点:

webView.loadUrl("http://www.stackoverflow.com/mypage.html#anchor");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接