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)
- 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_HEIGHT
和APPBAR_HEIGHT
這個順便說一下吧!
iOS螢幕座標是包含狀態列的,所以我們在iOS環境下執行一個元件它會在狀態列下繪製,即會和狀態列重疊,而Android則不會,他會在狀態列下邊開始繪製!
上邊程式碼是在StackNavigator/Header中,尤其在我們自定義Header標題欄的時候,需要注意要設定paddingTop:STATUSBAR_HEIGHT
,這樣就避免了iOS下在狀態列下繪製的問題!