package io.github.cawfree.webviewcallback;
/**
* Created by Alex Thomas (@Cawfree), 30/03/2017.
**/
import android.net.http.SslError;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.HashMap;
import java.util.Map;
/** An Activity demonstrating how to introduce a callback mechanism into Android's WebView. */
public class MainActivity extends AppCompatActivity {
/* Member Variables. */
private WebView mWebView;
private Map<String, Runnable> mCallbackMap;
/** Create the Activity. */
@Override protected final void onCreate(final Bundle pSavedInstanceState) {
// Initialize the parent definition.
super.onCreate(pSavedInstanceState);
// Set the Content View.
this.setContentView(R.layout.activity_main);
// Fetch the WebView.
this.mWebView = (WebView)this.findViewById(R.id.webView);
this.mCallbackMap = new HashMap<>();
// Define the custom WebClient. (Here I'm just suppressing security errors, since older Android devices struggle with TLS.)
this.getWebView().setWebViewClient(new WebViewClient() {
/** Handle when a request has been launched. */
@Override public final void onPageFinished(final WebView pWebView, final String pUrl) {
// Determine whether we're allowed to process the Runnable; if the page hadn't been redirected, or if we've finished redirection.
if(pUrl.equals(pWebView.getOriginalUrl())) {
// Fetch the Runnable for the OriginalUrl.
final Runnable lRunnable = getCallbackMap().get(pWebView.getOriginalUrl());
// Is it valid?
if(lRunnable != null) { lRunnable.run(); }
}
// Handle as usual.
super.onPageFinished(pWebView, pUrl);
}
/** Ensure we handle SSL state properly. */
@Override public final void onReceivedSslError(final WebView pWebView, final SslErrorHandler pSslErrorHandler, final SslError pSslError) { pSslErrorHandler.proceed(); }
});
// Assert that we wish to visit Zonal's website.
this.getWebView().loadUrl("http://www.zonal.co.uk/");
// Align a Callback for Zonal; this will be serviced once the page has loaded.
this.getCallbackMap().put("http://www.zonal.co.uk/", new Runnable() { @Override public void run() { /* Do something. */ } });
}
/* Getters. */
public final WebView getWebView() {
return this.mWebView;
}
private final Map<String, Runnable> getCallbackMap() {
return this.mCallbackMap;
}
}
15
如果要显示进度条,则需要监听进度更改事件,而不仅仅是完成页面:
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
//change your progress bar
}
});
private void setupWebViewClient() {
webView.setWebViewClient(new WebViewClient() {
private int running = 0; // Could be public if you want a timer to check.
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String urlNewString) {
running++;
webView.loadUrl(urlNewString);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
running = Math.max(running, 1); // First request move it to 1.
}
@Override
public void onPageFinished(WebView view, String url) {
if(--running == 0) { // just "running--;" if you add a timer.
// TODO: finished... if you want to fire a method.
}
}
});
}
6
这是一种利用Android's capability for JavaScript hooks加载URL时检测到的新方法 . 使用此模式,我们利用JavaScript 's knowledge of the document' s状态在Android运行时内生成本机方法调用 . 可以使用 @JavaScriptInterface 注释进行这些JavaScript可访问的调用 .
package io.github.cawfree.webviewcallback;
/**
* Created by Alex Thomas (@Cawfree), 30/03/2017.
**/
import android.net.http.SslError;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
/** An Activity demonstrating how to introduce a callback mechanism into Android's WebView. */
public class MainActivity extends AppCompatActivity {
/* Static Declarations. */
private static final String HOOK_JS = "Android";
private static final String URL_TEST = "http://www.zonal.co.uk/";
private static final String URL_PREPARE_WEBVIEW = "";
/* Member Variables. */
private WebView mWebView = null;
/** Create the Activity. */
@Override protected final void onCreate(final Bundle pSavedInstanceState) {
// Initialize the parent definition.
super.onCreate(pSavedInstanceState);
// Set the Content View.
this.setContentView(R.layout.activity_main);
// Fetch the WebView.
this.mWebView = (WebView)this.findViewById(R.id.webView);
// Enable JavaScript.
this.getWebView().getSettings().setJavaScriptEnabled(true);
// Define the custom WebClient. (Here I'm just suppressing security errors, since older Android devices struggle with TLS.)
this.getWebView().setWebViewClient(new WebViewClient() { @Override public final void onReceivedSslError(final WebView pWebView, final SslErrorHandler pSslErrorHandler, final SslError pSslError) { pSslErrorHandler.proceed(); } });
// Define the WebView JavaScript hook.
this.getWebView().addJavascriptInterface(this, MainActivity.HOOK_JS);
// Make this initial call to prepare JavaScript execution.
this.getWebView().loadUrl(MainActivity.URL_PREPARE_WEBVIEW);
}
/** When the Activity is Resumed. */
@Override protected final void onPostResume() {
// Handle as usual.
super.onPostResume();
// Load the URL as usual.
this.getWebView().loadUrl(MainActivity.URL_TEST);
// Use JavaScript to embed a hook to Android's MainActivity. (The onExportPageLoaded() function implements the callback, whilst we add some tests for the state of the WebPage so as to infer when to export the event.)
this.getWebView().loadUrl("javascript:" + "function onExportPageLoaded() { " + MainActivity.HOOK_JS + ".onPageLoaded(); }" + "if(document.readyState === 'complete') { onExportPageLoaded(); } else { window.addEventListener('onload', function () { onExportPageLoaded(); }, false); }");
}
/** Javascript-accessible callback for declaring when a page has loaded. */
@JavascriptInterface @SuppressWarnings("unused") public final void onPageLoaded() {
// Display the Message.
Toast.makeText(this, "Page has loaded!", Toast.LENGTH_SHORT).show();
}
/* Getters. */
public final WebView getWebView() {
return this.mWebView;
}
}
基本上,我们加载了're appending an additional JavaScript function that is used to test the state of the document. If it',我们在Android的 MainActivity 中启动自定义 onPageLoaded() 事件;否则,我们注册一个事件监听器,一旦页面准备好,使用 window.addEventListener('onload', ...); 更新Android .
13 回答
扩展WebViewClient并调用onPageFinished(),如下所示:
只是为了显示进度条,“onPageStarted”和“onPageFinished”方法就足够了;但是如果你想要一个“is_loading”标志(以及页面重定向,......),这个方法可以用非顺序执行,比如“onPageStarted> onPageStarted> onPageFinished> onPageFinished”队列 .
但是通过我的简短测试(自己测试),“onProgressChanged”方法值队列是“0-100> 0-100> 0-100> ......”
如果活动关闭,则在活动关闭时设置“
is_loading = false
”,因为活动可以在页面完成之前完成 .我简化了NeTeInStEiN的代码是这样的:
很容易理解,OnPageFinished如果前一个回调是在onPageStarted上,那么页面就完全加载了 .
@ian这不是100%准确 . 如果页面中有多个iframe,则会有多个onPageFinished(和onPageStarted) . 如果你有几个重定向,它也可能会失败 . 这种方法解决了(几乎)所有问题:
UPDATE:
根据文档:当嵌入框架的内容发生变化时,不会调用onPageStarted,即单击目标为iframe的链接 .
我在Twitter上找到了一个特定的案例,其中只有一个pageFinished被调用并且稍微混淆了逻辑 . 为了解决这个问题,我在X秒后添加了一个计划任务来删除加载 . 在所有其他情况下都不需要这样做 .
UPDATE 2 :
现在使用当前的Android WebView实现:
我找到了一个优雅的解决方案,但没有经过严格的测试:
这是一个允许您注册
Runnable
的方法,以便在特定网址完成加载后执行 . 我们将Runnable
与Map
中的相应URLString
相关联,并使用WebView
的getOriginalUrl()
方法选择适当的回调 .如果要显示进度条,则需要监听进度更改事件,而不仅仅是完成页面:
顺便说一句,如果你想只显示一个Indeterminate ProgressBar,那么覆盖onPageFinished方法就足够了
使用setWebViewClient()并覆盖onPageFinished()
我非常偏爱@NeTeInStEiN(和@polen)解决方案但是会用计数器实现它而不是多个布尔或州观察者(只是另一种味道,但我认为可能会分享) . 它确实有一个关于它的JS细微差别,但我觉得逻辑更容易理解 .
这是一种利用Android's capability for JavaScript hooks加载URL时检测到的新方法 . 使用此模式,我们利用JavaScript 's knowledge of the document' s状态在Android运行时内生成本机方法调用 . 可以使用
@JavaScriptInterface
注释进行这些JavaScript可访问的调用 .此实现要求我们在
WebView
的设置上调用setJavaScriptEnabled(true)
,因此根据您的应用程序的要求,它可能不合适,例如:安全问题 .src/io/github/cawfree/webviewcallback/MainActivity.java (果冻 beans ,API等级16)
res/layout/activity_main.xml
基本上,我们加载了're appending an additional JavaScript function that is used to test the state of the document. If it',我们在Android的
MainActivity
中启动自定义onPageLoaded()
事件;否则,我们注册一个事件监听器,一旦页面准备好,使用window.addEventListener('onload', ...);
更新Android .由于我们在调用
this.getWebView().loadURL("")
之后附加了这个脚本,因此它们根本不需要'listen'来处理事件,因为我们只有通过连续调用loadURL
才能附加JavaScript钩子 . 已经加载了 .Loading url with SwipeRefreshLayout and ProgressBar:
UrlPageActivity.java:
activity_url_page.xml:
您可以通过webview类中的getProgress方法跟踪Progress Staus .
初始化进度状态
然后像这样加载AsyncTask:
谢谢你的回答 . 它帮助了我,但我必须根据我的需要改进它 . 我有几个页面和完成,所以我添加了一个计时器,检查是否启动了pagefinish新的pagestart . 好的,不好的解释 . 看代码:)