當你辛苦的從網上爬下來一篇文章之后,怎么在你的應用內展示這些包含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中則不行。
?
<Pagex: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 = "<h1>我是一個字符串</h1>";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 = "<h1>我是一個字符串</h1>";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[] { "<h2>我是一個有很多內容的html頁面,真的很多。。。</h2>"});}}
小結
以上介紹了使用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