programing

웹 뷰가 URL 로드를 마치기 위해 듣는 방법은 무엇입니까?

muds 2023. 5. 31. 18:36
반응형

웹 뷰가 URL 로드를 마치기 위해 듣는 방법은 무엇입니까?

나는 있습니다WebView인터넷에서 페이지를 로드하는 것입니다.는 보주고싶어를 .ProgressBar적재가 완료될 때까지

페이지 로드 완료를 위해 어떻게 청취합니까?

WebViewClient를 확장하고 다음과 같이 PageFinished()를 호출합니다.

mWebView.setWebViewClient(new WebViewClient() {

   public void onPageFinished(WebView view, String url) {
        // do your stuff here
    }
});

@ian 이것은 100% 정확하지 않습니다.페이지에 iframe이 여러 개 있으면 PageFinished(및 PageStarted)에 여러 개가 표시됩니다.또한 리디렉션이 여러 개 있는 경우에도 실패할 수 있습니다.이 접근 방식은 (거의) 모든 문제를 해결합니다.

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; 
        }
    }
});

업데이트:

설명서에 따르면: 내장된 프레임의 내용이 변경될 때, 즉 iframe이 대상인 링크를 클릭하면 onPageStarted가 호출되지 않습니다.

저는 트위터에서 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;
                //HIDE LOADING IT HAS FINISHED
            } else {
                redirect = false; 
            }
        }
    });

저는 @NetTeInStEiN(및 @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.
            }
        }
    });
}

엄격하게 테스트하지 않은 우아한 솔루션도 하나 발견했습니다.

public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (webView.getProgress() == 100) {
                progressBar.setVisibility(View.GONE);
                webView.setVisibility(View.VISIBLE);
            }
        } 

진행률 표시줄을 표시하려면 페이지 완료뿐만 아니라 진행률 변경 이벤트를 수신해야 합니다.

mWebView.setWebChromeClient(new WebChromeClient(){

            @Override
            public void onProgressChanged(WebView view, int newProgress) {

                //change your progress bar
            }


        });

BTW 만약 당신이 단지 PageFinished에서 메소드를 재정의하는 불확실한 진행률 막대를 표시하고 싶다면 충분합니다.

나는 NetTeInStEiN의 코드를 다음과 같이 단순화했습니다.

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;
            }

        }
 });

이전 콜백이 PageStarted에 설정되어 있으면 OnPageFinished를 이해하기 쉬우므로 페이지가 완전히 로드됩니다.

Kotlin 사용자:

webView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView?, url: String?) {
                // do your logic
            }
        }

하지만 당신이 무시할 수 있는 많은 방법들이 있습니다.

setWebViewClient()를 사용하고 PageFinished()에서 재정의합니다.

뷰 클래스에서 getProgress 메서드를 사용하여 진행률 상태를 추적할 수 있습니다.

진행 상태 초기화

private int mProgressStatus = 0;

다음과 같이 로드하기 위한 비동기 작업:

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);
            }
        }

});

다음은 Android의 JavaScript 후크 기능을 사용하여 URL이 로드되었을 때 탐지하는 새로운 방법입니다.이 패턴을 사용하여 문서 상태에 대한 JavaScript의 지식을 활용하여 Android 런타임 내에서 네이티브 메서드 호출을 생성합니다.이러한 JavaScript 액세스 가능 통화는 다음을 사용하여 수행할 수 있습니다.@JavaScriptInterface주석

이 구현을 위해서는 다음을 호출해야 합니다.setJavaScriptEnabled(true)에서.WebView의 설정이므로 보안 문제 등 응용 프로그램의 요구 사항에 따라 적합하지 않을 수 있습니다.

src/io/github/cawfree/webview콜백/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;
    }

}

res/main/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"
/>

기본적으로 문서 상태를 테스트하는 데 사용되는 자바스크립트 함수를 추가하고 있습니다.로드된 경우 사용자 지정을 시작합니다.onPageLoaded()의 Android에서 »MainActivity합니다.window.addEventListener('onload', ...);.

통화 후에 이 스크립트를 추가하기 때문에this.getWebView().loadURL("")우리는 연속적으로 통화를 함으로써 자바스크립트 후크를 추가할 기회만 얻기 때문에 이벤트에 대해 '듣지' 않아도 될 가능성이 높습니다.loadURL페이지가 이미 로드된 후.

진행률 표시줄을 표시하기 위해 "onPageStarted" 및 "onPageFinished" 메서드로 충분하지만, "is_loading" 플래그(페이지 리디렉션과 함께...)를 사용하려면 이 메서드가 "onPageStarted > onPageFinished" 큐와 같이 순서가 아닌 "onPageStarted > 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활동이 페이지 완료 전에 완료될 수 있기 때문에 정적 변수인 경우 활동 종료 시.

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);
}

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://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>

다음은 등록할 수 있는 방법입니다.Runnable특정 웹 주소 로드가 완료되면 실행됩니다.우리는 각각을 연관시킵니다.Runnable해당 URL로String순식간에Map그리고 우리는 사용합니다.WebViewgetOriginalUrl()적절한 콜백을 선택하는 방법입니다.

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;
    }

}

이것을 사용하면 도움이 될 것입니다.'var currentUrl = "google.com " var partOfUrl = currentUrl.substring(0, currentUrl.length-2)

webView.setWebViewClient(개체:WebView 클라이언트() {

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와 같은 오류에 대한 다른 재정의 메서드를 재정의하여 이러한 메서드가 차례로 호출되는 방식과 중단점으로 디버깅하는 동안 동작하는 방식을 확인해야 합니다.} `

Kotlin 솔루션 First Solution은 웹 뷰 클라이언트를 개인 클래스로 만드는 것이며 더 효율적입니다.반면에 두 번째 해결책은 정렬입니다 :)

첫 번째 솔루션

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")
        }
     }
 }

두 솔루션 모두 실제로 동일합니다.그래서 onPageStarted 함수는 페이지가 다른 에 로드되기 시작할 때 실행됩니다. 페이지가 완전히 로드되면 PageFinished 함수가 작동합니다.PageFinished 함수의 내부에 예제 loadFinishedRun() 함수로 를 작성할 수 있습니다.

늦더라도 안 하느니보다는 낫다

언급URL : https://stackoverflow.com/questions/3149216/how-to-listen-for-a-webview-finishing-loading-a-url

반응형