我正在开发 React Native 应用程序,以便在iOS和Android(以及Windows,如果可能)上部署为本机应用程序。
问题是我们希望布局与不同,具体取决于屏幕尺寸及其方向。
我已经创建了一些返回样式对象的函数,并在每个组件渲染的函数上调用,所以我可以在应用程序启动时应用不同的样式,但是如果应用初始化后,方向(或屏幕大小)会发生变化,不会重新计算或重新应用。
我已将侦听器添加到顶部渲染,以便更新其方向更改状态(并强制渲染应用程序的其余部分),但子组件不会重新渲染(因为,实际上,他们没有被改变)。
所以,我的问题是:如何根据屏幕尺寸和方向制作可能完全不同的样式,就像 CSS Media一样查询(即时呈现)?
我已经尝试了react-native-responsive模块而没有运气。
谢谢!
答案 0 :(得分:12)
可以在[此处] [1]找到解决方案。
从纵向到横向(反之亦然)的应用程序定向是一项任务,听起来很容易,但是当方向更改时必须更改视图时,在响应本机时可能会比较棘手。换句话说,通过考虑这两个步骤,可以为两个方向定义不同的视图。
从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()',因此我们不必担心再次渲染它,这一切都得到了照顾。
答案 1 :(得分:12)
这是@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'}} />
);
}
答案 2 :(得分:9)
您可以使用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({
...
});
答案 3 :(得分:3)
最后,我已经能够这样做了。不知道它可以带来的性能问题,但它们不应该是一个问题,因为它只需要调整大小或方向更改。
我创建了一个全局控制器,我有一个接收组件(容器,视图)的函数,并为其添加一个事件监听器:
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
(或{ {1}})孩子们没有更新,他们不会被重新渲染,所以如果我们有一个大孩子树听它,那么它可以是表演杀人。
希望它有所帮助!
答案 4 :(得分:2)
我制造了一个超级轻便的组件来解决这个问题。 https://www.npmjs.com/package/rn-orientation-view
该组件会在方向改变时重新呈现其内容。 例如,您可以传递landscapeStyles和PortraitStyles以不同方式显示这些方向。 适用于iOS和Android。 易于使用。看看。
答案 5 :(得分:2)
我有同样的问题。方向更改后,布局未更改。 然后我了解了一个简单的想法-布局应取决于应在渲染函数内部计算的屏幕宽度,即
getScreen = () => {
return Dimensions.get('screen');
}
render () {
return (
<View style={{ width: this.getScreen().width }>
// your code
</View>
);
}
在这种情况下,宽度将在渲染时计算。
答案 6 :(得分:0)
到目前为止,我在此库中获得了最大的成功:https://github.com/axilis/react-native-responsive-layout 它可以满足您的要求,甚至更多。简单的组件实现,几乎没有像上面的一些更复杂的答案那样的逻辑。我的项目使用的是电话,平板电脑,和通过RNW的网络-实现过程完美无缺。此外,在调整浏览器大小时,它确实具有响应能力,而不仅仅是在初始渲染时即可响应-完美处理手机方向变化。
示例代码(将任何组件作为块的子代放置):
<Grid>
<Section> {/* Light blue */}
<Block xsSize="1/1" smSize="1/2" />
<Block xsSize="1/1" smSize="1/2" />
<Block xsSize="1/1" smSize="1/2" />
</Section>
<Section> {/* Dark blue */}
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
</Section>
</Grid>
为此:
答案 7 :(得分:0)
我为我的 expo SDK36项目编写了一个HoC解决方案,它支持方向更改并基于props.orientation
值传递ScreenOrientation.Orientation
。
import React, { Component } from 'react';
import { ScreenOrientation } from 'expo';
export default function withOrientation(Component) {
class DetectOrientation extends React.Component {
constructor(props) {
super(props);
this.state = {
orientation: '',
};
this.listener = this.listener.bind(this);
}
UNSAFE_componentWillMount() {
this.subscription = ScreenOrientation.addOrientationChangeListener(this.listener);
}
componentWillUnmount() {
ScreenOrientation.removeOrientationChangeListener(this.subscription);
}
listener(changeEvent) {
const { orientationInfo } = changeEvent;
this.setState({
orientation: orientationInfo.orientation.split('_')[0],
});
}
async componentDidMount() {
await this.detectOrientation();
}
async detectOrientation() {
const { orientation } = await ScreenOrientation.getOrientationAsync();
this.setState({
orientation: orientation.split('_')[0],
});
}
render() {
return (
<Component
{...this.props}
{...this.state}
onLayout={this.detectOrientation}
/>
);
}
}
return (props) => <DetectOrientation {...props} />;
}
答案 8 :(得分:0)
为了实现更高性能的集成,我将以下内容用作每个反应导航屏幕的超类:
export default class BaseScreen extends Component {
constructor(props) {
super(props)
const { height, width } = Dimensions.get('screen')
// use this to avoid setState errors on unmount
this._isMounted = false
this.state = {
screen: {
orientation: width < height,
height: height,
width: width
}
}
}
componentDidMount() {
this._isMounted = true
Dimensions.addEventListener('change', () => this.updateScreen())
}
componentWillUnmount() {
this._isMounted = false
Dimensions.removeEventListener('change', () => this.updateScreen())
}
updateScreen = () => {
const { height, width } = Dimensions.get('screen')
if (this._isMounted) {
this.setState({
screen: {
orientation: width < height,
width: width, height: height
}
})
}
}
设置任何根组件以从该组件扩展,然后将屏幕状态从继承的根组件传递到叶/哑组件。
另外,为了避免增加性能开销,请更改样式对象,而不是在组合中添加更多组件:
const TextObject = ({ title }) => (
<View style={[styles.main, screen.orientation ? styles.column : styles.row]}>
<Text style={[styles.text, screen.width > 600 ? {fontSize: 14} : null ]}>{title}</Text>
</View>
)
const styles = StyleSheet.create({
column: {
flexDirection: 'column'
},
row: {
flexDirection: 'row'
},
main: {
justifyContent: 'flex-start'
},
text: {
fontSize: 10
}
}
我希望这对将来的任何人都有帮助,并且您会发现在开销方面,它是非常理想的。
答案 9 :(得分:0)
React Native还具有useWindowDimensions
挂钩,可返回设备的宽度和高度。
这样,您可以通过比较宽度和高度轻松检查设备是处于“纵向”还是“横向”。
查看更多here
答案 10 :(得分:0)
**我将这种逻辑用于横向和纵向逻辑。** **这样,如果我首先在横向上启动我的应用程序,那么我将获得设备的实际高度。并相应地管理标题的高度。**
const [deviceOrientation, setDeviceOrientation] = useState( Dimensions.get('window').width < Dimensions.get('window').height ? 'portrait' : 'landscape' ); const [deviceHeight, setDeviceHeight] = useState( Dimensions.get('window').width < Dimensions.get('window').height ? Dimensions.get('window').height : Dimensions.get('window').width ); useEffect(() => { const setDeviceHeightAsOrientation = () => { if (Dimensions.get('window').width < Dimensions.get('window').height) { setDeviceHeight(Dimensions.get('window').height); } else { setDeviceHeight(Dimensions.get('window').width); } }; Dimensions.addEventListener('change', setDeviceHeightAsOrientation); return () => { //cleanup work Dimensions.removeEventListener('change', setDeviceHeightAsOrientation); }; }); useEffect(() => { const deviceOrientation = () => { if (Dimensions.get('window').width < Dimensions.get('window').height) { setDeviceOrientation('portrait'); } else { setDeviceOrientation('landscape'); } }; Dimensions.addEventListener('change', deviceOrientation); return () => { //cleanup work Dimensions.removeEventListener('change', deviceOrientation); }; }); console.log(deviceHeight); if (deviceOrientation === 'landscape') { return ( <View style={[styles.header, { height: 60, paddingTop: 10 }]}> <TitleText>{props.title}</TitleText> </View> ); } else { return ( <View style={[ styles.header, { height: deviceHeight >= 812 ? 90 : 60, paddingTop: deviceHeight >= 812 ? 36 : 10 } ]}> <TitleText>{props.title}</TitleText> </View> ); }
答案 11 :(得分:0)
我正在使用styled-components
,这就是我在方向改变时重新呈现UI的方式。
import React, { useState } from 'react';
import { View } from 'react-native';
import { ThemeProvider } from 'styled-components';
import appTheme from 'constants/appTheme';
const App = () => {
// Re-Layout on orientation change
const [theme, setTheme] = useState(appTheme.getTheme());
const onLayout = () => {
setTheme(appTheme.getTheme());
}
return (
<ThemeProvider theme={theme}>
<View onLayout={onLayout}/>
{/* Components */}
</ThemeProvider>
);
}
export default App;
即使您不使用styled-components
,也可以创建状态并在onLayout
上进行更新以重新呈现UI。