React native 分辨率适配(px,dp)

作者: terrantian | 来源:发表于2016-12-13 15:48 被阅读8269次

    React Native中使用的尺寸单位是dp(一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px),而设计师使用的是px, 这两种尺寸如何换算呢?官方提供了PixelRatio:

    import {PixelRatio} from 'react-native';
    const dp2px = dp=>PixelRatio.getPixelSizeForLayoutSize(dp);
    const px2dp = px=>PixelRatio.roundToNearestPixel(px);
    

    设计师给你一个尺寸,比如100px*200px的View,按照下面的方式可实现设计还原:

    <View style={{width:px2dp(100),height:px2dp(200),backgroundColor:"red"}}/>
    

    这个时候,你或许会说,这也太麻烦了,每个有尺寸的地方我都得转么,能不能我直接用px写,当然可以,不过需要整体加个缩放系数:

    import {PixelRatio,Dimensions}} from 'react-native';
    const dp2px = dp=>PixelRatio.getPixelSizeForLayoutSize(dp);
    const px2dp = px=>PixelRatio.roundToNearestPixel(px);
    
    let pxRatio = PixelRatio.get();
    let {win_width,win_height} = Dimensions.get("window");
    
    let scale = 1/pxRatio;
    let width = dp2px(win_width);
    let height = dp2px(win_height);
    const com = props=>(
                    <View sytle={styles.container}>
                        <View style={{width:100,height:200,backgroundColor:"red"}}/>
                    </View>)
    
    const styles={
      container: {
            width:width,
            height:height,
            transform:[{translateX:-width*.5},
                        {translateY:-height*.5},
                        {scale:scale},
                        {translateX:width*.5},
                        {translateY:height*.5}]
        },
    }
    

    这样处理后,在根节点内,你再也不用考虑dp的问题了,直接使用px即可。

    不过此时还有另外一个问题,设计尺寸是死的,屏幕大小是活的,得考虑分辨率适配啊,那在不同的分辨率下如何正确的实现设计师的设计呢?

    我们将使用一种游戏经常会用到得方案,fixedWidth/fixedHeight.

    fixedWidth

    fixedWidth 模式是保持原始宽高比缩放应用程序内容,缩放后应用程序内容在水平和垂直方向都填满播放器窗口,但只保持应用程序内容的原始宽度不变,高度可能会改变,简言之宽度固定,高度自适应

    fixedHeight

    fixedHeight 模式是保持原始宽高比缩放应用程序内容,缩放后应用程序内容在水平和垂直方向都填满播放器窗口,但只保持应用程序内容的原始高度不变,宽度可能会改变,简言之高度固定,宽度自适应

    具体如何应用呢,别急,一步步来。
    先来看看如何得到屏幕的像素宽高:

    import {Dimensions,PixelRatio} from 'react-native';
    
    let {width,height} = Dimensions.get("window");
    let w =dp2px(width);
    let h = dp2px(height);
    

    假定我们的设计尺寸是

    let designSize = {width:750,height:1336};
    

    按照fixedWidth、fixedHeight的定义,我们计算下新的宽高:

    //fixedWidth
    let scale = designSize.width/w;
    let winSize = {width:designSize.width,height:h*scale};
    
    //fixedHeight
    let scale = designSize.height/h;
    let winSize = {width:designSize.width*scale,height:designSize.height};
    
    

    这个winsize就是最终实际用来布局的屏幕尺寸,此时我们又会多了一个分辨率适配的缩放系数,还记得我们前一个我们添加的为了使用px的缩放系数么,我们在这里做一个整合:

    import {PixelRatio,Dimensions}} from 'react-native';
    const dp2px = dp=>PixelRatio.getPixelSizeForLayoutSize(dp);
    const px2dp = px=>PixelRatio.roundToNearestPixel(px);
    
    let designSize = {width:750,height:1336};
    
    let pxRatio = PixelRatio.get();
    let {win_width,win_height} = Dimensions.get("window");
    
    let width = dp2px(win_width);
    let height = dp2px(win_height);
    
    let design_scale = designSize.width/width;
    height = height*design_scale
    
    let scale = 1/pxRatio/design_scale;
    
    const com = props=>(
                    <View sytle={styles.container}>
                        <View style={{width:100,height:200,backgroundColor:"red"}}/>
                    </View>)
    
    const styles={
      container: {
            width:width,
            height:height,
            transform:[{translateX:-width*.5},
                        {translateY:-height*.5},
                        {scale:scale},
                        {translateX:width*.5},
                        {translateY:height*.5}]
        },
    }
    

    在后续的开发中将不必再关注适配的问题,只需要按照设计师给的尺寸实现布局即可。

    最后再附上一个工具类 Resolution.js:

    import React, {Component, PropTypes} from 'react';
    import {
        Dimensions,
        PixelRatio,
        Platform,
        StatusBar,
        View
    } from 'react-native';
    
    let props = {};
    export default class Resolution {
        static get(useFixWidth = true){
            return useFixWidth?{...props.fw}:{...props.fh}
        }
    
        static setDesignSize(dwidth=750,dheight=1336,dim="window"){
            let designSize = {width:dwidth,height:dheight};
    
            let navHeight = Platform.OS === 'android' ? StatusBar.currentHeight : 64;
            let pxRatio = PixelRatio.get(dim);
            let {width,height} = Dimensions.get(dim);
            if(dim != "screen")height-=navHeight;
            let w = PixelRatio.getPixelSizeForLayoutSize(width);
            let h = PixelRatio.getPixelSizeForLayoutSize(height);
    
            let fw_design_scale = designSize.width/w;
            fw_width = designSize.width;
            fw_height = h*fw_design_scale;
            fw_scale = 1/pxRatio/fw_design_scale;
    
            let fh_design_scale = designSize.height/h;
            fh_width = w*fh_design_scale;
            fh_height = designSize.height;
            fh_scale = 1/pxRatio/fh_design_scale;
    
            props.fw = {width:fw_width,height:fw_height,scale:fw_scale,navHeight};
            props.fh = {width:fh_width,height:fh_height,scale:fh_scale,navHeight};
        }
    
        static FixWidthView = (p) => {
            let {width,height,scale,navHeight} = props.fw;
            return (
                <View {...p} style={{
                                                marginTop:navHeight,
                                                width:width,
                                                height:height,
                                                backgroundColor: 'transparent',
                                                transform:[{translateX:-width*.5},
                                                            {translateY:-height*.5},
                                                            {scale:scale},
                                                            {translateX:width*.5},
                                                            {translateY:height*.5}]
                                            }}>
                </View>
            );
        };
    
        static FixHeightView = (p) => {
            let {width,height,scale,navHeight} = props.fh;
            return (
                <View {...p} style={{
                                                marginTop:navHeight,
                                                width:width,
                                                height:height,
                                                backgroundColor: 'transparent',
                                                transform:[{translateX:-width*.5},
                                                            {translateY:-height*.5},
                                                            {scale:scale},
                                                            {translateX:width*.5},
                                                            {translateY:height*.5}]
                                            }}>
                    {p.children}
                </View>
            );
        };
    };
    //init
    Resolution.setDesignSize();
    
    
    
    
    

    How to use:

    import React, { Component } from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Text,
      Image,
      View
    } from 'react-native';
    
    import Resolution from "./Resolution"
    
    export default class tets extends Component {
      render() { 
        return (
          <Resolution.FixWidthView style={styles.container}>
            <Image source={require("./assets/bg_day.jpg")} style={{position:"absolute"}}/>
            <Text style={styles.welcome}>
              Welcome to React Native!
            </Text>
            <Text style={styles.instructions}>
              To get started, edit index.ios.js
            </Text>
            <Text style={styles.instructions}>
              Press Cmd+R to reload,{'\n'}
              Cmd+D or shake for dev menu
            </Text>
          </Resolution.FixWidthView>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 0,
        justifyContent: 'center',
        alignItems: 'center',
        // backgroundColor: '#ff0000',
      },
      welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
        backgroundColor:"transparent"
      },
      instructions: {
        backgroundColor:"transparent",
        textAlign: 'center',
        color: 0xffff,
        marginBottom: 5,
      },
    });
    
    AppRegistry.registerComponent('rn_resolution', () => tets);
    
    

    bg_day.jpg的尺寸是750*1500,上面的程序在所有的分辨率下图片都能正确显示。

    这里有个demo:Github,如果解决你的问题了,记得给我加星哦~~

    另外:不同分辨率下背景图片尺寸如何选择,移步另一篇博文:《分辨率适配的取值范围》

    相关文章

      网友评论

      • 4f9f8b21fc30:你的那个js在所有分辨率下都会适配么???如果两个手机分辨率的宽高比例不一样,背景图会不会短一块或者长一块
      • f3f54b12e867:@terrantian
        在整个rn应用中采用了自适应方案后,有些元素不需要自适应,如何避免这个问题呢?请教下
      • Aric_52c1:楼主,请问能修改全局的字体大小吗?
      • 608924568ff8:只有一套2倍的图片资源怎么破?
        terrantian:@我家果果 处理图片或设计尺寸*2
      • 骑着蜗牛去遛狗:你好,使用工具类之后,字体的缩放是没有问题的,我是把Resolution.FixWidthView中嵌入了scrollView标签,工具类中设置了marginTop,我注释掉了,但是在下拉的时候会有一部分内容遮挡住,还需要麻烦帮忙看下是什么原因,图上传不了,可以给我一个QQ号码我给传过去吗
        terrantian:if(dim != "screen")height-=navHeight;
        这个地方也去掉试试
      • 炜_f0e4:scale 缩小一倍后文字变的好丑 有什么解决办法吗
      • 5330adc10b21:在项目中具体试了这个方法,用上面写的适配上还是有问题的。另外不明白方法中为什么高度height = height*design_scale,而宽度却不用。整个方法做适配的时候,宽度也乘了设计比例,发现最后还要放大两倍才能实现,但是内容却在垂直方向上被截取掉了一部分。还请赐教
        terrantian:使用这个方案后,单位就统一成px了
        60e70e0e3026:@terrantian 哥,android怎么适配?pt是ios,android是pd吧。
        terrantian:缩放是等比的,使用fixWidth的话,width * scale = designWidth,相应的height也需要乘以这个scale。

        至于你说的另一个问题,贴下你的code,我瞅瞅。
      • d085fdc7c938:同问这个工具类怎么使用呢?新手不太明白啊。另外在rn中给样式设置transform:[{translateX:-width*.5},
        {translateY:-height*.5},
        {scale:scale},
        {translateX:width*.5},
        {translateY:height*.5}]会红屏的
        terrantian:红屏有截图么,我看看具体是什么错
        terrantian:这个transform的目的是修改容器的注册中心,以中心点进行缩放。
      • 羽纱:谢谢分享,不过博主应该还要考虑android设备的status的高度,在ios中布局是与statusbar重叠的,android上布局会顶在statusbar下面:
        let statusBarHeight = Platform.OS === 'android' ? StatusBar.currentHeight : 0;
        let h = PixelRatio.getPixelSizeForLayoutSize(height - statusBarHeight)

        我在工具类中加了这两句。:blush:
      • 3262d7c36ca3:方便说一下工具类怎么使用或者有demo么

      本文标题:React native 分辨率适配(px,dp)

      本文链接:https://www.haomeiwen.com/subject/emxbmttx.html