ReactNative适配探讨

1.适配前置知识

px(CSS pixels): 逻辑像素,浏览器使用的抽象单位,多用于css样式单位,通常也是设计师使用的单位。
dp(device pixels): 设备像素(物理像素),顾名思义,显示屏是由一个个物理像素点组成的,通过控制每个像素点的颜色,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了,单位为pt(以下为了清晰暂用dp代替)。pt在css单位中属于真正的绝对单位,1pt = 1/72(inch),inch及英寸,而1英寸等于2.54厘米。
dpr(devicePixelRatio): 设备像素缩放比。计算公式:dpr = dp/px;1px=dpr*dpr*dp(px是个正方形,长*宽);

Reactnative中使用dp为单位

someStyle:{
  width:375,//这里的375代表375dp;
}

例:
iphone6宣称的分辨率是750*1334,这里指的是物理像素,但是在模拟器上显示出来的却是宽375像素(CSS逻辑像素),iphone6的375px*667px就是因为它的dpr等于2。此时一个css像素就会占据四个物理像素的位置,在x轴方向占两个,在y轴方向占两个。

2.适配问题

通常我们的设计师会以iphone6为设计稿,也就是375px*667px进行设计,此时如果我们使用iphone6(375dp*667dp)进行开发显示:
严谨的说在单一方向上,如水平方向 1px=1dp,375px=375dp(也是宽度满屏)

someStyle:{
  width:375,//设计稿里宽度为375,我们在这里设置width:375,代表375dp,此时dp与px等价,都是宽度上满屏
}

但如果我们在设备像素不等于(375dp*667dp)的设备上,例如iphone6s Plus(414dp*736dp),
我们设置

someStyle:{
  width:375,//设计稿里宽度为375,我们在这里设置width:375,代表375dp,但是我们的屏幕实际为414dp,就不能铺满全屏。
}

至此,问题浮现,我需要一个适配转换方法,使设计稿上的375px就是满屏,显示到任意设备像素的屏幕上还是满屏。
方法原理:
虽然单位不同,但是元素所占屏幕宽度的比例是相同的
利用元素所占屏幕比例不变的特性,来将 px 转为 dp(这样实现屏幕适配的话,在不同尺寸的屏幕下,元素会等比放大或缩小)
公式如下:
设计稿元素宽度(px) / 设计稿总宽度(px) = 元素的宽度(dp) / 屏幕的总宽度(dp)
我们要求的就是 元素的宽度(dp)
可以得出:
元素的宽度(dp,这是我们最后得到写到程序里的数值) = 设计稿元素宽度(px)* 屏幕的总宽度(dp) / 设计稿总宽度(px)
放到ReactNative里实现

const { width: screenW, height: screenH } = Dimensions.get('window'); //获取当前的设备的设备像素
// px转换成dp
// 以iphone6为基准,如果以其他尺寸为基准的话,请修改下面的defaultWidth和defaultHeight为对应尺寸即可. 以下为1倍图时
const defaultWidth = 375;
const defaultHeight = 667;
// 缩放比例
const _scaleWidth = screenW / defaultWidth;
const _scaleHeight = screenH / defaultHeight;
/**
 * 屏幕适配,缩放size , 默认根据宽度适配,纵向也可以使用此方法
 * 横向的尺寸直接使用此方法
 * 如:width ,paddingHorizontal ,paddingLeft ,paddingRight ,marginHorizontal ,marginLeft ,marginRight
 * @param size 设计图的尺寸
 * @returns {number}
 */
// sh  =》 scaleHorizontal
function sh(size) {
  return size * _scaleWidth;
}

/**
 * 屏幕适配 , 纵向的尺寸使用此方法应该会更趋近于设计稿
 * 如:height ,paddingVertical ,paddingTop ,paddingBottom ,marginVertical ,marginTop ,marginBottom
 * @param size 设计图的尺寸
 * @returns {number}
 */
// sv  =》 scaleVertical
function sv(size) {
  return size * _scaleHeight;
}

在确定了转换方法以后,我们只需在设置长度时调用转换方法即可

someStyle: {
    width: sh(44),
    height: sv(27),
    marginTop: sv(22),
    marginBottom: sv(48),
  }

3.异形屏的适配

此处的异形屏多指刘海屏,水滴屏,异形屏的statusbar(也就是显示信号,电量的顶部行)的高度会比正常的屏幕要高一些。
ReactNative官方给了一个解决方法:
SafeAreaView会自动根据系统的各种导航栏、工具栏等预留出空间来渲染内部内容。更重要的是,它还会考虑到设备屏幕的局限,比如屏幕四周的圆角或是顶部中间不可显示的“刘海”区域。
至此你的刘海屏的问题会得到解决,但是SafeAreaView只在ios生效,android还得使用StatusBar组件(https://reactnative.cn/docs/statusbar)获取statusbar的高度,再进行适配,这样就可以完成双端的适配。

StatusBar.currentHeight

具体链接:https://reactnative.cn/docs/safeareaview

image.png

But!But!But!But!But!But!But!But! 还有人说,我要从状态栏就开始渲染,也就是高逼格沉浸式设计,参考京东App
沉浸式设计:可以看到上边使用了SafeAreaView的屏幕很好的避开了刘海,但是如果设计稿的色彩和statubar的颜色出入很大,让用户很出戏,所以就出现了沉浸这个词,我不管你什么屏,我就从最上边开始渲染,开发者你帮我避开这些刘海和水滴。
来吧,安排一下!!!

  • 对于ios系统,它是默认沉浸式。
    iphoneX前导航栏的高度是64,其中statusBar的高度为20,而iPhoneX的statusBar高度变为了44,
// iPhoneX
const X_WIDTH = 375;
const X_HEIGHT = 812;
/**
 * 判断是否为iphoneX  大于iphonex尺寸的 iphone11也按X的尺寸处理
 * @returns {boolean}
 */
function isIphoneX() {
  return (
    Platform.OS === 'ios' &&
      ((screenH >= X_HEIGHT && screenW >= X_WIDTH) ||
          (screenH >= X_WIDTH && screenW >= X_HEIGHT))
  );
}

//在其他需要适配的界面进行判断赋值,我这里是tabnavigation
const statusHeight = isIphoneX ? sv(64) : sv(40);
tabBarOptions: {
    tabStyle: {
      minWidth: sh(115),
      height: statusHeight,
      display: 'flex',
      flexDirection: 'column',
      justifyContent: 'flex-end',
    },
    activeTintColor: '#323233',
    inactiveTintColor: '#999999',
    style: {
      borderWidth: 0,
      backgroundColor: '#FFCC66',
      height: statusHeight,
      shadowOpacity: 0,
      elevation: 0, // 去掉阴影
    },
  },

为方便观察,我将顶部tab变成了淡黄色长裙的背景,效果图如下,左边是iphone6sp,右边是iphone11


Lark20201019-172602.jpeg
  • 对于android系统。
    我们直接使用StatusBar,在android系统下使其透明即可,【backgroundColor、translucent】属性只在android上生效。
<View style={{ flex: 1 }}>
        <StatusBar
          animated // 指定状态栏的变化是否应以动画形式呈现。目前支持这几种样式:backgroundColor, barStyle和hidden
          backgroundColor="transparent" // 状态栏的背景色
          translucent // 指定状态栏是否透明。设置为true时,应用会在状态栏之下绘制(即所谓“沉浸式”——被状态栏遮住一部分)。常和带有半透明背景色的状态栏搭配使用。
        />
        <RootRoute />
      </View>

另外安卓设置translucent了后,statusbar的高度就没了,你需要手动去加上状态栏的高度例如

Feedback: {
    screen: Feedback,
    navigationOptions: ({ navigation }) => ({
      headerTitle: I18n.t('feedback'),
      tabBarVisible: false,
      headerStyle: {
        marginTop: statusBarHeight,  // 这里 statusBarHeight = StatusBar.currentHeight
        color: '#000',
      },
      headerTitleStyle: {
        color: '#121D27',
        fontSize: st(16),
      },
  }) },

由于安卓设备短缺,只用一台小米平板进行展示,平板上的适配还需再研究,在此只展示沉浸效果,如下图:


Lark20201019-173611.jpeg

4.何时何地使用适配

鄙人根据开发实践总结了几点:
1.需要宽高全屏显示容器的需要适配,如下图情况,上下两部分构成铺满全屏,此时需要宽高适配。不适配的话,在较长屏幕上会有大量下方留白

Lark20201019-173948.jpeg

2.列表项宽度容器适配,高度不适配,对于一些展示的列表项,我们通常会适配宽度,高度不进行适配拉伸,下方留白由机型高度决定。如果高度进行拉伸适配的话,会和设计稿出入较大,显得很不协调,

Lark20201019-173953.jpeg

3.不确定高度容器,宽度适配,高度不设置,有子元素决定高度,如下图的弹出层,我们无法确定白色容器在各种机型(有的机型会比设计稿高度还低)多少高度能显示下这些元素,所以我们不设置高度,或者只设置一个minHeight即可

Lark20201019-133109.png

**篇幅较长,谢谢阅读,个人见解,欢迎指正讨论.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容