Ich habe eine WebView
, die eine Seite aus dem Internet lädt. Ich möchte ein anzeigen, ProgressBar
bis das Laden abgeschlossen ist.
Wie höre ich auf den Abschluss des Seitenladens von a WebView
?
Ich habe eine WebView
, die eine Seite aus dem Internet lädt. Ich möchte ein anzeigen, ProgressBar
bis das Laden abgeschlossen ist.
Wie höre ich auf den Abschluss des Seitenladens von a WebView
?
Antworten:
Erweitern Sie WebViewClient und rufen Sie onPageFinished () wie folgt auf:
mWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
// do your stuff here
}
});
@ian das ist nicht 100% genau. Wenn Sie mehrere Iframes auf einer Seite haben, haben Sie mehrere onPageFinished (und onPageStarted). Und wenn Sie mehrere Weiterleitungen haben, kann dies ebenfalls fehlschlagen. Dieser Ansatz löst (fast) alle Probleme:
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;
//HIDE LOADING IT HAS FINISHED
} else {
redirect = false;
}
}
});
AKTUALISIEREN:
Gemäß der Dokumentation: onPageStarted wird NICHT aufgerufen, wenn sich der Inhalt eines eingebetteten Frames ändert, dh wenn Sie auf einen Link klicken, dessen Ziel ein Iframe ist.
Ich habe einen solchen speziellen Fall auf Twitter gefunden, in dem nur eine pageFinished aufgerufen wurde und die Logik ein wenig durcheinander gebracht hat. Um dies zu lösen, habe ich eine geplante Aufgabe hinzugefügt, um das Laden nach X Sekunden zu entfernen. Dies ist in allen anderen Fällen nicht erforderlich.
UPDATE 2 :
Jetzt mit der aktuellen Android WebView-Implementierung:
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;
//HIDE LOADING IT HAS FINISHED
} else {
redirect = false;
}
}
});
Ich bin ziemlich angetan von der Lösung @NeTeInStEiN (und @polen), hätte sie aber mit einem Zähler anstelle mehrerer Boolescher Werte oder Statusbeobachter implementiert (nur eine andere Variante, von der ich dachte, dass sie geteilt werden könnte). Es hat eine JS-Nuance, aber ich denke, die Logik ist etwas einfacher zu verstehen.
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.
}
}
});
}
running = 1;
rein onPageStarted()
?
Ich habe auch eine elegante Lösung gefunden, habe sie aber nicht rigoros getestet:
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);
}
}
Ich habe den Code von NeTeInStEiN so vereinfacht :
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;
}
}
});
Es ist leicht zu verstehen, OnPageFinished, wenn der vorherige Rückruf auf onPageStarted erfolgt, sodass die Seite vollständig geladen ist.
Wenn Sie einen Fortschrittsbalken anzeigen möchten, müssen Sie auf ein Fortschrittsänderungsereignis warten, nicht nur auf die Fertigstellung der Seite:
mWebView.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
//change your progress bar
}
});
Übrigens, wenn Sie nur eine unbestimmte Fortschrittsanzeige anzeigen möchten, die die Methode onPageFinished überschreibt, ist dies ausreichend
Verwenden Sie setWebViewClient () und überschreiben Sie onPageFinished ()
Sie können den Progress Staus mithilfe der getProgress- Methode in der Webview- Klasse verfolgen .
Initialisieren Sie den Fortschrittsstatus
private int mProgressStatus = 0;
dann die AsyncTask zum Laden wie folgt:
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();
}
}
}
Danke für die Antworten. Es hat mir geholfen, aber ich musste es ein bisschen für meine Bedürfnisse verbessern. Ich hatte mehrere Seitenstarts und Endungen, also habe ich einen Timer hinzugefügt, der prüft, ob nach dem Start der Seite ein neuer Seitenstart gestartet wird. Okay, schlechte Erklärung. Siehe den Code :)
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);
}
}
});
Hier ist eine neuartige Methode zum Erkennen, wenn eine URL geladen wurde, indem die Android-Funktion für JavaScript-Hooks verwendet wird . Mit diesem Muster nutzen wir das Wissen von JavaScript über den Status des Dokuments, um einen nativen Methodenaufruf innerhalb der Android-Laufzeit zu generieren. Diese JavaScript-zugänglichen Aufrufe können mithilfe der @JavaScriptInterface
Anmerkung erfolgen.
Diese Implementierung erfordert, dass wir setJavaScriptEnabled(true)
die WebView
Einstellungen des Benutzers aufrufen , sodass sie abhängig von den Anforderungen Ihrer Anwendung, z. B. Sicherheitsbedenken , möglicherweise nicht geeignet ist .
src / io / github / cawfree / webviewcallback / MainActivity.java (Jelly Bean, API Level 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;
}
}
res / layout / 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"
/>
Im Wesentlichen fügen wir eine zusätzliche JavaScript-Funktion hinzu, mit der der Status des Dokuments getestet wird. Wenn es geladen ist, starten wir ein benutzerdefiniertes onPageLoaded()
Ereignis in Android MainActivity
; Andernfalls registrieren wir einen Ereignis-Listener, der Android aktualisiert, sobald die Seite fertig ist window.addEventListener('onload', ...);
.
Da wir dieses Skript nach dem Aufruf sind Anfügen an this.getWebView().loadURL("")
gemacht worden ist , ist es wahrscheinlich , dass wir nicht brauchen zu ‚hören‘ überhaupt für die Ereignisse, da wir nur eine Chance, den JavaScript - Haken anzuzuhängen , indem sie einen sukzessiven Aufruf loadURL
, sobald die Seite bereits geladen wurde.
Nur um den Fortschrittsbalken anzuzeigen, reichen die Methoden "onPageStarted" und "onPageFinished" aus. Wenn Sie jedoch ein "is_loading" -Flag (zusammen mit Seitenumleitungen, ...) haben möchten, werden diese Methoden möglicherweise ohne Sequenzierung ausgeführt, z. B. "onPageStarted> onPageStarted> onPageFinished> onPageFinished".
Bei meinem kurzen Test (testen Sie es selbst) lautet die Warteschlange für die Methodenwerte "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);
}
}
Setzen Sie is_loading = false
beim Schließen der Aktivität auch " ", wenn es sich um eine statische Variable handelt, da die Aktivität vor dem Ende der Seite beendet werden kann.
Laden URL mit SwipeRefreshLayout
und 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://stackoverflow.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>
Hier ist eine Methode, mit der Sie eine registrieren können Runnable
, die ausgeführt werden soll, sobald eine bestimmte Webadresse vollständig geladen wurde. Wir verknüpfen jede Runnable
mit einer entsprechenden URL String
in a Map
und verwenden die Methode von WebView
's getOriginalUrl()
, um den entsprechenden Rückruf auszuwählen.
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;
}
}
Der Renderer beendet das Rendern nicht, wenn die OnPageFinshed-Methode aufgerufen wird oder der Fortschritt 100% erreicht, sodass beide Methoden nicht garantieren, dass die Ansicht vollständig gerendert wurde.
Mit der OnLoadResource-Methode können Sie jedoch herausfinden, was bereits gerendert wurde und was noch gerendert wird. Und diese Methode wird mehrmals aufgerufen.
@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.
}
}
Verwenden Sie dies, es sollte helfen.`var currentUrl = "google.com" var partOfUrl = currentUrl.substring (0, currentUrl.length-2)
webView.setWebViewClient (Objekt: 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.
}
// Sie sollten auch andere Überschreibungsmethoden für Fehler wie onReceiveError überschreiben, um zu sehen, wie alle diese Methoden nacheinander aufgerufen werden und wie sie sich beim Debuggen mit Haltepunkt verhalten. } `
für Kotlin-Benutzer:
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
// do your logic
}
}
Es gibt jedoch viele Methoden, die Sie überschreiben können