美文网首页
安卓自定义weex控件webview,解决scrollView嵌

安卓自定义weex控件webview,解决scrollView嵌

作者: 铁肩侠 | 来源:发表于2019-08-02 09:48 被阅读0次

weex页面有scrollView嵌套web的页面,如果不给web指定高度就显示不出来。RN也有这个问题,查了下全部是通过原生开启一个不显示的webview提前加载一遍再将高度传给weex来解决,这种方式需要加载俩边资源拖慢了显示速度,而且浪费资源。

研究了下新方式,只加载一遍,用原生自定义的webview在加载完后在原生端修改高度。

同时使用自定义的webivew解决了有些特殊机型无法显示(如LG nexus5),以及webview的一系列设置,安全,js问题。

面临三个问题:
1,如何自定义weex组件?
2,如何通过源码找出修改weex控件宽高的方法?
3,如何得到webview的准确高度?

自定义weex控件:

继承WXComponent指定泛型。

public class WWebview extends WXComponent<DisplayX5Webview> {
 
}

在原生端application中注册组件后weex端就能直接引用,注意:不能有大写字母,weex引用时为蓝色方为成功;暗黄则是失败。

WXSDKEngine.registerComponent("mywebview", WWebview.class);

weex直接引用:

<mywebview
    style={[styles.webViewContainer]}
/>

在初始化里返回weex显示的控件,这里可以返回任意布局,甚至动态添加子布局等。

@Override
protected DisplayX5Webview initComponentHostView(@NonNull Context context) {
       webView = new DisplayX5Webview(context.getApplicationContext());
       initWebView((Activity) context);//初始化
       return webView;
}

约定weex端的标签属性source:(注意要小写)

@WXComponentProp(name = "source")
public void setSource(String source) {
      mSource = source;
}

属性直接使用即可:

<mywebview
    style={[styles.webViewContainer]}
    source={`url`}
/>

加载weex时component的生命周期会先调用@WXComponentProp(name = "source"),再调用bindData()顾名思义就是加载数据的(小坑:如果weex端没有做判空,在网络请求下来之前先render一遍,后面网络数据返回以后再render就不会执行bindDta了)

 @Override
    public void bindData(WXComponent component) {
        super.bindData(component);
 
        if (!TextUtils.isEmpty(mSource)) {
            webView.loadData(mSource, "text/html; charset=UTF-8", "UTF-8");
          //  webView.loadUrl(mSource);
        }
 
}

到这里,如果如果在weex端指定高度就已经可以显示了。

在安卓端修改控件高度

修改高度很麻烦,无论如何设置LayoutParams都是没用的,下面大致看下源码里为什么会设置不进去。

Component的注册加载机制参考下:https://www.jianshu.com/p/53f69bfcbc50

直接看处理渲染的中心类WXDomHandler。dom,渲染处理都在此处。

 @Override
 public boolean handleMessage(Message msg) {
   if (msg == null) {
     return false;
   }
   int what = msg.what;
   Object obj = msg.obj;
   WXDomTask task = null;

   if (obj != null && obj instanceof WXDomTask) {
     task = (WXDomTask) obj;
     Object action = ((WXDomTask) obj).args.get(0);
     if (action != null && action instanceof TraceableAction) {
       ((TraceableAction) action).mDomQueueTime = SystemClock.uptimeMillis() - msg.getWhen();
     }
   }

   if (!mHasBatch) {
     mHasBatch = true;
     if(what != WXDomHandler.MsgType.WX_DOM_BATCH) {
       int delayTime = DELAY_TIME;
       if(what == MsgType.WX_DOM_TRANSITION_BATCH){
         delayTime = TRANSITION_DELAY_TIME;
       }
       mWXDomManager.sendEmptyMessageDelayed(WXDomHandler.MsgType.WX_DOM_BATCH, delayTime);
     }
    }
   switch (what) {
     case MsgType.WX_EXECUTE_ACTION:
       mWXDomManager.executeAction(task.instanceId, (DOMAction) task.args.get(0), (boolean) task.args.get(1));
       break;
     case MsgType.WX_DOM_UPDATE_STYLE:
       //keep this for direct native call
       mWXDomManager.executeAction(task.instanceId, Actions.getUpdateStyle((String) task.args.get(0),
           (JSONObject) task.args.get(1),
           task.args.size() > 2 && (boolean) task.args.get(2)),false);
       break;
     case MsgType.WX_DOM_BATCH:

       mWXDomManager.batch();
       mHasBatch = false;
       break;
     case MsgType.WX_CONSUME_RENDER_TASKS:
       mWXDomManager.consumeRenderTask(task.instanceId);
       break;
     default:
       break;
   }
   return true;
 }

很明显case MsgType.WX_DOM_UPDATE_STYLE:字面意思就是我们要找的。再看下具体做了什么来更新dom刷新ui:

 case MsgType.WX_DOM_UPDATE_STYLE:
        //keep this for direct native call
        mWXDomManager.executeAction(task.instanceId, Actions.getUpdateStyle((String) task.args.get(0),
            (JSONObject) task.args.get(1),
            task.args.size() > 2 && (boolean) task.args.get(2)),false);
        break;

需要三个参数:instanceId——WXComponent初始化自带,第二个参数getUpdateStyle,第三个不管

看第二个参数传入的方法:

  public static DOMAction getUpdateStyle(String ref, JSONObject data, boolean byPesudo){
    return new UpdateStyleAction(ref, data, byPesudo);
  }

new 的DOMAction最终executeAction来执行执行代码依然在UpdateStyleAction中:

@Override
  public void executeDom(DOMActionContext context) {
    if (context.isDestory() || mData == null) {
      return;
    }
 
 
...
 
if (!mData.isEmpty()) {
      domObject.updateStyle(mData, mIsCausedByPesudo);
      domObject.applyStyle(mData);
      if(!mData.isEmpty()) {
        context.postRenderTask(this);
      }
    }
 
 
}
具体看updateStyle方法:

  public void updateStyle(Map<String, Object> updates, boolean byPesudo) {
...
 
  /**
     * diff styles
     * */
    if(!diffUpdates(updates, getStyles())){
      return;
    }
 
    if(mStyles == null) {
      mStyles = new WXStyle();
    }
    mStyles.putAll(updates,byPesudo);
 
...
 
}

diffUpdates做了新老对比,无改变return。

mStyles.putAll(updates,byPesudo);这句可以看出最终还是追加修改的方式,所以只需要传入要修改的参数即可。

在WXComponent中有notifyNativeSizeChanged方法但是断点看mNeedLayoutOnAnimation一直为false。把这个方法拷贝出来用。

   public void notifyNativeSizeChanged(int w, int h) {
 
        Message message = Message.obtain();
        WXDomTask task = new WXDomTask();
        task.instanceId = getInstanceId();
        if (task.args == null) {
            task.args = new ArrayList<>();
        }
 
        JSONObject style = new JSONObject(2);
 
        Spacing padding = getDomObject().getPadding();
        Spacing border = getDomObject().getBorder();
 
        int top = (int) (padding.get(Spacing.TOP) + border.get(Spacing.TOP));
        int bottom = (int) (padding.get(Spacing.BOTTOM) + border.get(Spacing.BOTTOM));
        float webW = WXViewUtils.getWebPxByWidth(w);
        float webH = WXViewUtils.getWebPxByWidth(h) + top + bottom;
 
        style.put("height", webH + 1);
        style.put("width", webW + 1);
 
        task.args.add(getRef());
        task.args.add(style);
        message.obj = task;
        message.what = WXDomHandler.MsgType.WX_DOM_UPDATE_STYLE;
        WXSDKManager.getInstance().getWXDomManager().sendMessage(message);
 
        //   ((WXDomObject) getDomObject()).setStyleHeight(h);
        // ((WXDomObject) getDomObject()).setLayoutHeight(h);
        //WXSDKManager.getInstance().getWXDomManager().postRenderTask(getInstanceId());
//        Message msg = Message.obtain();
//        msg.what = WXDomHandler.MsgType.WX_CONSUME_RENDER_TASKS;
//        WXDomTask task = new WXDomTask();
//        task.instanceId = getInstanceId();
//        task.args = new ArrayList<>();
//        task.args.add(null);
//        msg.obj = task;
//        WXSDKManager.getInstance().getWXDomManager().sendMessage(msg);
    }

高度计算需要算上padding和border,不然会显示不全,获取方式源码都有提供。

综上发起更新style改变高度的方法如下:

    private void reSetHeight() {
        int contentHeight = webView.getContentHeight();
        notifyNativeSizeChanged(screenWidth, WXViewUtils.dip2px(contentHeight));
     }

获取webview内容高度最准确方式

webview获取高度是个小坑,

1,setWebViewClient和setWebChromeClient分别在有些机型上不回调。

2,onPageFinished执行完并不一定加载完

比较推荐自定义webview在onDraw中获取getContentHeight,因为不管webview设置的高度够不够,都会绘制完。

public class DisplayX5Webview  extends X5WebView {
    public interface DisplayFinish{
        void After();
    }
    DisplayX5Webview.DisplayFinish df;
    public void setDisplayListner(DisplayX5Webview.DisplayFinish df) {
        this.df = df;
    }
    public DisplayX5Webview(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public DisplayX5Webview(Context context) {
        super(context);
    }
    //onDraw表示显示完毕
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        df.After();
    }
}

setDisplayListner回调中调用reSetHeight。以 if (oldContentHeight < contentHeight) 判断,只取大的高度值。

    private void reSetHeight() {
 
        int contentHeight = webView.getContentHeight();
        if (oldContentHeight < contentHeight) {
            notifyNativeSizeChanged(screenWidth, WXViewUtils.dip2px(contentHeight));
            oldContentHeight = contentHeight;
        }
    }

相关文章

  • 安卓自定义weex控件webview,解决scrollView嵌

    weex页面有scrollView嵌套web的页面,如果不给web指定高度就显示不出来。RN也有这个问题,查了下全...

  • 【10】WebView

    一、什么是WebView WebView类是安卓中用来显示网页的帮助类,通过使用WebView控件可显示网页内容,...

  • AndroidCustomViews

    AndroidCustomViews 方便安卓开发者使用的自定义控件库 加入Gradle依赖 自定义控件列表 Nu...

  • 自定义的控件简介

    android 自定义控件简介 安卓中的自定义控件可以分为三种: 通过将系统提供的控件组合,成为新的控件 自定义V...

  • 2018-11-07

    Android 加载长图片的自定义控件!解决安卓加载长图出现解码失败导致显示空白的问题! 关于解决超大图片超过bi...

  • WKWebView坑记录

    如果在wkwebview下设置了自定义scrollview代理(webview.scrollview.delega...

  • 关于安卓自定义地址选择控件的实现

    标题:关于安卓自定义地址选择控件的实现 对于安卓地址选择,我们常常会使用三方控件,今天博主就写一款自定义的地址选择...

  • Android-常用控件

    1. WebView 2. ScrollView 能包含一个子控件,若想添加多个控件,可添加一个布局 问题解决:自...

  • Android Touch System(一)

    前言 之前写了一篇文章是关于自定义控件的。在学习自定义view的时候顺便把安卓的touch system(安卓触摸...

  • 动态获取WebView中的数据

    在安卓中显示html页面可以使用WebView控件,那么如何从WebView中动态地获取到数据呢。其实很简单。先上...

网友评论

      本文标题:安卓自定义weex控件webview,解决scrollView嵌

      本文链接:https://www.haomeiwen.com/subject/hsekdctx.html