歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 移動開發中“單位”的那些事兒

移動開發中“單位”的那些事兒

日期:2017/3/1 9:11:38   编辑:Linux編程

前言

在移動開發中我們常常會考慮度量單位的問題,在傳統pc的web開發中用的比較多的是px(css像素),在Android開發中一般則用dp、sp、px(物理像素)等,而移動web開發中同樣也是用px(css像素)做單位,通常會結合viewport進行縮放。本篇blog就梳理一下在移動開發中用到的各種單位以及他們之間的聯系和區別。

絕對單位和相對單位

如題,首先來看看什麼是絕對單位和相對單位:

  • 絕對單位:即值在任何環境下都不會變化的常量,如我們日常生活中經常會用到的in(inch英寸)、cm(厘米)、mm(毫米)、pt(磅,1磅等於1/72英寸)等,pt常用於標准印刷,這些絕對長度單位在界面設置中很少用到,僅作了解。

  • 相對單位:主要就是我們最熟悉的px,之所以是相對的因為它會隨著設備和環境的變化而變化,後面再具體說明,還包括在css中常用到的em、rem等。

屏幕尺寸、分辨率與像素密度

這是第二個話題,首先來看看屏幕尺寸,屏幕尺寸實際指的是矩形屏幕對角線的長度,單位是英寸(in),1英寸=2.54厘米,例如常見的筆記本電腦尺寸是14英寸,而在手機中大屏幕普遍是5.5英寸以上,而小屏手機的代表iphone4s則是3.5英寸。

接下來看一下分辨率,屏幕分辨率指的是橫縱向上的像素點數,單位是px,1px=1個像素點,一般以縱向像素*橫向像素來表示完整分辨率,例如魅族最新的旗艦手機pro6的分辨率就是:1920*1080,也就意味著pro6的屏幕是由2073600個像素點組成的,也就是說即使屏幕尺寸一樣,而像素不一樣的話(例如:iphone3和iphone4都是3.5英寸,而前者的分辨率是480*320,後者則是960*640),所能容納的像素點也是不一樣的,這一點就充分說明了android開發中控件單位不能用px的原因,比如將一個TextView設置為500px*500px,在相同的屏幕尺寸情況下,分辨率為480*320的設備上的占屏比要比960*640的設備上的占屏比大一倍,我們來舉個例子看看。

首先准備兩台android模擬器,一個是Google Nexus 4,4.7寸屏,分辨率為1280*768:

另一台是HTC One XL,為了演示我將它的分辨率修改為1920*1080 - 480dpi,這樣剛好能保證屏幕尺寸也為4.7,而且分辨率也高Nexus 4很多(關於為什麼修改成這個分辨率以及dpi的概念稍後再說):

接下來雙開這兩台模擬器跑一個簡單的android程序,僅僅是在一個線性布局中放了一個TextView,並將它的width和height都設置為500px,並設置了背景色方便觀察效果:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="500px"
        android:layout_height="500px"
        android:text="abc" 
        android:background="#FFFF00"
        />

</LinearLayout>

接下來看一下這兩台虛擬機的運行效果:

如上圖,左邊是高分辨率的HTC(1920*1080),右邊是低分辨率的Nexus(1280*768),它們的尺寸都是4.7英寸,但通過觀察發現明顯左邊的TextView占屏面積小,而又邊的占屏面積大的多,這就是android中不能用px做單位的原因,好了,通過這個問題我們又引入了一個新的概念,就是屏幕像素密度(pixels per inch),也就是ppi/dpi

首先看一下概念,屏幕像素密度指的是屏幕上每英寸可以顯示的像素點的數量,屏幕像素密度和屏幕尺寸以及屏幕分辨率有關,在單一變化條件下,屏幕尺寸越小、分辨率越高,則像素密度就越大。計算密度的公式是:屏幕對角線分辨率/屏幕尺寸。以上面的兩台虛擬機設備為例,4.7寸的HTC One XL的屏幕分辨率是1920*1080,那麼它的屏幕像素密度就是:

x=19202+10802−−−−−−−−−−−√4.7

通過勾股定理先算出對角線的像素,再除以屏幕尺寸,結果約等於470dpi,現在再回頭看我剛才為什麼要選1080*1920 - 480dpi呢?顯而易見就是為了選一個約等於4.7英寸屏幕的設備,這樣才方便和同是4.7英寸的Nexus進行比較,同理再計算一下Nexus 4的屏幕像素密度:

y=12802+7682−−−−−−−−−−√4.7

得到的結果是317.6,和設備參數中的320dpi基本一致:

現在再結合像素密度的概念想一下,我們在上面寫的android程序中,之所以高分辨率的HTC中的TextView占屏比較小,正是因為它的像素密度大,所以固定的500*500px尺寸的TextView在它的屏幕裡面就顯得占的地方小,而Nexus 4的像素密度小,所以500*500px這個像素對於它來說占的地方就會更大。最後總結一句話就是:屏幕尺寸相同的情況下,分辨率越高,像素密度就越大

再看下面這張圖應該就徹底清楚了它們的換算關系:

到這裡這三個概念就算介紹完畢了,雖然本篇blog的重點不是介紹android中的單位,但說到這裡,不得不提幾個相關的單位,例如:dp、sp、dip等等。

android中的單位

如題,依次來看看google為android引入的單位:

  • dp:又稱dip(ios中叫ppi),density independent pixels,即密度獨立像素,它和px有一個根據dpi按比例的換算關系,是一個android標准,即:以160dpi為基准,1dp=1px。結合上一小節的內容再想想,用dp做單位的話就不會再出現上面的窘境,因為分辨率不同,dpi也就不同,那麼換算的px也就不一樣了,這樣即使在不同分辨率的設備上也可以讓控件看起來一樣。
  • sp:與dp用法類似,只不過是android中專門用來定義文字大小的單位,即所有的fontSize都應用sp來度量。

android中主要就用到上面這兩種單位,還有一點值得提一下就是做過android開發的話都知道在項目工程中有這樣的目錄:

理論上我們是應該在每個目錄中放一套不同分辨率的圖片,app會根據設備的分辨率自動進行加載,那麼這些不同分辨率的數值標准又是什麼呢?google官方給出了以下標准:

沒錯,依然是通過像素密度(dpi)來做劃分,目前市場上的主流機型基本都在xxhdpi范圍內,所以一般情況下在xxhdpi放一套圖就能滿足需求了。蘋果手機則比較簡單的分為非高清屏、高清屏和超高清屏,對應的機型有iphone3(163ppi)、iphone6(326ppi)和iphone6 plus(401ppi),都是類似的道理。梳理了一遍這些單位之後,接下來就是要介紹移動頁面相關(mobile page)的單位以及viewpoint。

移動web單位和viewport

在移動web網頁開發中用到的單位也是px,但和上面談到的px要完全區分開,因為在安卓程序中談到的px指的就是設備的物理像素,而在移動網頁開發中css裡的px像素只是一個抽象單位,在不同的設備或不同的環境中,css中的1px所代表的設備物理像素是不同的,它的名字叫“設備獨立像素”,它是浏覽器中使用的抽象單位,也叫“css邏輯像素”,而設備物理像素和設備獨立像素的比值叫devicePixelRatio,它是移動浏覽器中的window對象的一個屬性。例如:在Retina屏的iphone上,devicePixelRatio的值為2,也就是說1個css像素相當於2個物理像素,同樣的用一個例子來說明。

首選准備一個簡單的頁面,只放一個大小為1280px*768px的div:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Test 01</title>
        <style>
            .pic{
                width: 768px;
                height: 1280px;
                background-color: #FFE872;
                color: white;
                font-size: 150px;
                text-align: center;
                line-height: 1280px;
                font-family: cursive;
            }
        </style>
    </head>
    <body>
        <div class="pic"> Yellow </div>
    </body>
</html>

在PC浏覽器中的效果圖如下:

由於我們的PC顯示器的分辨率為1366*768,所以在浏覽器中顯示縱向肯定會出現滾動條的,但之所以將這個div設置為1280*768是因為假如沒有上述的“屏幕獨立像素(css邏輯像素)”的概念,那麼這個div應當在一個1280*768像素的手機上滿屏顯示,我們在上面創建的Google Nexus 4正好是這個分辨率:,所以我們就在這個設備上看一下該頁面的效果:

如上圖所示,有留邊,很明顯並沒有占滿屏幕,這裡我們先明確一點就是物理像素和邏輯像素存在明顯差異,其次再看一下上面提到的dpr(縮放比)的問題,我們使用的虛擬設備Google Nexus 4的dpr也為2(看下圖),所以說它實際的分辨率為640*384,那麼問題來了,我的圖片是768*1280px,那麼在640*384的小屏幕上為什麼還顯示不全呢?理論上應該不但能全顯示,而且只能顯示圖片總面積的1/4(經緯度分別為1/2,所以總面積為1/4)呢?這裡明確兩點,首先我們說的這一點沒錯,看下面這幅圖:

首先有一個小問題就是chrome的手機調試工具中的Nexus 4 為384*567而並非384*640,這個不用在意,安卓模擬器中的Nexus 4是768*1280px,版本問題而已。觀察上面的chrome手機調試工具中的效果(page sacle=1.0表示正常頁面比例,即0縮放),C、D標注是橫縱向的兩個滾動條,也就是說我滾動到剛好能看到這個div的右下部分,同時再看A、B標注的標尺部分,可視范圍內,橫向是400到800,縱向是750到1350,也就是說橫縱差不多都顯示了1/2,也就是說圖中的可視面積正是div總面積的1/4,符合我們上面的計算,這是第一點,接下來再考慮安卓模擬器中為什麼沒有和上面一樣,而是沒有占滿屏幕呢?原因正是因為viewport的存在,接下來具體介紹一下viewport的概念。

介紹viewport的概念之前再看一個例子,我們在手機浏覽器中打開一個PC網站時我們發現可以看到網頁的全貌,而並非出現了滾動條:

如上圖,左邊是chrome的手機調試工具,右邊的是Nexus 4的安卓模擬器,之所以能看到1000多像素寬的PC網頁的全貌是因為viewport的存在,手機浏覽器將PC頁面渲染在一個比較大的viewport裡面(android4.x普遍為980px,ios也為980px),然後再進行縮放,就能看到整個頁面的全貌了。所以明確一下手機浏覽器的工作分為兩步:

  1. 將頁面渲染在一個比較大的viewport裡面並進行縮放(這個viewport也叫layout viewport),保證頁面排版以及css布局不會亂。
  2. 通過大小等於手機浏覽器可視區域的viewport(這個viewport也叫visual viewport)選取可視區域進行展示。

OK,接下來就具體介紹一下viewport的分類,viewport分為layout viewport和visual viewport,layout viewport可以理解為一個大的窗體,處在底層,包含整個PC浏覽器頁面,如下圖所示:

layout viewport的值可以通過以下js代碼來獲取:

document.documentElement.clientWidth

而visual viewport則代表手機浏覽器當前可視區域的窗體,處在上層,如下圖所示:

visual viewport的值可以通過以下js代碼來獲取:

window.innerWidth

接下來就分別看一下Nexus 4下的layout viewport和visual viewport的值:

如上圖所示,都是980px,至此就可以解釋最開始的那個問題——1280*768px的div為什麼不能在Nexus 4上全屏顯示。答案很簡單,默認的layout viewport的值是980px,而這個圖片的寬度是768px,所以無法鋪滿屏幕。然而在實際開發中我們並不會用默認的980px的viewport,原因很簡單,上面顯示pc頁面的效果雖然沒有滾動條可以看到頁面的全貌,但是可以發現縮放的太小了以至於有些看不清,這樣明顯是不合適的,所以在實際開發中我們都會自定義layout viewport的值從而得到合適的縮放效果,那麼該如何自定義layout viewport的值?很簡單,都過設置Meta標簽就可以完成。

下面就介紹一下如何通過Meta標簽來設置layout viewport的值,Meta viewport有以下6個屬性值:

Content屬性value width 設置layout viewport 的寬度,可以是固定值或“width-device” initial-scale 設置頁面的初始縮放值 minimum-scale 允許用戶的最小縮放值 maximum-scale 允許用戶的最大縮放值 user-scalable 是否允許用戶進行縮放,值為“no”或“yes”

再看一下Meta標簽的格式:

<meta name="viewport" content="width=device-width">

如上所述,設置width的值為device-width(即設備寬度)就表示將layout viewport的值設置為設備寬度,比如Nexus 4就是384px,加上這個Meta標簽後我們再看看上面Yellow的效果,看之前首先要理清,div的寬度依舊是1280*768px,而我們chrome手機調試工具選擇的設備是Nexus 4,也就是說此時的layout viewport的值是384px,在html源文件中添加<meta name="viewport" content="width=device-width"> 後再看看渲染的效果:

如上圖所示,layout viewport的值變成了384px,和Nexus 4的設備寬度保持一致,而visual viewport的值變成了776px,因為我們頁面的div給的是768px,所以為了容納下這個div我們的visual viewport的值就為776px,如標尺上的一致,注意第三行js:

window.innerWidth/document.documentElement.clientWidth

用visual viewport除以layout viewport得到的就是頁面的縮放比,這個縮放比是手機浏覽器通過頁面大小和設備寬度自動計算的,通俗的講,上面這一情況可以理解為:為了在384px的Nexus 4中完全顯示1280*768的這個div,那麼只有通過縮放,縮放比是2.02,也就是說當我們的div的像素更大時,這個縮放比也會越大,在實際開發中顯然這樣任由它縮放是不合理的,我們應該控制這個縮放系數,通常指定值為1:

<meta name="viewport" content="width=device-width,initial-scale=1">

在html源文件中添加如上修改,再次看一下渲染後的效果:

如上圖,我們指定了initial-scale為1,可以看到layout viewport和visual viewport的值都為384了,這也就回歸了我們最最原始的預期,在這個384*567的Nexus 4上只能顯示1280*768像素的這個div總面積的1/4,所以我們在移動web開中關於viewport最佳的設置方案就是:layout viewport=設備寬度=visual viewport,對應的Meta標簽代碼就是:

<meta name="viewport" 
      content="width=device-width,
               initial-scale=1,
               user-scalable=no">

最後一點,為了讓這個div能在Nexus 4上正常鋪滿顯示,我們應該合理設置它的寬度和高度,即將它設置為384*567px,如下圖所示就完美的剛好鋪滿了屏幕:

至此關於viewport以及移動開發中的單位就全部介紹完畢了。

總結

簡單記錄一下移動開發中用到的各種單位以及移動web開發中viewport的相關概念和應用,希望對遇到類似問題的同學有所幫助,The End。

Copyright © Linux教程網 All Rights Reserved