【问题标题】:Is there a listener for when the WebView displays it's content?WebView 显示其内容时是否有监听器?
【发布时间】:2011-05-03 04:23:11
【问题描述】:

使用 WebViewClient 和/或 WebChromeClient 您可以获得页面加载时间的侦听器,但有时会在 WebView 包含任何内容之前调用它。

确定 WebView 何时显示其内容的有效方法是什么?

编辑:(试图更清楚)

当我在 WebView 中加载页面时,我想将滚动设置到特定位置。在页面加载并且它具有实际内容高度之前,似乎无法设置滚动位置。因此,我尝试了两种不同的方法来确定页面何时完成加载,来自 WebViewClient 的 onPageFinished() 和来自 WebChromeClient 的 onProgressChanged()。这两个都告诉我页面何时完成加载。

但是,问题是有时在页面显示之前调用它,因此页面没有高度,滚动调用什么也不做。

我正在尝试找到一种可靠的方法来确定页面何时可以滚动,即。当它有它的内容高度时。

我想我可以在它完成加载后设置一个检查循环,以继续寻找高度可用的时间,但这看起来很像 hack。希望有更清洁的方法。

【问题讨论】:

标签: android events webview listener


【解决方案1】:

我成功使用Richard's answer with a PictureListener 几年了,但我不再推荐此作为最佳解决方案。

这有两个原因:

  1. webView.setPictureListenerPictureListener 均已弃用。
  2. 使用此侦听器会导致 WebView 经常分配新图片。这种分配代价高昂,可能会对性能产生重大影响,甚至会导致 JellyBean MR1 上的本机崩溃。

相反,我建议创建 WebView 的子类并像这样覆盖 invalidate():

@Override
public void invalidate() {
    super.invalidate();

    if (getContentHeight() > 0) {
        // WebView has displayed some content and is scrollable.
    }
}

如果您仍然想使用 PictureListener 方法,则在完成后将PictureListener 设置回 null 将获得更好的性能。

【讨论】:

  • 这是我找到的最佳解决方案。但是,在某些情况下,更新 WebView 中的 WebView 内容时不会调用 invalidate()。一个不寻常的情况是,当调用 loadDataWithBaseURL 时先前的内容位于顶部,因此我还在调用 loadDataWithBaseURL 后添加了延迟调用以使无效。
  • 确实是最好的解决方案。添加getProgress() == 100 帮助我确保内容完全呈现。
  • 我知道这有点晚了,但是这里的新手:我将上述代码放在哪里?无论我把它放在哪里,Android 控制台都说无法识别。
  • @user4951834 创建 WebView 的子类并将其放入其中。然后使用您的自定义 WebView 类而不是原始类。 WebView 有一个名为 invalidate() 的方法,这将覆盖其原始实现。
  • @Fizzix 如果内容高度 > 0,则 webview 已显示内容。所以在 if 语句里面代表了当它发生时你想做的任何事情。
【解决方案2】:

编辑:自从我第一次发布此方法以来,此方法已在 Android API 12 中弃用。感谢 Google!我会保留原来的答案:


是的——有一个监听器可以知道内容何时完成加载。我必须在我的项目上创建一个,这样我就可以制作一个启动画面,它会一直保持到 webview 加载页面。

它叫做 .setPictureListener。

例如:

mWebView.setPictureListener(new MyPictureListener());
//... and then later on....
class MyPictureListener implements PictureListener {

    @Override
    public void onNewPicture(WebView view, Picture arg1) {
      // put code here that needs to run when the page has finished loading and
      // a new "picture" is on the webview.      
    }    
} 

【讨论】:

  • 太棒了。这完全符合我的预期。警告其他人使用它...当您在页面中移动时,它确实会经常被调用,因此请确保 onNewPicture 方法中发生的任何事情都很快。
  • @Paul:我的理解是当 webview 更新它的“图片”,即它在屏幕上显示的内容时,这个监听器被触发,不管它显示的是什么.但是,如果您确实需要在页面上至少有一个图像,您总是可以在某个地方添加一个简单的纯色 .gif 或 .jpg ,它会融入背景并且对用户不可见,但会导致这种情况听众开火。但我无法确认是否需要,因为我从未测试过仅包含文本的 web 视图。其他人可以回复澄清吗?
  • 不,我弄错了。 WebKit 文档所指的“图片”是浏览器内容的呈现。我最初对为什么上述技术对我不起作用感到困惑,因为除非渲染实际出现在屏幕上,否则不会触发事件。
  • @Paul:我明白了,虽然我自己使用这种技术来知道什么时候让启动屏幕消失并显示 web 视图(我希望它在加载内容后显示)。在此过程中,webview 的可见性设置为“不可见”。所以我认为它实际上不需要在手机屏幕上触发。如果你的 webview 设置为“gone”,也许这就是问题所在?
  • 也许这就是问题所在。有机会我会检查是否是这种情况。
【解决方案3】:

这里建议的解决方案充其量只是hacky,而有一个非常干净的解决方案in accordance with the Android docs

WebViewClient client = new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url) {
        Log.d("MYAPP", "Page loaded");
    }
};
webView.setWebViewClient(client);

【讨论】:

  • 我希望这是答案,但根据我的经验,onPageFinished 可以而且在很多时候都可以在内容显示/可滚动/可测量之前调用。所以如果你尝试在 onPageFinished 方法中滚动,内容高度可能仍然为 0。
  • 啊,对。文档实际上建议使用onNewPicture(WebView, Picture),但根据我的经验,它并不总是会触发。
  • 后续:onNewPicture 仅在 WebView 可见并显示在设备边界内时才会触发。因此,如果您想在显示 WebView 之前使用它来计算尺寸,这似乎无济于事。
  • onNewPicture(WebView, Picture) 很久以前(现在)已被弃用。
【解决方案4】:

我阅读了这个讨论,我也面临同样的问题。

我搜索了很多,但它们不是他们承诺的解决方案。

而且我不想使用已弃用的 `onNewPicture` 回调。

在我自己的情况下,我只需要在activity启动时恢复`WebView`滚动位置,我认为这是大多数情况,因为当activity任务堆栈进入后台时,Android会自动保存`WebView`状态,所以当它弹出到前台时,它看起来是一样的。如果它在后台被杀死,我相信你会设法将状态保存在 Activity 生命周期方法中,如 `onPause`。

所以,我没有扩展 `WebView` 并覆盖等等等等,而是使用 Handler 和 post 模型。

private final Handler mHandler = new Handler();
// in onCreate
mHandler.postDelayed(new Runnable() {

                @Override
                public void run() {
                    if (mWebView.getContentHeight() > 0) {
                        mWebView.scrollTo(0, mLastPosition);
                        Log.d("scrolling", "true");
                        mHandler.removeCallbacks(this);
                    } else {
                        mHandler.postDelayed(this, 100);
                    }
                }
            }, 100);

此代码块将定期检查 WebView 页面是否可以操作,如果可以,则执行滚动并删除回调,否则循环。 我对此进行了测试,发现通常不会花费很长时间。

希望这会有所帮助!

【讨论】:

  • 我尝试了 WebViewClient.onPageFinished、WebChromeClient.onProgressChanged,甚至是已弃用的 PictureListener.onNewPicture,它们都不可靠,但这种方法效果很好。但是,如果您需要清除 WebView 内容,将 URL 设置为 about:blank 不会像您预期的那样将 contentHeight 返回为 0。我能找到清除内容并知道 WebView 何时准备好接受新内容的唯一方法是创建一个新的 WebView 对象。
  • 这适用于我的情况,但不是mWebView.getContentHeight() > 0,而是我计算了设备屏幕的高度。只是看看有没有选择。
  • 这是唯一适用于我的设备,我在自己构建的页面之间导航,并在导航到已访问的页面时保留滚动位置。与@sandalone 类似,我在离开页面之前记录了页面的 contentHeight (WebView.getContentHeight),然后我运行类似于上面的代码以仅在 getContentHeight 返回的结果与存储的值匹配时滚动到保存的位置.我在 onPageFinished 中运行它。
【解决方案5】:

我创建了一个自定义视图扩展 webview,它覆盖了“onSizeChanged”方法。 onSizeChanged 发生在 webview 加载其中的所有内容之后。

【讨论】:

  • 这在第一次加载页面时完美运行,但在加载第一个页面后不再调用它,因此如果用户将移动到其他页面,它就不起作用。
【解决方案6】:

编辑:我不再推荐这个解决方案,但为了提供信息,我将把它留在这里。

如果这对其他人有帮助,我最终这样做的方法是继承 WebView 并覆盖 computeVeritcalScrollExtent() 方法。

@Override
protected int computeVerticalScrollExtent() {
    // do you checking here

    return super.computeVerticalScrollExtent();
}

只要 WebView 计算其垂直滚动条,就会调用它。但是,第一次调用getContentHeight() > 0的地方,那就是第一次显示内容。

【讨论】:

    【解决方案7】:

    这不是问题的直接答案,但您也可以使用 WebChromeClient。我发现它对我来说更可靠,并且我已将其配置为显示加载进度。在页面完全加载之前,我在 WebView 隐藏 ProgressBar 时遇到了同样的问题,我用 WebView 客户端替换了 WebView。

    我使用下面的代码

    final WebView wvMax = (WebView) findViewById(R.id.webViewTnSmax);
    final ProgressBar pbMax = (ProgressBar) findViewById(R.id.progressBarTnSmax);
    
    wvMax.setWebChromeClient(new WebChromeClient() {
        public void onProgressChanged(WebView view, int progress) {
            pbMax.setVisibility(View.VISIBLE);
            pbMax.setProgress(progress);
            if (progress == 100) {
                pbMax.setVisibility(View.GONE); // Make the bar disappear after URL is loaded
            }
        }
    
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            // do something
        }
    });
    

    【讨论】:

      【解决方案8】:

      我知道它的旧但它可以帮助,这里的答案并不完美,我发现最好的是覆盖扩展 WebView 上的 onDraw

        @Override
              protected void onDraw(Canvas canvas) {
          //do your math/stuff/magic/blackmagic/hackish here
      
                  super.onDraw(canvas);
              }
      

      所以每次向下/向上滚动 每个 fontZoomChange

      几乎所有东西(没有测试所有)都会调用 onDraw

      我使用它来将 SeekBar 用作垂直滚动条

      ;)

      检查是否为空很重要,如果为避免无用的方法调用,请添加一些其他的

      【讨论】:

        【解决方案9】:

        我已经解决了这个问题,创建了一个覆盖 onDraw 方法并调用我自己的侦听器的自定义视图。这是代码:

        public class CustomWebView extends WebView{
        
        public interface FinishedDrawListener{
            void onDrawFinished(WebView view, String url);
        }
        
        private FinishedDrawListener drawListener;
        
        public CustomWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
        
        public CustomWebView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
        
        public CustomWebView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        
        public CustomWebView(Context context) {
            super(context);
        }
        
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if(drawListener != null){
                drawListener.onDrawFinished(this, this.getUrl());
            }
        }
        
        public void setOnFinishedDrawListener(FinishedDrawListener listener){
            this.drawListener = listener;
        }   
        

        我认为这不是最好的方法,但它适用于我的应用程序。

        【讨论】:

          【解决方案10】:
          onLoadResource(WebView view, String url)
          

          通知宿主应用程序 WebView 将加载给定 url 指定的资源。

          还不清楚这里的目标是什么,但你可以试试

          public void doUpdateVisitedHistory (WebView view, String url, boolean isReload)
          

          我会“假设”页面在添加到历史记录之前已经完成加载...

          【讨论】:

          • 感谢您的回复,但这不只是告诉应用程序何时将要开始加载特定资源,而不是在它刚刚显示时? onLoadResource 为 on 页面上的每个资源调用,这些都在 onPageFinished 之前调用。我正在寻找的是在 onPageFinished 之后调用的东西,在页面实际显示给用户之后。
          • 您应该更新问题以使其更清楚...也许有人会给您更合适的答案?
          • 抱歉,由于某种原因,我没有看到您回复。谢谢你的耐心。我编辑了我的问题以使其更清楚。另外,我尝试了 doUpdateVisitedHistory() 想法,它在页面加载时被调用,因此它不起作用。再次感谢。
          【解决方案11】:

          它在所有设备上都可以正常工作:- Load Web View ProgressDialog

          【讨论】:

          • 这不起作用。 onPageFinished 中的高度可能为 null。
          • 只有当您对网页数据已加载这一事实感兴趣时,此方法才适用。它与绘制/绘制数据无关。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-12-22
          • 2011-01-14
          • 2014-02-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多