zoukankan      html  css  js  c++  java
  • 博客园客户端UAP开发随笔 -- App连接云端内容的桥梁:WebView

    当你辛苦的从网上爬下来一篇文章之后,怎么在你的应用内展示这些包含HTML标记的文章?如果你使用的是Javascript开发应用,恭喜你,直接塞进页面就可以了,同时说明你很熟悉页面开发,而现在windows也支持这种方式。但是对于使用XAML开发的应用怎么办呢?我们还有WebView控件可以用。

    越来越多的服务器端API返回的数据使用HTML了,所以我们也不得不对WebView多了解一些。

    WebView有个Bug:放在Grid里时,最右侧有一个pixel缝隙时隐时现。要小心,别让PM抓住你的小辫子。另外,在一个App里大量使用WebView要小心内存。在Silverlight中,一个WebBrowser(不叫WebView)要占用大概30M吧,如果把Script disable掉可以省很多。在WinRT中情况就好多了,我开过6个WebView也就150M。

    准备工作

    WebView控件可以用来展示在线页面,应用内的离线页面,甚至是内存中拼接出来的html字符串也可以。下面我们介绍下这3个方式的使用:

    首先在页面上添加一个WebView控件和3个按钮,这里需要注意的是,最好是吧WebView放在Grid中,因为这样WebView默认会填充整个区域,而在StackPanel中则不行。

    <Page
        x:Class="WebViewTest.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebViewTest"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="1*"></RowDefinition>
                <RowDefinition Height="50"></RowDefinition>
            </Grid.RowDefinitions>
    
            <WebView x:Name="wv" Margin="10,20"/>
    
            <StackPanel Grid.Row="1" Orientation="Horizontal">
                <Button x:Name="online" Click="online_Click">在线页面</Button>
                <Button x:Name="offline" Click="offline_Click">应用文件</Button>
                <Button x:Name="memory" Click="memory_Click">内存中</Button>
            </StackPanel>
        </Grid>
    </Page>

    跑起来之后,我们的页面是下面的丑样子,这是因为我们还没加载内容。。。

    01

    展示在线页面

        展示在线页面是最基本的需求了,使用这个功能,你就可以做一个自定义的浏览器了。。

        这个功能实现非常简单,我们在online_Click中添加一行代码就可以了。

    private void online_Click(object sender, RoutedEventArgs e)
            {
                this.wv.Navigate(new Uri("http://www.microsoft.com"));
            }

    如上面的代码,我们只需要调用WebView的Navigate方法,传入网页的地址即可。现在点击在线页面按钮,我们的应用有点浏览器的影子了。。

    02

    展示应用文件

       通常我们会在应用内使用离线的页面文件来展示内容,比如一个帮助页面,或者用来展示内容的页面框架(这个之后会介绍)。

       这个功能的实现其实使用的方法和在线是一样的,都是调用Navigate,只是在Uri上有区别,因为我们要使用的是应用内的文件,这里我们需要在构造Uri的时候,使用ms-appx-web这个Uri Scheme,更多关于Uri Scheme的内容,请点击这里

    private void offline_Click(object sender, RoutedEventArgs e)
            {
                // ms-appx-web 表示我们要读取的是应用内的一个文件,并且是在Web区域,相对路径是Data/help.html, 
                this.wv.Navigate(new Uri("ms-appx-web:///Data/help.html"));
            }

    03

    展示内存中字符串的内容

    private void memory_Click(object sender, RoutedEventArgs e)
            {
                var html = "&lt;h1&gt;我是一个字符串&lt;/h1&gt;";
    
                this.wv.NavigateToString(html);
            }

    04

    和dom交互

    使用WebView我们可以很方便的展示HTML页面,但是如果我们想要对dom做一些操作的话,还是需要使用javascript,比如调节字体大小,颜色等。这里用到的是InvokeScriptAsync方法,这个方法异步调用页面内指定的javascript方法。

    首先为之前的帮助页面添加一个方法changeColor,用来修改h2的字体颜色:

    <!DOCTYPE html>
    
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script>
    
            function changeColor()
            {
                document.getElementsByTagName("h2")[0].style.color = "red";
            }
    
        </script>
    </head>
    <body>
        <h2>这是个帮助页面</h2>
        <p>这里是帮助内容</p>
    </body>
    </html>

    然后在主页面上添加一个按钮(方法同前,这里就略过了),在点击事件中调用这个方法:

    private async void runCustomScript_Click(object sender, RoutedEventArgs e)
            {
                // 第一个参数是页面内的方法名,第二个是传入的参数列表,这个例子没有用到,这里用来演示下怎么用
                await wv.InvokeScriptAsync("changeColor", new []{"arg1", "arg2"});
            }

    点击之后,我们标题就红了。

    在前面的代码中,其实存在一个潜在的问题,如果我们页面有点复杂,在dom还没有加载分析完成的时候,方法changeColor或标题可能还不存在,这样我们的调用不但不会有效果,还会引起异常。

    所以需要在进行dom相关操作之前确认页面已经加载分析完成,WebView.DOMContentLoaded这个事件是在dom文档分析完成之后触发的,这样我们所有的dom操作就不会有错了。

    public sealed partial class MainPage : Page
        {
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
            private void online_Click(object sender, RoutedEventArgs e)
            {
                this.wv.Navigate(new Uri("http://www.microsoft.com"));
            }
    
            private void offline_Click(object sender, RoutedEventArgs e)
            {
                // ms-appx-web 表示我们要读取的是应用内的一个文件,并且是在Web区域,相对路径是Data/help.html, 
                this.wv.Navigate(new Uri("ms-appx-web:///Data/help.html"));
            }
    
            private void memory_Click(object sender, RoutedEventArgs e)
            {
                var html = "&lt;h1&gt;我是一个字符串&lt;/h1&gt;";
                this.wv.NavigateToString(html);
            }
    
            public MainPage()
            {
                this.InitializeComponent();
    
                this.NavigationCacheMode = NavigationCacheMode.Required;
    
                this.wv.DOMContentLoaded += wv_DOMContentLoaded;
            }
    
            // 我们新加入一个变量,用来标识当前页面是否分析完成
            bool domLoaded = false;
    
            void wv_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
            {
                domLoaded = true;
            }
    
            private async void runCustomScript_Click(object sender, RoutedEventArgs e)
            {
                if (domLoaded)
                {
                    // 第一个参数是页面内的方法名,第二个是传入的参数列表,这个例子没有用到,这里用来演示下怎么用
                    await wv.InvokeScriptAsync("changeColor", new[] { "arg1", "arg2" });
                }
            }
        }

    使用模板页

    现在如果你把页面的内容加的更多一些的话,你会发现WebView有一小段时间是空白的,这个就是在页面分析渲染完成之前的间隙。。。

    要解决这个问题,我们可以先让WebView加载下面这样一个很小的模板页。

    <!DOCTYPE html>
    
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="utf-8" />
        <title></title>
    
        <script>
            function setContent(content)
            {
                var container = document.getElementById("container");
                container.innerHTML = content;
            }
        </script>
    </head>
    <body>
        <div id="container"></div>
    </body>
    </html>

    这个模板页里只有一个简单框架和一个用来填充内容的方法,因为模板页很小且是本地的,所以加载渲染的很快,不会有白色的闪烁。然后通过调用javascript方法来把内容写入到dom中。

    private async void runCustomScript_Click(object sender, RoutedEventArgs e)
            {
                if (domLoaded)
                {
                    await wv.InvokeScriptAsync("setContent", new[] { "&lt;h2&gt;我是一个有很多内容的html页面,真的很多。。。&lt;/h2&gt;"});
                }
            }

    小结

    以上介绍了使用WebView的基本方法,还有一些别的技巧我们以后再说,比如调整成夜间模式,比如检测手势左右滑动,比如监测页面跳转......哎,有的公司就是靠这些技巧用WebView控件包装一下做了浏览器,但是可悲(或者可笑)的是,一些用户还在评论中说:不错,速度很快,流畅。除非有服务器端的数据压缩,否则能快到哪里去呢?

    分享代码,改变世界!

    Windows Phone Store App link:

    http://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc

    Windows Store App link:

    http://apps.microsoft.com/windows/zh-cn/app/c76b99a0-9abd-4a4e-86f0-b29bfcc51059

    GitHub open source link:

    https://github.com/MS-UAP/cnblogs-UAP

    MSDN Sample Code:

    https://code.msdn.microsoft.com/CNBlogs-Client-Universal-477943ab

  • 相关阅读:
    计算几何模板1 点部分
    TTimerThread和TThreadedTimer(都是通过WaitForSingleObject和CreateEvent来实现的)
    dddd
    Ubuntu中查看硬盘分区UUID的方法(所有Linux目录的解释)
    VS2010对C++11的支持列表(感觉大部分都不支持)
    VC版本的MakeObjectInstance把WNDPROC映射到类的成员函数
    FpGrowth算法
    Go语言Web框架gwk介绍2
    页面缓存OutputCache
    jquery mobile扁平化设计样式--Jquery mobile Flat UI介绍
  • 原文地址:https://www.cnblogs.com/ms-uap/p/4274484.html
Copyright © 2011-2022 走看看