• 【WP8.1】WebView笔记


    之前在WP8的时候做过WebBrowser相关的笔记,在WP8.1的WebView和WebBrowser有些不一样,在这里做一些笔记

    下面分为几个部分

      1、禁止缩放

      2、JS通知后台C#代码(notify)

      3、C#调用JS方法

        动态加载JS文件,动态注册事件方法(eval)

      4、WebView导航

      5、手势(WinJS)

      6、常见问题

    1、禁用缩放  

    body {
        /* Block area from manipulation actions (zoom, pan) */
        touch-action: pan-y;
    }

      这个值可以禁用掉缩放和横向手势

      关于touch-action参见:https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh767313.aspx?f=255&MSPPError=-2147217396

    2、JS通知后台C#代码(notify) 

    window.external.notify(msg);

    注意:这里的方法名是小写的,在WP8上后面的Notify方法的首字母是大小写都可以

    3、C#调用JS方法(InvokeScriptAsync)

      通过后台代码动态加载css,js,动态绑定事件和方法

      3.1、先定义传输的数据格式

    /// <summary>
    /// JS消息格式
    /// </summary>
    public class JsInvokeModel
    {
        [JsonProperty("type")]
        public string Type { get; set; }
    
        [JsonProperty("content1")]
        public string Content1 { get; set; }
    
        [JsonProperty("content2")]
        public string Content2 { get; set; }
    
        [JsonProperty("content3")]
        public string Content3 { get; set; }
    }

      3.2、XAML

    <WebView x:Name="WebView" DOMContentLoaded="WebView_OnDOMContentLoaded" ScriptNotify="WebView_OnScriptNotify" />

      3.3、下面是事件方法

    //DOM树加载完成后执行
    private async void WebView_OnDOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
    {
        //1、动态加载css
        var js = @"var myCss = document.createElement(""link"");
            myCss.rel = ""stylesheet"";
            myCss.type = ""text/css"";
            myCss.href = ""ms-appx-web:///Assets/Html/css/default.css"";
            document.body.appendChild(myCss);";
        await sender.InvokeScriptAsync("eval", new[] { js });
    
        //2、动态加载js库(json2)
        js = @"var myScript = document.createElement(""script"");
            myScript.type = ""text/javascript"";
            myScript.src = ""ms-appx-web:///Scripts/json2.min.js"";
            document.body.appendChild(myScript);";    
        await sender.InvokeScriptAsync("eval", new[] {js});
        
        //3、调用js执行自定义代码(为图片添加点击事件,并通知)
        js = @"var imgs = document.getElementsByTagName(""img"");
               for (var i = 0, len = imgs.length; i < len; i++) {
                   imgs[i].onclick = function (e) {
                       var jsonObj = { type: 'image', content1: this.src };
                       window.external.notify(JSON.stringify(jsonObj));
                   };
               }";
        await sender.InvokeScriptAsync("eval", new[] {js});
    
        //4、动态加载手势
        js = @"var myScript = document.createElement(""script"");
            myScript.type = ""text/javascript"";
            myScript.src = ""ms-appx-web:///Assets/Html/js/gesture.js"";
            document.body.appendChild(myScript);
            window.external.notify(myScript.src+"""");";
        await sender.InvokeScriptAsync("eval", new[] { js });
    
        //5、为body添加手势监听
        js = @"var target = document.getElementsByTagName(""body"")[0];
               prepareTarget(target, eventListener);";
        await sender.InvokeScriptAsync("eval", new[] { js });
    }

      3.4、Notify监听

    private void WebView_OnScriptNotify(object sender, NotifyEventArgs e)
    {
        //这个事件函数可以监听到JS通知的消息,消息类型为文本
        //这里统一消息格式为:JsInvokeModel
        var model = JsonConvert.DeserializeObject<JsInvokeModel>(e.Value);
        switch (model.Type)
        {
            case "image":
                Info.Text = e.Value;
                break;
            case "swiperight":
                //右滑
                Info.Text = e.Value;            
                break;
            case "swipeleft":
                //左滑
                Info.Text = e.Value;
                break;
            case "text":
                Info.Text = e.Value;
                break;
        }
    }

      WebView虽然提供了同步方法InvokeScript,但是在WP8.1没有实现

       通过InvokeScriptAsync,可以做更多操作,例如,相信对于更换颜色(夜间模式),修改字体大小等

    4、WebView导航 

    两种方式
    //后退
        //WebView.GoBack();
        await WebView.InvokeScriptAsync("eval", new []{"history.go(-1)"});
    
        //刷新
        //WebView.Refresh();
        await WebView.InvokeScriptAsync("eval", new[] { "history.go()" });        
    
        //前进
        //WebView.GoForward();
        await WebView.InvokeScriptAsync("eval", new[] { "history.go(1)" });

    5、手势

      由于WebView的内部结构与WebBrowser不同,WebView无法监听到Manipulation事件

      场景:当我们需要在PivotItem中放置WebView的时候,左右滑动无法切换PivotItem,下面通过JS手势监听WebView上面的手势操作,然后传到后台代码进行处理,这里没有做实时处理,只是监听了手势离开时的速度判断左右滑动

      5.1、定义手势监听事件方法

    var gesture;
    //记录手势操作开始位置
    var gestureStartX;
    
    //触发Id,防止重复触发,触发Id与手势Id
    var gestureId = 1;
    var lastGestureId = 0;
    
    //速度触发
    var gestureVector = 1.5;
    
    //注册手势事件
    function prepareTarget(target, eventListener) {
        //var target = document.getElementById(targetId);
        target.addEventListener("MSGestureStart", eventListener, false);
        target.addEventListener("MSGestureEnd", eventListener, false);
        target.addEventListener("MSGestureChange", eventListener, false);
        target.addEventListener("MSInertiaStart", eventListener, false);
        //target.addEventListener("MSGestureTap", eventListener, false);
        //target.addEventListener("MSGestureHold", eventListener, false);
        target.addEventListener("pointerdown", onPointDown, false);
        target.addEventListener("pointerup", onPointUp, false);
    
        gesture = new MSGesture();
        gesture.target = target;
    }
    
    function onPointUp(e) {
        //把触发时间参数传到gesture
        gesture.addPointer(e.pointerId);
    }
    
    function onPointDown(e) {
        //把触发时间参数传到gesture
        gesture.addPointer(e.pointerId);
    }
    
    //手势事件
    //具体的属性参见:https://msdn.microsoft.com/zh-cn/library/ie/hh772076%28v=vs.85%29.aspx
    function eventListener(evt) {
        var myGesture = evt.gestureObject;
        if (evt.type == "MSGestureStart") {
            //开始触发,记录初始位置
            gestureStartX = evt.clientX;
        }
        else if (evt.type == "MSInertiaStart") {
            if (lastGestureId == gestureId || evt.velocityX == "undefined") {
                return;
            } else {
                //释放时触发惯性事件,判断手势释放时的速度
                if (evt.velocityX > gestureVector) {
                    var jsonObj = { type: "swiperight" };
                    window.external.notify(JSON.stringify(jsonObj));
                    lastGestureId = gestureId;
                } else if (evt.velocityX < -gestureVector) {
                    jsonObj = { type: "swipeleft" };
                    window.external.notify(JSON.stringify(jsonObj));
                    lastGestureId = gestureId;
                }
            }
        }
        else if (evt.type == "MSGestureChange") {
            //if (lastGestureId == gestureId) {
            //    return;
            //} else {
            //    var change = evt.clientX - gestureStartX;
            //    window.external.notify("clientX:" + change);
            //}
        } else if (evt.type == "MSGestureEnd") {
            //手势结束,Id+1
            gestureId = gestureId + 1;
            myGesture.reset();
        }
    }
    gesture.js

      5.2、在WebView加载完成后,动态加载改JS文件,获取body标签,然后监听事件(参见3.3)

      5.3、当事件触发的时候改变Pivot.SelectedIndex,这样就能实现在WebView上滑动切换PivotItem

    private void WebView_OnScriptNotify(object sender, NotifyEventArgs e)
    {
        //这个事件函数可以监听到JS通知的消息,消息类型为文本
        //这里统一消息格式为:JsInvokeModel
        var model = JsonConvert.DeserializeObject<JsInvokeModel>(e.Value);
        switch (model.Type)
        {
            case "image":
                Info.Text = e.Value;
                break;
            case "swiperight":
                Info.Text = e.Value;
                if (pivot.Items != null)
                {
                    if (pivot.SelectedIndex > 0)
                    {
                        pivot.SelectedIndex--;
                    }
                    else
                    {
                        pivot.SelectedIndex = pivot.Items.Count - 1;
                    }
                }
                break;
            case "swipeleft":
                Info.Text = e.Value;
                if (pivot.Items != null)
                {
                    if (pivot.SelectedIndex < pivot.Items.Count - 1)
                    {
                        pivot.SelectedIndex++;
                    }
                    else
                    {
                        pivot.SelectedIndex = 0;
                    }
                }
                break;
            case "text":
                Info.Text = e.Value;
                break;
        }
    }

    6、常见问题

      6.1、alert和prompt方法在WebView失效,如果需要,可以使用 window.external.notify('message');然后再后台代码进行处理

        6.2、如果是导航到本地路径(ms-appdata:///local/),需要注意

        6.2.1、必须要将html需要的资源和html文件放在同一个文件夹下,html的资源路径不管是不是”/”开头,webview都认为是相对路径

        6.2.2、如果html中要的本地文件,如果在对应目录中不存在,wp上会直接跳出应用,大概过1分钟左右崩溃,且捕获不到异常。Windows上没有这现象

        6.2.3、不支持js的window.exteranl.notify功能

    webView1.Navigate(new Uri("ms-appdata:///local/data/html/sample.htm"));

        不知道为什么WebView在导航到本地路径的时候  window.external.notify 会失效,如果需要可以考虑使用 内容流的方式加载本地文件

        下面做简单的演示

          1、定义内容留转换器

        public class HtmlStreamUriResolver : IUriToStreamResolver
        {
            public IAsyncOperation<IInputStream> UriToStreamAsync(Uri uri)
            {
                string path = uri.AbsolutePath;
                return GetContent(path).AsAsyncOperation();
    
            }
    
            // 根据 uri 返回对应的内容流
            private async Task<IInputStream> GetContent(string path)
            {
                if (path.StartsWith("http"))
                {
                    // http 方式获取内容数据
                    var client = new HttpClient();
                    var response = await client.GetAsync(new Uri(path), HttpCompletionOption.ResponseHeadersRead);
                    return (await response.Content.ReadAsStreamAsync()).AsInputStream();
                }
                else if (path.StartsWith("/local/cachedata/html/"))
                {
                    path = string.Format("ms-appdata://{0}", path);
                    // 获取本地数据
                    var fileRead = await StorageFile.GetFileFromApplicationUriAsync(new Uri(path));
                    return await fileRead.OpenAsync(FileAccessMode.Read);
                }
                else
                {
                    // 获取本地数据
                    var fileRead = await StorageFile.GetFileFromApplicationUriAsync(new Uri(path, UriKind.Absolute));
                    return await fileRead.OpenAsync(FileAccessMode.Read);
                }
            }
        }
    HtmlStreamUriResolver

          2、生成,导航,html中的所有引用都通过HtmlStreamUriResolver返回,具体逻辑在里面处理,通过这样导航的页面可以使用 window.external.notify 方法

      //生成内容流
      var htmlStreamUriResolver = new HtmlStreamUriResolver();
      var uri = webView.BuildLocalStreamUri("tag", "/local/cachedata/html/sample.html");
      webView.NavigateToLocalStreamUri(uri, htmlStreamUriResolver);

    7、Demo

       http://files.cnblogs.com/files/bomo/WebViewDemo.zip

    8、参考链接

      手势事件参数说明:https://msdn.microsoft.com/zh-cn/library/ie/hh772076%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

      WinJS手势:https://msdn.microsoft.com/zh-cn/library/ie/hh968249(v=vs.85).aspx

      Win8.1和WP8.1在UniversalAPP中WebView的研究:http://blog.csdn.net/hzdinglai/article/details/41073739

      重新想象 Windows 8.1 Store Apps (81) : WebView:http://www.cnblogs.com/webabcd/p/3803384.html

    关于WP8的WebBrowser,请移步:http://www.cnblogs.com/bomo/p/3949994.html

    个人能力有限,如果上文有误或者您有更好的实现,可以给我留言

    转载请注明出处:http://www.cnblogs.com/bomo/p/4320077.html 

  • 相关阅读:
    iOS7上在xib中使用UITableViewController设置背景色bug
    Android 转载一篇.9图片详解文章
    Android 中4种屏幕尺寸
    网络传输中的三张表,MAC地址表、ARP缓存表以及路由表
    防火墙简介
    Makefile有三个非常有用的变量。分别是$@,$^,$
    makefile简单helloworld
    Python异常处理try except
    shell 读取配置文件的方法
    ubuntu 添加开机启动服务
  • 原文地址:https://www.cnblogs.com/bomo/p/4320077.html
Copyright © 2020-2023  润新知