如何给WebView添加下拉刷新
1,需求:
SmartRefreshLayout + WebView进行下拉刷新
PS:之前产品说要给所有的H5页面添加下拉刷新,表示有事件冲突,而且不能捕获H5页面是否已经滑动到顶部,有小伙伴提出使用js实现,这种方式不喜欢,扩展性太差了!!!
2,实现方案
想破了头,终于让我逮到了:腾讯X5浏览器安卓版本添加了下拉头!!!
别说了,赶紧写代码!!!
3,代码实现:
如何监听是否H5是否滑动到顶部?
首先添加监听:
setWebViewCallbackClient(new X5WebCallbackClient());
class X5WebCallbackClient implements WebViewCallbackClient {
private float yStart = 0f;
/**
* 处理上滑到头的时候也会触发滑动到头的回调的问题
*/
private boolean isOverScroll = false;
@Override
public void invalidate() {
}
@Override
public boolean onTouchEvent(MotionEvent event, View view) {
return super_onTouchEvent(event);
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent, View view) {
Log.e("0705", "overScrollBy Y:" + scrollY + "X:" + scrollRangeY);
//这里就是滑动到头了!!!
return super_overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,
isTouchEvent);
}
@Override
public void computeScroll(View view) {
super_computeScroll();
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY, View view) {
Log.e("0706", "overScrollBy Y:" + scrollY + "X:" + clampedY);
super_onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt, View view) {
Log.e("0707", l + ">>>" + t + ">>>" + oldl + ">>>" + oldt);
super_onScrollChanged(l, t, oldl, oldt);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev, View view) {
return super_dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev, View view) {
return super_onInterceptTouchEvent(ev);
}
}
下拉头怎么办?自己写一个?觉得太麻烦,为什么有搞好的下拉控件不用呢?
<com.scwang.smartrefresh.layout.SmartRefreshLayout
android:id="@+id/refreshView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srlEnableLoadMore="false">
//这个是自定义的webView,
<XXXX.X5WebView
android:id="@+id/wv_webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
webView的代码贴出来:
package XXXXX.web;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.jumei.lib.storage.shareperfrence.JmSharePrefrence;
import com.jumei.lib.util.http.HTTP;
import com.tencent.smtt.export.external.interfaces.HttpAuthHandler;
import com.tencent.smtt.export.external.interfaces.JsPromptResult;
import com.tencent.smtt.export.external.interfaces.JsResult;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewCallbackClient;
import com.tencent.smtt.sdk.WebViewClient;
import java.util.HashMap;
import java.util.Map;
/**
* 对内核的webView再次封装
*/
public class X5WebView extends WebView {
private static boolean isSmallWebViewDisplayed = false;
private Map<String, Object> mJsBridges;
private X5WebViewClient client;
private X5WebChromeClient chromeClient;
private OnScrollCallback scrollCallback;
public void setScrollCallback(OnScrollCallback scrollCallback) {
this.scrollCallback = scrollCallback;
}
/**
* 初始化
*/
@SuppressLint("SetJavaScriptEnabled")
public X5WebView(Context context, AttributeSet attr) {
super(context, attr);
// 配置X5webview的事件处理
initView(context);
}
private void initView(Context context) {
initClient();
this.setWebViewClient(client);
this.setWebChromeClient(chromeClient);
initWebViewSettings();
}
public X5WebView(Context context) {
super(context);
initView(context);
}
public X5WebView(Context context, AttributeSet attr, int style) {
super(context, attr, style, false);
initView(context);
}
/**
* 配置属性
*/
@SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled"})
private void initWebViewSettings() {
WebSettings webSetting = this.getSettings();
webSetting.setJavaScriptEnabled(true);
webSetting.setJavaScriptCanOpenWindowsAutomatically(true);
webSetting.setAllowFileAccess(true);
webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webSetting.setSupportZoom(true);
webSetting.setUseWideViewPort(true);
// 修改userAgent 用于网页去头尾
String userAgent = webSetting.getUserAgentString();
webSetting.setUserAgentString(userAgent);
webSetting.setLoadWithOverviewMode(true);
webSetting.setAppCacheEnabled(true);
webSetting.setDatabaseEnabled(true);
webSetting.setDomStorageEnabled(true);
webSetting.setGeolocationEnabled(true);
webSetting.setAppCacheMaxSize(Long.MAX_VALUE);
webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND);
webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH);
webSetting.setCacheMode(WebSettings.LOAD_NO_CACHE);
webSetting.setAllowFileAccessFromFileURLs(true);
webSetting.setAppCacheEnabled(false);
WebView.setWebContentsDebuggingEnabled(true);
setWebViewCallbackClient(new X5WebCallbackClient());
addJavascriptInterface(new X5SimpleJavaScriptFunction() {
}, "JavaScriptInterface");
}
private void initClient() {
if (getContext() instanceof Activity) {
client = new X5WebViewClient((Activity) getContext()) {
@Override
public void onReceivedHttpAuthRequest(WebView webview,
HttpAuthHandler httpAuthHandlerhost, String host,
String realm) {
boolean flag = httpAuthHandlerhost.useHttpAuthUsernamePassword();
}
};
chromeClient = new X5WebChromeClient((Activity) getContext()) {
@Override
public boolean onJsConfirm(WebView arg0, String arg1, String arg2, JsResult arg3) {
return super.onJsConfirm(arg0, arg1, arg2, arg3);
}
/**
* webview 的窗口转移
*/
@Override
public boolean onCreateWindow(WebView arg0, boolean arg1, boolean arg2, Message msg) {
// TODO Auto-generated method stub
if (X5WebView.isSmallWebViewDisplayed == true) {
WebViewTransport webViewTransport = (WebViewTransport) msg.obj;
WebView webView = new WebView(X5WebView.this.getContext()) {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setTextSize(15);
canvas.drawText("新建窗口", 10, 10, paint);
}
;
};
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView arg0, String arg1) {
arg0.loadUrl(arg1);
return true;
}
;
});
webViewTransport.setWebView(webView);
msg.sendToTarget();
}
return true;
}
@Override
public boolean onJsAlert(WebView arg0, String arg1, String arg2, JsResult arg3) {
/*
这里写入你自定义的window alert
*/
Log.i("cdelweb", "setX5webview = null");
return super.onJsAlert(null, "", "", arg3);
}
/**
* 对应js 的通知弹框 ,可以用来实现js 和 android之间的通信
*/
@Override
public boolean onJsPrompt(WebView arg0, String arg1, String arg2, String arg3, JsPromptResult arg4) {
// 在这里可以判定js传过来的数据,用于调起android native 方法
if (X5WebView.this.isMsgPrompt(arg1)) {
if (X5WebView.this.onJsPrompt(arg2, arg3)) {
return true;
} else {
return false;
}
}
return super.onJsPrompt(arg0, arg1, arg2, arg3, arg4);
}
@Override
public void onReceivedTitle(WebView arg0, final String arg1) {
super.onReceivedTitle(arg0, arg1);
Log.i("cdelweb", "webpage title is " + arg1);
}
};
}
}
/**
* 一般不用这个,它的作用是在webview上面再次绘制控件
*
* @param canvas
* @param child
* @param drawingTime
* @return
*/
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean ret = super.drawChild(canvas, child, drawingTime);
canvas.save();
return ret;
}
/**
* 是否允许小窗
*
* @param enabled
*/
public static void setSmallWebViewEnabled(boolean enabled) {
isSmallWebViewDisplayed = enabled;
}
/**
* js桥接
*
* @param jsBridgeBundle
*/
public void addJavascriptBridge(X5SecurityJsBridgeBundle jsBridgeBundle) {
if (this.mJsBridges == null) {
this.mJsBridges = new HashMap<String, Object>(5);
}
if (jsBridgeBundle != null) {
String tag = X5SecurityJsBridgeBundle.BLOCK + jsBridgeBundle.getJsBlockName() + "-"
+ X5SecurityJsBridgeBundle.METHOD + jsBridgeBundle.getMethodName();
this.mJsBridges.put(tag, jsBridgeBundle);
}
}
/**
* 当webchromeClient收到 web的prompt请求后进行拦截判断,用于调起本地android方法
*
* @param methodName 方法名称
* @param blockName 区块名称
* @return true :调用成功 ; false :调用失败
*/
private boolean onJsPrompt(String methodName, String blockName) {
String tag = X5SecurityJsBridgeBundle.BLOCK + blockName + "-" + X5SecurityJsBridgeBundle.METHOD + methodName;
if (this.mJsBridges != null && this.mJsBridges.containsKey(tag)) {
((X5SecurityJsBridgeBundle) this.mJsBridges.get(tag)).onCallMethod();
return true;
} else {
return false;
}
}
/**
* 判定当前的prompt消息是否为用于调用native方法的消息
*
* @param msg 消息名称
* @return true 属于prompt消息方法的调用
*/
private boolean isMsgPrompt(String msg) {
if (msg != null && msg.startsWith(X5SecurityJsBridgeBundle.PROMPT_START_OFFSET)) {
return true;
} else {
return false;
}
}
@Override
public final void loadUrl(String s) {
if (!s.startsWith("javascript")) {
Map<String, String> map = new HashMap<>();
map.put(JmSharePrefrence.SMDEVICENAME, JmSharePrefrence.getInstance().getSmdevicename());
map.put(JmSharePrefrence.SMDEVICEID, JmSharePrefrence.getInstance().getSmdeviceid());
map.put(JmSharePrefrence.SMVERSION, JmSharePrefrence.getInstance().getVersion());
super.loadUrl(s, map);
} else {
super.loadUrl(s);
}
}
public void setX5WebViewCallBack(X5WebViewCallBack callBack) {
if (callBack == null) {
return;
}
if (client != null) {
client.setCallBack(callBack);
}
if (chromeClient != null) {
chromeClient.setCallBack(callBack);
}
}
class X5WebCallbackClient implements WebViewCallbackClient {
private float yStart = 0f;
/**
* 处理上滑到头的时候也会触发滑动到头的回调的问题
*/
private boolean isOverScroll = false;
@Override
public void invalidate() {
}
@Override
public boolean onTouchEvent(MotionEvent event, View view) {
switch (event.getAction()) {
//这个地方做什么呢?这里之所以加这些事件的处理,是因为在H5上滑滑动到头的时候也会调、
//用overScrollBy,所以不光要判断方向,还要在webView滑动的时候去掉下拉刷新控件的事件
case MotionEvent.ACTION_DOWN:
yStart = event.getY();
break;
case MotionEvent.ACTION_UP:
float yEnd = event.getY();
if (yEnd <= yStart) {
isOverScroll = true;
} else {
isOverScroll = false;
}
if (Math.abs(yEnd - yStart) > 20f && scrollCallback != null) {
scrollCallback.onFinishRefresh();
}
break;
default:
break;
}
return super_onTouchEvent(event);
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent, View view) {
Log.e("0705", "overScrollBy Y:" + scrollY + "X:" + scrollRangeY);
if (scrollCallback != null && !isOverScroll) {
scrollCallback.onRefresh();
}
return super_overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,
isTouchEvent);
}
@Override
public void computeScroll(View view) {
super_computeScroll();
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY, View view) {
Log.e("0706", "overScrollBy Y:" + scrollY + "X:" + clampedY);
super_onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt, View view) {
Log.e("0707", l + ">>>" + t + ">>>" + oldl + ">>>" + oldt);
super_onScrollChanged(l, t, oldl, oldt);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev, View view) {
return super_dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev, View view) {
return super_onInterceptTouchEvent(ev);
}
};
public interface OnScrollCallback {
void onRefresh();
void onFinishRefresh();
boolean isTouched(MotionEvent event);
}
}
接下来实现回调:里面的rootView = SmartRefreshLayout
private X5WebView.OnScrollCallback scrollCallback = new X5WebView.OnScrollCallback() {
@Override
public void onRefresh() {
if (rootView != null && !rootView.isEnabled()) {
rootView.setEnabled(true);
rootView.setEnableRefresh(true);
}
if (webViewCallBack != null) {
webViewCallBack.onOverScrolled(true);
}
}
@Override
public void onFinishRefresh() {
if (rootView != null) {
rootView.finishRefresh();
rootView.setEnabled(false);
rootView.setEnableRefresh(false);
}
}
@Override
public boolean isTouched(MotionEvent event) {
if (rootView != null) {
if (RefreshUtil.isRefresh(rootView)) {
return false;
}
}
return true;
}
};
对了,别忘了执行刷新:
rootView.setOnRefreshListener(refreshLayout1 -> {
x5WebView.reload();
});
最后还有一个问题,怎么停止刷新?
package XXXXt.web;
import android.app.Activity;
import android.text.TextUtils;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebView;
/**
* Created by GongPeng on 2017/5/27.
* use of
*/
public class X5WebChromeClient extends WebChromeClient {
private static final String TAG = X5WebChromeClient.class.getName();
private Activity mContext;
private X5WebViewCallBack mCallBack;
private X5WebLoadCallback webLoadCallback;
public X5WebChromeClient(Activity mContext) {
this.mContext = mContext;
}
private String titleStr;
private TextView tvTitle;
private ProgressBar mPageLoadingProgressBar;
public void setmPageLoadingProgressBar(ProgressBar mPageLoadingProgressBar) {
this.mPageLoadingProgressBar = mPageLoadingProgressBar;
}
public void setTitleStr(String titleStr) {
this.titleStr = titleStr;
}
public void setTvTitle(TextView tvTitle) {
this.tvTitle = tvTitle;
}
@Override
public void onReceivedTitle(WebView view, String title) {
if (TextUtils.isEmpty(titleStr)) {
String titleTemp;
if (TextUtils.isEmpty(title)) {
titleTemp = "";
} else if (title.length() > 16) {
titleTemp = title.substring(0, 15);
} else {
titleTemp = title;
}
setTitleName(titleTemp);
if (mCallBack != null) {
try {
mCallBack.onReceivedTitle(titleTemp);
} catch (Exception e) {
if (e != null) {
}
}
}
} else {
setTitleName(titleStr);
}
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
/////////在这个我们可以知道网页已经开始加载了,停止刷新就行了
if (webLoadCallback != null){
webLoadCallback.onStartLoad();
}
if (mCallBack != null) {
try {
mCallBack.onProgressChanged(newProgress);
} catch (Exception e) {
if (e != null) {
}
}
}
if (mPageLoadingProgressBar == null) {
return;
}
mPageLoadingProgressBar.setProgress(newProgress);
if (mPageLoadingProgressBar != null && newProgress != 100) {
mPageLoadingProgressBar.setVisibility(View.VISIBLE);
} else if (mPageLoadingProgressBar != null) {
mPageLoadingProgressBar.setVisibility(View.GONE);
}
}
private void setTitleName(String name) {
if (tvTitle != null) {
tvTitle.setText(name);
}
}
public void setCallBack(X5WebViewCallBack callBack) {
mCallBack = callBack;
}
public void setWebLoadCallback(X5WebLoadCallback webLoadCallback) {
this.webLoadCallback = webLoadCallback;
}
}