1. 程式人生 > >React Native入門(十一)之螢幕適配

React Native入門(十一)之螢幕適配

準備

首先,我們在官方文件寬度和高度一節可以知道,RN中單位是dp,這個跟Android中的單位是一致的!
官網中:

A dp is equal to one physical pixel on a screen with a density of 160.To calculate dp:
dp = (width in pixels * 160) /  screen density

這裡的 screen density 螢幕密度,它約等於(dpi/160),在RN中我們可以通過PixelRatio得到:

  • PixelRatio.get() === 1
    • mdpi Android 裝置 (160 dpi)
  • PixelRatio.get() === 1.5
    • hdpi Android 裝置 (240 dpi)
  • PixelRatio.get() === 2
    • iPhone 4, 4S
    • iPhone 5, 5c, 5s
    • iPhone 6
    • xhdpi Android 裝置 (320 dpi)
  • PixelRatio.get() === 3
    • iPhone 6 plus
    • xxhdpi Android 裝置 (480 dpi)
  • PixelRatio.get() === 3.5
    • Nexus 6

說到這裡,不妨把涉及到的一些概念梳理一下:

  • dp(dip)(Density-independent pixels)
    一種基於螢幕密度的抽象單位,畫素無關密度。在每英寸160點的顯示器上,1dp = 1px。特別需要注意dp是相對長度單位,簡單的說1dp在不同的螢幕或者不同的ppi下展示出來的“物理長度”可能不一致。
  • px 畫素點。也是相對長度
  • in(inch)英寸。物理長度
  • pt 中文叫磅,等於1/72英寸。物理長度
  • sp(與刻度無關的畫素):與dp類似,但是可以根據使用者的字型大小首選項進行縮放,Android設定字型的時候使用。
  • density 密度。這個可以參考上邊說的,跟pixelRatio相近,約等於DPI/160。
  • pixelRatio 裝置畫素比。通過PixelRatio.get()獲得
  • PPI (pixels per inch) 影象解析度
    (每英寸所包含的畫素數,指對角線)
  • DPI(dots per inch)列印精度 (每英寸所能列印的點數,指寬高)
  • 解析度 : 橫縱2個方向的畫素點的數量,常見取值 480X800 ,750X1334
  • 螢幕尺寸: 螢幕對角線的長度(英寸)。電腦電視同理。

所以我們可以知道:
1dp=(1 * density) px,相反1px=(1/density)dp

那麼,我們如何在RN中設定寬度為1px的分割線呢?

width: 1 / PixelRatio.get()

這樣就可以了!

另外,一個裝置的寬高,我們可以這樣獲得:

width: Dimensions.get('window').width,
height: Dimensions.get('window').height,

那麼這個得到的寬高是什麼呢?
我們可以打印出來,比如iPhone6s,這個值打印出來是width:375,height:667,因為6s的density是2,所以我們知道這個得到的寬高是dp。

適配方案

所以,這裡RN中的適配方案:以6,6s為例:
UI設計原型:基於iphone6
解析度:1334 x 750 px;
螢幕大小:4.7英寸
DPI: 326dpi(約等於320dpi,density=2)

import {Dimensions} from 'react-native';

const deviceWidthDp = Dimensions.get('window').width;
const uiWidthPx = 750;

export default function px2dp(uiElementPx) {
  return uiElementPx * deviceWidthDp / uiWidthPx;
}

deviceWidthDp為當前執行裝置的寬度,uiWidthPx為UI設計圖的寬度,uiElementPx設計圖中標註的元素的px值。
那麼使用的話,比如一張圖片UI標註寬高為200x400:

<Image style={{width:px2dp(200),height:px2dp(400)}} source=xxx />

其他

我們可以寫一個工具類,這些常用的寫在一起,方便使用:
ScreenUtil.js

import {Dimensions, Platform, PixelRatio} from 'react-native'

export default {
  width: Dimensions.get('window').width,
  height: Dimensions.get('window').height,
  onePixel: 1 / PixelRatio.get(),
  STATUSBAR_HEIGHT: (Platform.OS === 'ios' ? 20 : 0),
  APPBAR_HEIGHT: (Platform.OS === 'ios' ? 44 : 56),
}

需要注意的就是:如果這個工具類和呼叫方不在一個包下邊的話,需要我們在工具類這個包下,寫一個index.js匯出才行,要不然找不到!

import ScreenUtil from './ScreenUtil'
...

export {ScreenUtil};

使用的時候就直接:ScreenUtil.width就行!

還有一個問題就是STATUSBAR_HEIGHTAPPBAR_HEIGHT這個順便說一下吧!
iOS螢幕座標是包含狀態列的,所以我們在iOS環境下執行一個元件它會在狀態列下繪製,即會和狀態列重疊,而Android則不會,他會在狀態列下邊開始繪製!

上邊程式碼是在StackNavigator/Header中,尤其在我們自定義Header標題欄的時候,需要注意要設定paddingTop:STATUSBAR_HEIGHT,這樣就避免了iOS下在狀態列下繪製的問題