博客园客户端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>

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

展示在线页面

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

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

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

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

展示应用文件

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

这个功能的实现其实使用的方法和在线是一样的,都是调用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"));
        }

展示内存中字符串的内容

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

            this.wv.NavigateToString(html);
        }

和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

时间: 2024-08-25 13:43:40

博客园客户端UAP开发随笔 -- App连接云端内容的桥梁:WebView的相关文章

博客园客户端UAP开发随笔 -- App的心动杀手锏:动画

前言 在前面一篇“新年快乐”的随笔中,我们介绍了WinRT中的简单动画实现.其实在使用Windows/Windows Phone时,我们都会看到一些动画,最简单的比如按下一个button时,该button的状态变化就是动画的一种.再比如弹出式窗口或菜单,也是一种动画.WinRT中的动画种类很多,但是分类有点儿让初学者摸不着头脑:主题过渡,主题动画,视觉转换,情节提要动画.这些我们就不说了,这里主要说说自定义动画,或者说是情节提要动画(Storyboard Animation),因为这种动画是我们

博客园客户端UAP开发随笔 -- App UI设计的三大纪律八项注意

前言 每一个页面都是这个App的门面,尤其是主页面,看上去干净整洁清爽宜人容易操作,那么你的App就成功了一半.这也反映出了你这个开发团队的基本审美素质和设计理念.如果你不是一个团队,而是一个个人开发者,建议你好好读读以下心得体会,相信会帮助你做出好看而实用的App.用一堆拥有丑陋UI的App充斥Window Store,不是我们高大上的程序员所为,被其他手机开发平台的开发者们耻笑. 三大纪律: 1)不乱用颜色.一个页面内不要超过3种颜色 2)不乱用大图片当背景.你是想让用户看你的背景图片呢,还

博客园客户端UAP开发随笔 – 让自己的App连接世界(2):WinRT中的内置分享

看到一篇眼前一亮的博文,是不是有一种希望其他小伙伴都能看到的感觉呢?有没有一种“不转不是程序员”的冲动呢?在 PC 浏览器上看到还好办,直接网址复制,另一边 IM 上就发过去了,但是如果是 App 中的内容,就没这么方便了,总不能那边 IM 上喊话:“隔壁老王,博客园上有篇叫‘博客园客户端(Universal App)开发随笔 – 为应用插上分享的翅膀’的博文超好看,要不你也瞅瞅?”.隔壁老王再去搜索就太麻烦了.可能你会说了,嗨,直接分享不就完了么.嗯,没错,就是分享功能.那么如何把分享功能引入

博客园客户端UAP开发随笔 -- 搭建App之间的桥梁

开发Windows Phone应用的同学们应该都注意到了,Windows Phone 为了安全性,对应用的限制还是比较多的.我记得一位360的同学很无奈的说:WP太安全了,我们这些做WP上360卫士的基本上没啥可做的.但是当WP360那个App出来后,还是有很多用户安装了呢,尽管真的对安全没什么太大的帮助,但是对用户了解自己的手机的使用情况还是有帮助的.其中一位用户的评价是:从android到PC,我一直用360, 所以在WP上我也用.呵呵,粉丝啊!具体有什么用其实他也不关心铁粉而已. 扯远了!

博客园客户端UAP开发随笔 -- 让你自己和你的App有国际范儿

大家是不是发现,在商店中看到的高大上的应用都有着多语言支持,可以根据操作系统的语言自动适配:或者可以通过用户的选择,显示对应的语言界面,确实很高大上呢.不过这个可不是什么难事,通过简单的几个步骤,让你的应用也高大上起来,支持多语言.这样在你以后的简历里,也可以写上:面向国际市场开发过多语言应用.听上去相当有底气! 0. 准备工作 在建立多语言支持前,首先要看一下 Manifest 文件中的默认语言选项,将它设置为你希望的默认显示语言. 接下来建立存放语言字符串的文件夹.如果是Universal

博客园客户端UAP开发随笔 -- 狡兔三窟:App内的三种通知消息的实现

使用应用时,总会有各种各样的交互,其中有些是需要和用户交互的,有些是仅仅告知用户某些信息的.对于前者,通常的解决方案都是弹出一个带有按钮(或其他控件)的对话框,上面有需要用户知晓的信息,以及需要用户通过按钮(或其他控件)做出的响应交互,这里就不再介绍.对于后者,那些不需要用户做出交互,仅仅是告知用户信息的,实现方式大家各有不同,本文将提出几种解决思路,抛砖引玉,希望通过交流,得到更好的人机交互解决方案. 1. 弹出窗口提示 这个方法比较简单粗暴,直接调用了系统的 MessageDialog 方法

博客园客户端UAP开发随笔 -- 让自己的App连接世界:WinRT中的微博分享

近年来社交app可谓红红火火,大家每天发微博,分享到朋友圈也是不亦乐乎.我们的Universal应用自然也希望加入社交分享的功能.然而国内主流的社交平台微博和微信还没有推出适用于Universal应用的SDK,怎么办呢?当然敲碗等开饭也是办法,另一方面我们也可以自己做一些努力.今天我们就想分享一下我们在Universal应用中实现分享到微博功能上的探索. 准备 想要分享到微博,先得注册成为微博开发者.我们在http://open.weibo.com/上注册好开发者账号,建立个测试应用,就可以拿到

博客园客户端UAP开发随笔 -- 奔跑吧,页面!

前言 页面导航,是App中的基本功,一般的App,一来一去,只需要简单的Navigate + Back就行了,一个复杂的App可能需要很多导航模式的混合才能实现最佳用户体验. SplashScreen 启动屏幕 我们先从最开始的SplashScreen说起吧.如果你把启动屏幕做成一个Page,启动时先显示一下,然后假装忙乎两秒,跳到下一个主页面开始进入正题,这个好像看上去也很美好.但是当用户玩命儿按Back键时,哦,露出马脚了,启动页面被唤出了.不过这个bug倒是不妨作为一个新奇的体验. MSD

博客园客户端UAP开发随笔--自定义控件的左膀右臂

前言 我们上一次说到了App的精灵:自定义控件.这一次,我们接着这一话题,说说自定义控件的两个得力助手: 选择器 - TemplateSelector 转换器 – Converter 这两个东西能帮助自定义控件更为简单方便地被使用,所以必须掌握. 数值转换器 Converter 这个大家可能不陌生,因为在MSDN里,介绍到Data Binding时,总会顺带着介绍一下数据转换,比如这个网页: http://msdn.microsoft.com/library/windows/apps/xaml/