Just extends BaseWebView
below:
public abstract class BaseWebView extends WebView {
private static final String TAG = "BaseWebView";
private static final int DELAY_MS_MAX = 1000;
private static final int DELAY_MS_DRAW = 100;
private static final long REQUEST_ID_INIT = 0;
public BaseWebView(Context context) {
super(context);
init();
}
public BaseWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BaseWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* to make sure to set {@link #loadDataFinished} to true at last
* in {@link #DELAY_MS_MAX} ms later.
*/
private static final int MSG_LOAD_DATA_FINISH_DEF = 0;
/**
* to set {@link #loadDataFinished} to true in {@link #DELAY_MS_DRAW} ms later.
*/
private static final int MSG_LOAD_DATA_FINISH_DRAW = 1;
private String description;
/**
* if load data finished
* <P><P>
* mark the status for load API {@link #loadData(String, String, String)}
* & {@link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {@link #loadUrl(String)}, {@link #loadUrl(String, Map)} and {@link #reload()}
*/
private AtomicBoolean loadDataFinished = new AtomicBoolean(true);
/**
* if progress reached 1000
* <P><P>
* mark the status for load API {@link #loadData(String, String, String)}
* & {@link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {@link #loadUrl(String)}, {@link #loadUrl(String, Map)} and {@link #reload()}
*/
private AtomicBoolean progressFinished = new AtomicBoolean(true);
/**
* the request id of load data
* <P><P>
* mark the status for load API {@link #loadData(String, String, String)}
* & {@link #loadDataWithBaseURL(String, String, String, String, String)}
* <p>
* <b>ignored:</b> {@link #loadUrl(String)}, {@link #loadUrl(String, Map)} and {@link #reload()}
*/
private AtomicLong loadDataTimestamp = new AtomicLong(REQUEST_ID_INIT);
private Handler handler = new MyHandler(this);
/**
* clear status for load API {@link #loadData(String, String, String)}
* & {@link #loadDataWithBaseURL(String, String, String, String, String)}, set to false.
* <p>
* <b>ignored:</b> {@link #loadUrl(String)}, {@link #loadUrl(String, Map)} and {@link #reload()}
* <p>
* <b>Usage:</b> call in load API {@link #loadData(String, String, String)}
* & {@link #loadDataWithBaseURL(String, String, String, String, String)}
*/
private void clearLoadDataStatus() {
DLog.d(TAG, "clearLoadDataStatus: set false, obj=" + this.hashCode());
loadDataFinished.set(false);
progressFinished.set(false);
// generates a new load request id
loadDataTimestamp.set(System.currentTimeMillis());
}
private void setLoadDataFinished(long requestId) {
DLog.d(TAG, "setLoadDataFinished: id=" + requestId
+ ", loadDataTimestamp=" + loadDataTimestamp.get() + ", obj=" + this.hashCode());
if (!progressFinished.get() || requestId != loadDataTimestamp.get()) {
return;
}
loadDataFinished();
}
private void loadDataFinished() {
DLog.d(TAG, "loadDataFinished: set true, obj=" + this.hashCode());
loadDataFinished.set(true);
// clear load request id
loadDataTimestamp.set(REQUEST_ID_INIT);
}
/**
* get status for load API {@link #loadData(String, String, String)}
* & {@link #loadDataWithBaseURL(String, String, String, String, String)}, set to false.
* <p>
* <b>ignored:</b> {@link #loadUrl(String)}, {@link #loadUrl(String, Map)} and {@link #reload()}
*/
public boolean isLoadDataFinished() {
return loadDataFinished.get();
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
private void handleLoadDataFinished(int what) {
if (loadDataTimestamp.get() == REQUEST_ID_INIT) {
// there is no load data actions
//DLog.w(TAG, "handleLoadDataFinished: there is no load data actions, obj="
// + this.hashCode());
return;
}
DLog.d(TAG, "handleLoadDataFinished: obj=" + this.hashCode());
int delay = 0;
long requestId = loadDataTimestamp.get();
if (what == MSG_LOAD_DATA_FINISH_DEF) {
delay = DELAY_MS_MAX;
} else if (what == MSG_LOAD_DATA_FINISH_DRAW) {
delay = DELAY_MS_DRAW;
}
handler.removeMessages(what);
handler.sendMessageDelayed(Message.obtain(handler, what, (Object) requestId), delay);
}
private void init() {
this.setWebChromeClient(new DefWebChromeClient());
this.setHorizontalScrollBarEnabled(false);
this.setVerticalScrollBarEnabled(false);
this.description = onGetDescription();
}
/**
* a description of this WebView
*/
abstract public String onGetDescription();
@Override
public void loadDataWithBaseURL(
@Nullable String baseUrl, String data, @Nullable String mimeType,
@Nullable String encoding, @Nullable String historyUrl) {
clearLoadDataStatus();
super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@Override
public void loadData(String data, @Nullable String mimeType, @Nullable String encoding) {
clearLoadDataStatus();
super.loadData(data, mimeType, encoding);
}
@Override
public void stopLoading() {
super.stopLoading();
loadDataFinished();
}
@Override
public void setWebChromeClient(WebChromeClient client) {
if (!(client instanceof DefWebChromeClient)) {
throw new IllegalArgumentException(
"the WebChromeClient must be an instance of " + DefWebChromeClient.class);
}
super.setWebChromeClient(client);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Log.d(TAG, "onDraw: " + this.hashCode());
if (progressFinished.get()) {
// set load data finished in DELAY_MS_DRAW.
// !!! Notice that the Runnable will not be exec if this WebView is not to show,
// and this will cause the `loadDataFinished` being `false` all the time,
// if you want that executing whatever, plz use `handle.post()`.
post(new Runnable() {
@Override
public void run() {
handleLoadDataFinished(MSG_LOAD_DATA_FINISH_DRAW);
}
});
}
}
class DefWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (newProgress == 100) {
progressFinished.set(true);
// set load data finished in DELAY_MS_MAX as def.
// !!! Notice that the Runnable will not be exec if this WebView is not to show,
// and this will cause the `loadDataFinished` being `false` all the time,
// if you want that executing whatever, plz use `handle.post()`.
post(new Runnable() {
@Override
public void run() {
handleLoadDataFinished(MSG_LOAD_DATA_FINISH_DEF);
}
});
}
Log.d(TAG, "onProgressChanged: " + newProgress
+ ", desc=" + description + ", obj=" + view.hashCode());
}
}
static class MyHandler extends Handler {
WeakReference<BaseWebView> mViewReference;
MyHandler(BaseWebView webView) {
mViewReference = new WeakReference<>(webView);
}
@Override
public void handleMessage(Message msg) {
final BaseWebView webView = mViewReference.get();
if (webView == null) {
return;
}
// handle msg
if (msg.what == MSG_LOAD_DATA_FINISH_DEF || msg.what == MSG_LOAD_DATA_FINISH_DRAW) {
webView.setLoadDataFinished((Long) msg.obj);
}
}
}
}
=== END ===
Notice that only loadData()
& loadDataWithBaseURL()
works above,
you can add loadUrl()
, reload()
by yourself if needed.
Demo:https://github.com/zhaoya188/completed-load-webview