[Windows Phone] 無聊小範例 - 在Windows Phone 8手機螢幕上畫出指定長度的線條

最近在MSDN論壇上看到有人發問一個問題:「要如何在手機上畫出3公分的直線?」。
乍看之下這應該是一個再也簡單不過的事情,啊不就拿尺來量一量就好了!? 連小學生都會的事情啊~~(被打)
不過事情往往沒有想像中那麼簡單.... 這個問題牽扯到不同裝置有不同的解析度,所以,同樣都是三公分,但是在不同的裝置上面的像素可是大不相同的啊~~

 

最近在MSDN論壇上看到有人發問一個問題:「要如何在手機上畫出3公分的直線?」。

乍看之下這應該是一個再也簡單不過的事情,啊不就拿尺來量一量就好了!? 連小學生都會的事情啊~~(被打)

不過事情往往沒有想像中那麼簡單.... 這個問題牽扯到不同裝置有不同的解析度,所以,同樣都是三公分,但是在不同的裝置上面的像素可是大不相同的啊~~

 

有在看廣告的朋友們應該知道,現在的 Windows Phone 8 手機支援的解析度除了之前的 WVGA(800 x 480)、WXGA(1,280 x 768)和 720p(1,280 x 720)三種之外,Nokia最近推出的新機型 Lumia 1520 及 Lumia 1320 兩款手機又支援了 1080p (1,920 x 1080)的螢幕解析度;除了有不同的螢幕解析度這個變數存在之外,目前Windows Phone 8 手機螢幕的尺寸也從 4 吋(例如 Nokia Lumia 520)到 6 吋(例如 Nokia Lumia 1520)都有 ,而且不排除之後還可能推出更多種不同解析度與螢幕大小搭配的手機。

而處於「螢幕解析度」和「螢幕尺寸」這兩個可變的因素存在的情況下,寫出來的App不能只限定於特定型號的手機上都能順利畫出符合需求長度的線條就是第一個即將面臨的課題。

 

以下是完成本範例的思考過程:

1. 由於要考慮到不同裝置都可以使用,所以我們就可以先透過 手機螢幕的寬度(英吋)除以 手機的水平解析度 來計算出裝置的 PPI(Pixels Per Inch / 每一吋的像素個數)這個數值,如此一來就可以解決跨裝置執行的問題。

2. 將 PPI 值 除以 2.54 就可以得到每公分的像素個數。

3. 再將每公分的像素個數 乘上 要求線條的長度(例如 3 公分),就可以得到實際需要的像素。

 

經過以上的推論,距離成功應該不遠了!!~  看起來,我只要能拿到螢幕的尺寸和解析度這兩個關鍵的數據,接下來問題就可以迎刃而解!! 不過呢~~老話一句:「事情往往沒有想像中那麼簡單....」。

除了上述的幾個專業術語之外,Windows Phone 8 為了要支援更多元化的螢幕配置,所以還有一個關鍵的變數 – Scale Factor (縮放比例)的存在,系統會在不同的裝置上自動套用符合該裝置的 Scale Factor 去縮放畫面上的物件,以讓不同的裝置都能得到看起來相同的 Look and Feel。而且在 GDR 3 之後也加入了相關的 API 可供開發時使用(有興趣的同學可以參考 如何充分利用 Windows Phone 高清屏幕 一文)。

 

另外,身為一個懶人,去讀出上面提到的那些數值之後還要算出裝置實際顯示時的 PPI 也是一件讓我頭痛的事。所以 MSDN 上的 Taking Advantage of Large Screen Windows Phones 範例中的 ScreenSizeSupport 專案就成為幫助我偷懶的好幫手了。 只要透過 ScreenSizeSupport 專案中的 DisplayInformationEx 類別裡面的 ViewPixelsPerInch 屬性就可以取得實際每英吋可顯示的像素個數了。

 

再來,就可以動手寫程式啦!!~  先建立一個 Windows Phone 8 的專案,並且將 ScreenSizeSupport 專案加入參考。

 

我們先來寫一個ValueConverter用來把公分數轉換為像素:

CentimeterToPixelsConverter.cs

public class CentimeterToPixelsConverter : IValueConverter
    {
        private const double CentimeterToInchFactor = 2.54;

        public object Convert( object value , Type targetType , object parameter , System.Globalization.CultureInfo culture )
        {
            double pixelsForOneCentimeter = DisplayInformationEx.Default.ViewPixelsPerInch / CentimeterToInchFactor;

            double actualPixels = double.Parse( value.ToString() ) * pixelsForOneCentimeter;

            return actualPixels;
        }

        public object ConvertBack( object value , Type targetType , object parameter , System.Globalization.CultureInfo culture )
        {
            throw new NotImplementedException();
        }
    }

 

再來是UI的部份,我們會透過一個 Slider 來決定線條的長度,為了讓 Slider 的刻度更加明顯,所以需要先從 Nuget 安裝 Windows Phone Toolkit Customized by Kinnara 套件,並且使用裡面的 PhoneSlider 來取代原來的 Slider:

MainPage.xaml

<phone:PhoneApplicationPage
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
	xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	xmlns:local="clr-namespace:LineLengthByDpiSample"
	xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
	x:Class="LineLengthByDpiSample.MainPage"
	mc:Ignorable="d"
	SupportedOrientations="Landscape"
	Orientation="Landscape"
	shell:SystemTray.IsVisible="True">
	<phone:PhoneApplicationPage.Resources>
		<local:CentimeterToPixelsConverter
			x:Key="CentimeterToPixelsConverter" />
	</phone:PhoneApplicationPage.Resources>
	<phone:PhoneApplicationPage.FontFamily>
		<StaticResource
			ResourceKey="PhoneFontFamilyNormal" />
	</phone:PhoneApplicationPage.FontFamily>
	<phone:PhoneApplicationPage.FontSize>
		<StaticResource
			ResourceKey="PhoneFontSizeNormal" />
	</phone:PhoneApplicationPage.FontSize>
	<phone:PhoneApplicationPage.Foreground>
		<StaticResource
			ResourceKey="PhoneForegroundBrush" />
	</phone:PhoneApplicationPage.Foreground>
	<Grid
		x:Name="LayoutRoot"
		Background="Transparent">
		<Grid.RowDefinitions>
			<RowDefinition
				Height="Auto" />
			<RowDefinition
				Height="*" />
		</Grid.RowDefinitions>
		<StackPanel
			x:Name="TitlePanel"
			Grid.Row="0"
			Margin="12,17,0,28">
			<TextBlock
				Text="無聊小範例"
				Style="{StaticResource PhoneTextNormalStyle}"
				Margin="12,0" />
			<TextBlock
				Text="有尺之徒"
				Margin="9,-7,0,0"
				Style="{StaticResource PhoneTextTitle1Style}" />
		</StackPanel>
		<Grid
			x:Name="ContentPanel"
			Grid.Row="1"
			Margin="12,0,12,0">
			<StackPanel
				Orientation="Horizontal"
				HorizontalAlignment="Center">
				<TextBlock
					Text="您裝置的視覺解析度為每英吋可顯示 "
					FontSize="24" />
				<TextBlock
					Name="txtPpi"
					FontSize="24" />
				<TextBlock
					Text=" 個像素。"
					FontSize="24" />
			</StackPanel>
			<StackPanel
				Orientation="Vertical"
				HorizontalAlignment="Center"
				VerticalAlignment="Center"
				Margin="0,24,0,0">
				<TextBlock
					HorizontalAlignment="Center"
					TextWrapping="Wrap"
					Text="請拖動下方的滑桿來畫線"
					VerticalAlignment="Top"
					FontSize="36" />
				<toolkit:PhoneSlider
					Name="slider"
					TickFrequency="0.5"
					Maximum="7"
					Width="600"
					SmallChange="0.5" />
				<Border
					BorderBrush="White"
					BorderThickness="1"
					VerticalAlignment="Top"
					HorizontalAlignment="Center"
					Width="{Binding Value, Converter={StaticResource CentimeterToPixelsConverter}, ElementName=slider}"
					Margin="0,24" />
				<StackPanel
					Orientation="Horizontal"
					HorizontalAlignment="Center"
					Margin="0,24,0,0">
					<TextBlock
						HorizontalAlignment="Center"
						TextWrapping="Wrap"
						Text="線條長度為 "
						VerticalAlignment="Top"
						FontSize="32" />
					<TextBlock
						HorizontalAlignment="Center"
						TextWrapping="Wrap"
						Text="{Binding Value, ElementName=slider}"
						VerticalAlignment="Top"
						FontSize="32" />
					<TextBlock
						HorizontalAlignment="Center"
						TextWrapping="Wrap"
						Text=" 公分"
						VerticalAlignment="Top"
						FontSize="32" />
				</StackPanel>
			</StackPanel>
		</Grid>
	</Grid>
</phone:PhoneApplicationPage>

 

以及 UI 的 CodeBehind:

MainPage.xaml.cs

using Microsoft.Phone.Controls;
using ScreenSizeSupport;

namespace LineLengthByDpiSample
{
    public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();

            var ppi = (int) DisplayInformationEx.Default.ViewPixelsPerInch;

            txtPpi.Text = ppi.ToString();
        }
    }
}

 

Ok,大功告成!!~ (透過模擬器執行的結果與發佈到手機上的版本會有落差,請以發佈到手機的版本為準。)

image

 

最後,奉上專案原始檔,請自行服用。

SampleDownload6