解决大部分react-native适配问题

1,065 阅读4分钟

前言

最近在做RN的一个项目,发现一个问题,就是我在本手机上跑的时候样式好好的,换到了模拟器上发现样式发生了变化,所以就想着是RN的适配问题。

好的,接下来开始记录吧。

基本名词

适配: 假设有一个元素在375X667的设计图上标注为375X44,即宽度占满整个屏幕,高度44px。如果我们做好了RN的屏幕适配,那么:
在iPhone 6/7/8(375X667)机型与iPhone X(375X812)机型上,此元素渲染结果会占满屏幕宽度
在iPhone 6/7/8 Plus(414X736)机型上,此元素渲染结果也应占满屏幕宽度

  1. 适配:不同屏幕下,元素显示效果一致
  2. 屏幕尺寸:指的是屏幕对角线的长度
  3. px(单位): px实际是pixel(像素)的缩写。
  4. 分辨率 :是指宽度上和高度上最多能显示的物理像素点个数
  5. 设备物理像素:指设备能控制显示的最小物理单位,意指显示器上一个个的点。从屏幕在工厂生产出的那天起,它上面设备像素点就固定不变了,和屏幕尺寸大小有关
  6. 设备独立像素(设备逻辑像素):计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),这个点是没有固定大小的,越小越清晰,然后由相关系统转换为物理像素
  7. CSS 像素 : css px 和物理像素的对应关系, 与 viewport 的缩放有关 scale = 1/dpr 时 1px 对应一个 物理像素
  8. DPI:印机可以在一英寸内打多少个点。每inch 多少个点
  9. PPI:屏幕上每英寸可以显示的像素点的数量,即屏幕像素密度。
  10. DPR:设备像素比 = 设备物理像素 / 设备独立像素(CSS像素,一般设计师给的设计图)。

为什么react native没有单位? React Native 是跨平台的,iOS的逻辑像素单位是 pt,Android的逻辑像素单位是 dp,没办法兼顾,就直接去掉了,运行在哪个平台,就用哪个平台的单位。 RN 提供的就是 逻辑像素单位,是和像素密度无关的,RN本身会根据像素密度做适配,渲染对应的物理像素。

比例

为什么要设置比例:保证在页面上元素大小都是按设计图进行等比例缩放。

比例:设备宽度逻辑像素/设计图宽度

在设备上元素的大小:比例 * 元素大小(ps:这个元素大小为设计稿的px元素)

其实所有的适配都是围绕一个比例在做,如web端缩放、rem适配、postcss plugin 等,大道万千,殊途同归!

局部盒子缩放

RN上的元素大小、位置设计尺寸的地方,要按照上面的比例进行计算。

布局适配

布局,保证页面元素大小按照设计稿尺寸进行等比例缩放。

//获取设备dp
const designTotalWidth = 1920; // 设计稿尺寸 1920px*1080px
const {deviceWidth, deviceHeight} = Dimensions.get('window');
//设计稿上一个 640px*540px 的区域。CSS
{
    width:   (640/designTotalWidth) * deviceWidth
    height:  (540/designTotalWidth) * deviceWidth
}
//得到px转换后的在各个手机上的适配
export const transformAdaption = (element: number) => {
    const scale = Dimensions.get('window').width / UiWidth
    return scale * element
}

文字适配

包装的文字适应组件,别的组件使用这个组件就可以了

import React, { memo } from 'react'
import type { FC, ReactNode } from 'react'
import { Text, PixelRatio, Dimensions } from 'react-native'
import { transformAdaption } from '../../utils/adaptation'

interface IProps {
    children?: ReactNode
    style: any
    fontSize?: number
}

const AutoText: FC<IProps> = ({ children, style, fontSize, ...props }) => {
//PixelRatio.get() 得到的是设备的像素密度
    const size = PixelRatio.get() * transformAdaption(fontSize ? fontSize : 15)
    return (
        <Text style={[style, { fontSize: size }]} {...props}>
            discover
        </Text>
    )
}

export default memo(AutoText)

图像

使用resizeMode属性:Image组件中,使用resizeMode属性来指定图片的缩放模式。适配的时候可以考虑使用 covercontain 或者 stretch 等模式。

<Image source={require('./path/to/image.png')} style={{ width: 100, height: 100, resizeMode: 'cover' }} />

边框和线条

边框和线条一般要求所有屏幕保持相同的px 显示,比如需要看到一条最细的边框线。

//边框和线条
export const myBorderWidth = (width = 1) => {
    return width / PixelRatio.get()
}
//最小的border
width:1/PixelRatio.get()

一些疑惑的点

DimensionsApi的screen和window

const {windowWidth,windowHeight} = Dimensions.get('window');
const {screenWidth,screenHeight} = Dimensions.get('screen');
  • Dimensions.get('window')——获取视口参数width、height、scale、fontScale
  • Dimensions.get('screen')——获取屏幕参数width、height、scale、fontScale
    其中,在设备屏幕同状态的默认情况下screen的width、height永远是≥window的width、height,因为,window获取的参数会排除掉状态栏高度(translucent为false时)以及底部虚拟菜单栏高度。  当此安卓机设置了状态栏translucenttrue并且没有开启虚拟菜单栏时,Dimensions.get('window')就会与Dimensions.get('screen')获取的width、height一致,否则就不同。这就是本段开始时有时相同,有时又有差异的问题的答案。

最后

引用该博客的:

segmentfault.com/a/119000002…