WebView+SmartRefreshLayout实现下拉刷新

如何给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;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容