React Native:在方向更改时应用不同的样式

Una*_*dra 14 css screen-orientation reactjs react-native

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

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

我已经制作了一些返回样式对象的函数,并在每个组件渲染的函数上调用,所以我可以在应用程序启动时应用不同的样式,但是如果应用程序初始化后方向(或屏幕的大小)发生了变化,不重新计算也不重新应用.

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

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

我已经尝试过反应原生响应模块而没有运气.

谢谢!

Mri*_*thi 25

解决方案可以在[这里] [1]找到.

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

从React Native导入维度

import { Dimensions } from 'react-native';
Run Code Online (Sandbox Code Playgroud)

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

/**
 * 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;
};
Run Code Online (Sandbox Code Playgroud)

要知道何时更改方向以相应地更改视图

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

组装所有件

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
      );
    }

  }
}
Run Code Online (Sandbox Code Playgroud)

由于为查找方向更改而定义的事件使用此命令' this.setState() ',此方法会自动再次调用' render() ',因此我们不必担心再次渲染它,所有这些都需要处理.


Eri*_*ner 19

这是@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;
}
Run Code Online (Sandbox Code Playgroud)

然后您可以使用它来使用它:

import {useOrientation} from './useOrientation';

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

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

  • 您可能需要添加“[]”作为“useEffect”的第二个参数,否则效果挂钩将无限期地运行。 (2认同)

小智 10

你可以使用onLayout道具:

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({
  ...
});
Run Code Online (Sandbox Code Playgroud)


Una*_*dra 5

Finally, I've been able to do so. Don't know the performance issues it can carry, but they should not be a problem since it's only called on resizing or orientation change.

I've made a global controller where I have a function which receives the component (the container, the view) and adds an event listener to it:

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
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

With this, I force to rerender the component when there's a change on orientation, or on window resizing.

Then, on every component constructor:

import ScreenMetrics from './globalFunctionContainer';

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

    this.state = {};

    ScreenMetrics.bindScreenDimensionsUpdate(this);
  }
}
Run Code Online (Sandbox Code Playgroud)

This way, it gets rerendered everytime there's a window resize or an orientation change.

You should note, however, that this must be applied to every component which we want to listen to orientation changes, since if the parent container is updated but the state (or props) of the children do not update, they won't be rerendered, so it can be a performance kill if we have a big children tree listening to it.

Hope it helps someone!

  • 卸载组件时删除侦听器很有用。如果你不这样做,你最终会耗尽内存。 (2认同)