React Native:应用于方向变化的不同样式

IT技术 css reactjs screen-orientation react-native
2021-05-05 08:34:05

我正在开发一个React Native应用程序,作为本机应用程序部署在 iOS 和 Android(以及 Windows,如果可能)上。

问题是我们希望布局根据屏幕尺寸和方向而不同

我已经创建了一些返回样式对象并在每个组件渲染函数上调用的函数,因此我能够在应用程序启动时应用不同的样式,但是如果应用程序初始化后方向(或屏幕的大小)发生变化,它们不会重新计算或重新应用。

我已将侦听器添加到渲染的顶部,因此它会在方向更改时更新其状态(并强制为应用程序的其余部分进行渲染),但子组件并未重新渲染(因为实际上,它们并未更改)。

所以,我的问题是:如何根据屏幕大小和方向制作可能完全不同样式,就像CSS 媒体查询(即时呈现)一样?

我已经尝试过react-native-responsivemodule而没有运气。

谢谢!

4个回答

可以在[此处][1]找到解决方案。

应用程序从纵向到横向(反之亦然)的方向是一项任务,这听起来很容易,但当方向改变时必须改变视图时,在本机react中可能会很棘手。换句话说,通过考虑这两个步骤可以实现为两个方向定义不同的视图。

从 React Native 导入维度

import { Dimensions } from 'react-native';

识别当前方向并相应地渲染视图

/**
 * Returns true if the screen is in portrait mode
 */
const isPortrait = () => {
    const dim = Dimensions.get('screen');
    return dim.height >= dim.width;
};

/**
 * Returns true of the screen is in landscape mode
 */
const isLandscape = () => {
    const dim = Dimensions.get('screen');
    return dim.width >= dim.height;
};

了解何时更改方向以相应地更改视图

// Event Listener for orientation changes
    Dimensions.addEventListener('change', () => {
        this.setState({
            orientation: Platform.isPortrait() ? 'portrait' : 'landscape'
        });
    });

组装所有部件

import React from 'react';
import {
  StyleSheet,
  Text,
  Dimensions,
  View
} from 'react-native';

export default class App extends React.Component {
  constructor() {
    super();

    /**
    * Returns true if the screen is in portrait mode
    */
    const isPortrait = () => {
      const dim = Dimensions.get('screen');
      return dim.height >= dim.width;
    };

    this.state = {
      orientation: isPortrait() ? 'portrait' : 'landscape'
    };

    // Event Listener for orientation changes
    Dimensions.addEventListener('change', () => {
      this.setState({
        orientation: isPortrait() ? 'portrait' : 'landscape'
      });
    });

  }

  render() {
    if (this.state.orientation === 'portrait') {
      return (
          //Render View to be displayed in portrait mode
       );
    }
    else {
      return (
        //Render View to be displayed in landscape mode
      );
    }

  }
}

由于为查看方向变化而定义的事件使用此命令“ this.setState() ”,因此此方法会自动再次调用“ render() ”,因此我们不必担心再次渲染它,这一切都已解决.

这是@Mridul Tripathi 作为可重复使用的钩子的答案:

// useOrientation.tsx
import {useEffect, useState} from 'react';
import {Dimensions} from 'react-native';

/**
 * Returns true if the screen is in portrait mode
 */
const isPortrait = () => {
  const dim = Dimensions.get('screen');
  return dim.height >= dim.width;
};

/**
 * A React Hook which updates when the orientation changes
 * @returns whether the user is in 'PORTRAIT' or 'LANDSCAPE'
 */
export function useOrientation(): 'PORTRAIT' | 'LANDSCAPE' {
  // State to hold the connection status
  const [orientation, setOrientation] = useState<'PORTRAIT' | 'LANDSCAPE'>(
    isPortrait() ? 'PORTRAIT' : 'LANDSCAPE',
  );

  useEffect(() => {
    const callback = () => setOrientation(isPortrait() ? 'PORTRAIT' : 'LANDSCAPE');

    Dimensions.addEventListener('change', callback);

    return () => {
      Dimensions.removeEventListener('change', callback);
    };
  }, []);

  return orientation;
}

然后你可以使用它:

import {useOrientation} from './useOrientation';

export const MyScreen = () => {
    const orientation = useOrientation();

    return (
        <View style={{color: orientation === 'PORTRAIT' ? 'red' : 'blue'}} />
    );
}

您可以使用onLayoutprops:

export default class Test extends Component {

  constructor(props) {
    super(props);
    this.state = {
      screen: Dimensions.get('window'),
    };
  }

  getOrientation(){
    if (this.state.screen.width > this.state.screen.height) {
      return 'LANDSCAPE';
    }else {
      return 'PORTRAIT';
    }
  }

  getStyle(){
    if (this.getOrientation() === 'LANDSCAPE') {
      return landscapeStyles;
    } else {
      return portraitStyles;
    }
  }
  onLayout(){
    this.setState({screen: Dimensions.get('window')});
  }

  render() {
    return (
      <View style={this.getStyle().container} onLayout = {this.onLayout.bind(this)}>

      </View>
      );
    }
  }
}

const portraitStyles = StyleSheet.create({
 ...
});

const landscapeStyles = StyleSheet.create({
  ...
});

最后,我已经能够这样做了。不知道它可能带来的性能问题,但它们应该不是问题,因为它只需要调整大小或方向更改。

我制作了一个全局控制器,其中有一个接收组件(容器、视图)并为其添加事件侦听器的函数:

const getScreenInfo = () => {
    const dim = Dimensions.get('window');
    return dim;
}    

const bindScreenDimensionsUpdate = (component) => {
    Dimensions.addEventListener('change', () => {
        try{
            component.setState({
                orientation: isPortrait() ? 'portrait' : 'landscape',
                screenWidth: getScreenInfo().width,
                screenHeight: getScreenInfo().height
            });
        }catch(e){
            // Fail silently
        }
    });
}

有了这个,当方向发生变化或窗口大小调整时,我会强制重新渲染组件。

然后,在每个组件构造函数上:

import ScreenMetrics from './globalFunctionContainer';

export default class UserList extends Component {
  constructor(props){
    super(props);

    this.state = {};

    ScreenMetrics.bindScreenDimensionsUpdate(this);
  }
}

这样,每次调整窗口大小或方向更改时,它都会重新渲染。

但是,您应该注意,这必须应用于我们想要侦听方向更改的每个组件,因为如果父容器已更新但子容器的state(或props)未更新,则它们不会被重新渲染,因此如果我们有一棵大的儿童树来听它,那可能是一场表演

希望它可以帮助某人!