Как прослушать WebView, заканчивающий загрузку URL?
У меня есть WebView
это загрузка страницы из Интернета. Я хочу показать ProgressBar
пока загрузка не будет завершена.
Как мне прослушать завершение загрузки страницы WebView
?
18 ответов
Расширьте WebViewClient и вызовите onPageFinished() следующим образом:
mWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
// do your stuff here
}
});
@ian это не на 100% точно. Если у вас есть несколько фреймов на странице, у вас будет несколько onPageFinished (и onPageStarted). И если у вас есть несколько перенаправлений, он также может потерпеть неудачу Этот подход решает (почти) все проблемы:
boolean loadingFinished = true;
boolean redirect = false;
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String urlNewString) {
if (!loadingFinished) {
redirect = true;
}
loadingFinished = false;
webView.loadUrl(urlNewString);
return true;
}
@Override
public void onPageStarted(WebView view, String url) {
loadingFinished = false;
//SHOW LOADING IF IT ISNT ALREADY VISIBLE
}
@Override
public void onPageFinished(WebView view, String url) {
if (!redirect) {
loadingFinished = true;
}
if (loadingFinished && !redirect) {
//HIDE LOADING IT HAS FINISHED
} else {
redirect = false;
}
}
});
ОБНОВИТЬ:
Согласно документации: onPageStarted НЕ будет вызываться при изменении содержимого встроенного фрейма, т. Е. По ссылке, целью которой является фрейм iframe.
В Твиттере я обнаружил конкретный случай, подобный этому, когда вызывался только pageFinished, и он немного испортил логику. Чтобы решить эту проблему, я добавил запланированное задание, чтобы удалить загрузку через X секунд. Это не нужно во всех других случаях.
ОБНОВЛЕНИЕ 2:
Теперь с текущей реализацией Android WebView:
boolean loadingFinished = true;
boolean redirect = false;
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(
WebView view, WebResourceRequest request) {
if (!loadingFinished) {
redirect = true;
}
loadingFinished = false;
webView.loadUrl(request.getUrl().toString());
return true;
}
@Override
public void onPageStarted(
WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
loadingFinished = false;
//SHOW LOADING IF IT ISNT ALREADY VISIBLE
}
@Override
public void onPageFinished(WebView view, String url) {
if (!redirect) {
loadingFinished = true;
}
if (loadingFinished && !redirect) {
//HIDE LOADING IT HAS FINISHED
} else {
redirect = false;
}
}
});
Я также нашел одно изящное решение, но не проверил его строго:
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (m_webView.getProgress() == 100) {
progressBar.setVisibility(View.GONE);
m_webView.setVisibility(View.VISIBLE);
}
}
Я довольно неравнодушен к решению @NeTeInStEiN (и @polen), но реализовал бы его со счетчиком, а не с несколькими логическими значениями или наблюдателями состояния (просто другой вариант, но я подумал, что может поделиться). У него есть нюанс JS, но я чувствую, что логику немного легче понять.
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.
}
}
});
}
Если вы хотите показать индикатор выполнения, вам нужно прослушивать событие изменения прогресса, а не только для завершения страницы:
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
//change your progress bar
}
});
Кстати, если вы хотите отобразить только Indeterminate ProgressBar, переопределяющий метод onPageFinished достаточно
Я упростил neteinstein, чтобы он был таким:
mWebView.setWebViewClient(new WebViewClient() {
private int webViewPreviousState;
private final int PAGE_STARTED = 0x1;
private final int PAGE_REDIRECTED = 0x2;
@Override
public boolean shouldOverrideUrlLoading(WebView view, String urlNewString) {
webViewPreviousState = PAGE_REDIRECTED;
mWebView.loadUrl(urlNewString);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
webViewPreviousState = PAGE_STARTED;
if (dialog == null || !dialog.isShowing())
dialog = ProgressDialog.show(WebViewActivity.this, "", getString(R.string.loadingMessege), true, true,
new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// do something
}
});
}
@Override
public void onPageFinished(WebView view, String url) {
if (webViewPreviousState == PAGE_STARTED) {
dialog.dismiss();
dialog = null;
}
}
});
Это легко понять, OnPageFinished, если предыдущий обратный вызов включен onPageStarted, поэтому страница полностью загружена.
Для пользователей Kotlin:
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
// do your logic
}
}
Есть много методов, которые вы можете переопределить, хотя
Используйте setWebViewClient() и переопределите onPageFinished()
Вы можете отследить состояние прогресса с помощью метода getProgress в классе webview.
Инициализировать статус прогресса
private int mProgressStatus = 0;
тогда AsyncTask для загрузки так:
private class Task_News_ArticleView extends AsyncTask<Void, Void, Void> {
private final ProgressDialog dialog = new ProgressDialog(
your_class.this);
// can use UI thread here
protected void onPreExecute() {
this.dialog.setMessage("Loading...");
this.dialog.setCancelable(false);
this.dialog.show();
}
@Override
protected Void doInBackground(Void... params) {
try {
while (mProgressStatus < 100) {
mProgressStatus = webview.getProgress();
}
} catch (Exception e) {
}
return null;
}
protected void onPostExecute(Void result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
}
}
Спасибо за ответы. Это помогло мне, но мне пришлось немного улучшить его для своих нужд. У меня было несколько пусковых и конечных запусков, поэтому я добавил таймер, который проверяет, запущен ли при завершении страницы новый запуск страницы. Ладно, плохое объяснение. Смотрите код:)
myWebView.setWebViewClient(new WebViewClient() {
boolean loadingFinished = true;
boolean redirect = false;
long last_page_start;
long now;
// Load the url
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!loadingFinished) {
redirect = true;
}
loadingFinished = false;
view.loadUrl(url);
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.i("p","pagestart");
loadingFinished = false;
last_page_start = System.nanoTime();
show_splash();
}
// When finish loading page
public void onPageFinished(WebView view, String url) {
Log.i("p","pagefinish");
if(!redirect){
loadingFinished = true;
}
//call remove_splash in 500 miSec
if(loadingFinished && !redirect){
now = System.nanoTime();
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
remove_splash();
}
},
500);
} else{
redirect = false;
}
}
private void show_splash() {
if(myWebView.getVisibility() == View.VISIBLE) {
myWebView.setVisibility(View.GONE);
myWebView_splash.setVisibility(View.VISIBLE);
}
}
//if a new "page start" was fired dont remove splash screen
private void remove_splash() {
if (last_page_start < now) {
myWebView.setVisibility(View.VISIBLE);
myWebView_splash.setVisibility(View.GONE);
}
}
});
Вот новый метод обнаружения, когда URL-адрес загружен, с помощью возможности Android для ловушек JavaScript. Используя этот шаблон, мы используем знания JavaScript о состоянии документа для генерации собственного вызова метода во время выполнения Android. Эти доступные через JavaScript вызовы могут быть сделаны с помощью @JavaScriptInterface
аннотаций.
Эта реализация требует, чтобы мы setJavaScriptEnabled(true)
на WebView
настройки, поэтому он может не подходить в зависимости от требований вашего приложения, например, из соображений безопасности.
src / io / github / cawfree / webviewcallback / MainActivity.java (Jelly Bean, API-уровень 16)
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;
}
}
разреш / макет /activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<WebView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
По сути, мы добавляем дополнительную функцию JavaScript, которая используется для проверки состояния документа. Если он загружен, мы запускаем кастом onPageLoaded()
событие в Android MainActivity
; в противном случае мы регистрируем прослушиватель событий, который обновляет Android, как только страница готова, используя window.addEventListener('onload', ...);
,
Поскольку мы добавляем этот скрипт после вызова this.getWebView().loadURL("")
было сделано, вполне вероятно, что нам вообще не нужно "слушать" события, поскольку у нас есть только возможность добавить хук JavaScript, сделав последовательный вызов loadURL
после того, как страница уже была загружена.
Просто чтобы показать индикатор выполнения, достаточно методов "onPageStarted" и "onPageFinished"; но если вы хотите иметь флаг "is_loading" (вместе с перенаправлениями страниц, ...), эти методы могут выполняться с непоследовательностью, например, "onPageStarted > onPageStarted > onPageFinished > onPageFinished" очередь.
Но с моим коротким тестом (протестируйте его самостоятельно), очередь значений метода onProgressChanged равна "0-100 > 0-100 > 0-100 > ..."
private boolean is_loading = false;
webView.setWebChromeClient(new MyWebChromeClient(context));
private final class MyWebChromeClient extends WebChromeClient{
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 0){
is_loading = true;
} else if (newProgress == 100){
is_loading = false;
}
super.onProgressChanged(view, newProgress);
}
}
Также установить "is_loading = false
msgstr "при закрытии активности, если это статическая переменная, потому что активность может быть завершена до окончания страницы.
Рендерер не завершит рендеринг, когда вызывается метод OnPageFinshed или прогресс достигает 100%, поэтому оба метода не гарантируют, что представление было полностью отрисовано.
Но из метода OnLoadResource вы можете выяснить, что уже было отрендерено, а что еще рендерится. И этот метод вызывается несколько раз.
@Override
public void onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
// Log and see all the urls and know exactly what is being rendered and visible. If you wanna know when the entire page is completely rendered, find the last url from log and check it with if clause and implement your logic there.
if (url.contains("assets/loginpage/img/ui/forms/")) {
// loginpage is rendered and visible now.
// your logic here.
}
}
Это будет вызвано до того, как он начнет загружать страницу (и получит те же параметры, что и
onFinished()
)
@Override
public void onPageCommitVisible(WebView view, String url) {
super.onPageCommitVisible(view, url);
}
Используйте это, это должно помочь.`Var currentUrl =" google.com "var partOfUrl = currentUrl.substring(0, currentUrl.length-2)
webView.setWebViewClient (объект: WebViewClient() {
override fun onLoadResource(WebView view, String url) {
//call loadUrl() method here
// also check if url contains partOfUrl, if not load it differently.
if(url.contains(partOfUrl, true)) {
//it should work if you reach inside this if scope.
} else if(!(currentUrl.startWith("w", true))) {
webView.loadurl("www.$currentUrl")
} else if(!(currentUrl.startWith("h", true))) {
webView.loadurl("https://$currentUrl")
} else { ...}
}
override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
// you can call again loadUrl from here too if there is any error.
}
// Вы также должны переопределить другой метод переопределения для ошибки, такой как onReceiveError, чтобы увидеть, как все эти методы вызываются один за другим и как они себя ведут при отладке с точкой останова. } `
Вот метод, который позволяет вам зарегистрировать Runnable
выполняется после того, как загрузка определенного веб-адреса завершена. Мы связываем каждого Runnable
с соответствующим URL String
в Map
и мы используем WebView
"s getOriginalUrl()
метод, чтобы выбрать соответствующий обратный вызов.
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;
}
}
Загрузка URL с SwipeRefreshLayout
а также ProgressBar
:
UrlPageActivity.java:
WebView webView;
SwipeRefreshLayout _swipe_procesbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_url_page);
String url = "http://stackru.com/";
_swipe_procesbar = (SwipeRefreshLayout)findViewById(R.id.url_path_swipe_procesbar);
_swipe_procesbar.post(new Runnable() {
@Override
public void run() {
_swipe_procesbar.setRefreshing(true);
}
}
);
webView = (WebView) findViewById(R.id.url_page_web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
_swipe_procesbar.setRefreshing(false);
_swipe_procesbar.setEnabled(false);
}
});
webView.loadUrl(url);
}
activity_url_page.xml:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/url_path_swipe_procesbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.test.test1.UrlPageActivity">
<WebView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/url_page_web_view" />
</RelativeLayout>
</android.support.v4.widget.SwipeRefreshLayout>
Котлин раствор
Первое решение
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val webView : WebView = findViewById(R.id.webView)
webView.webViewClient = MyWebViewClient()
}
private class MyWebViewClient : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
println("Load Started")
}
override fun onPageFinished(view: WebView, url: String) {
println("Load Finished")
}
}
}
Второе решение
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val webView : WebView = findViewById(R.id.webView)
webView.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
println("Load Started")
}
override fun onPageFinished(view: WebView, url: String) {
println("Load Finished")
}
}
}
Лучше поздно, чем никогда