Reputation: 5085
Is it possible to get the size (width and height) of a certain view? For example, I have a view showing the progress:
<View ref='progressBar' style={{backgroundColor:'red',flex:this.state.progress}} />
I need to know the actual width of the view to align other views properly. Is this possible?
Upvotes: 253
Views: 365750
Reputation: 20838
The way to measure a view's dimensions is to call getBoundingClientRect()
(use unstable_getBoundingClientRect
with React Native 0.76) on a component's ref within a useLayoutEffect
Hook. This requires the New Architecture, which is enabled by default as of React Native 0.76.
import { useLayoutEffect, useRef } from 'react';
function Example {
const ref = useRef(null);
useLayoutEffect(() => {
const { width, height } = ref.current.getBoundingClientRect();
// or unstable_getBoundingClientRect()
console.log('The view measures %sx%s', width, height);
}, []);
return <View ref={ref} />;
}
The function passed to useLayoutEffect
is invoked during React's "commit" phase, after the view's layout has been calculated and before it is visible. The call to getBoundingClientRect()
is synchronous. Together, you can update your UI, such as with a call to setState
, based on the view's dimensions before the view is displayed to the user.
In apps that do not use the New Architecture, View components as of React Native 0.4.2 have an onLayout
prop. Pass in a function that takes an event object. The event's nativeEvent
contains the view's layout.
<View onLayout={(event) => {
const {x, y, width, height} = event.nativeEvent.layout;
}} />
The onLayout
handler will also be invoked whenever the view is resized.
The main caveat is that the onLayout
handler is first invoked one frame after your component has mounted, so you may want to hide your UI until you have computed your layout.
Upvotes: 522
Reputation: 823
Yes possible you can achieve this by onLayout prop
const [progress, setProgress] = useState(0.5); // Example progress value
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const progressBarRef = useRef(null);
const onLayout = event => {
const { width, height } = event.nativeEvent.layout;
setDimensions({ width, height });
};
then your view should be like this
<View style={styles.container}>
<View
ref={progressBarRef}
style={[styles.progressBar, { flex: progress }]}
onLayout={onLayout}
/>
<Text>Progress Bar Width: {dimensions.width}</Text>
<Text>Progress Bar Height: {dimensions.height}</Text>
</View>
and style like this
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
progressBar: {
backgroundColor: 'red',
height: 20,
alignSelf: 'stretch',
},
also you can check the full code in expo snack
Upvotes: 0
Reputation: 691
I create this simple component
import React, {Dispatch, SetStateAction} from 'react';
import {View, ViewProps} from 'react-native';
interface GetDimensionsProps {
children: React.ReactNode | React.ReactNode[];
onDimensions: Dispatch<SetStateAction<{height: number; width: number}>>;
viewProps?: ViewProps;
}
export const GetDimensions: React.FC<GetDimensionsProps> = ({
children,
onDimensions,
...viewProps
}: GetDimensionsProps) => {
return (
<View
onLayout={event =>
onDimensions({
width: Math.round(event.nativeEvent.layout.width),
height: Math.round(event.nativeEvent.layout.height),
})
}
{...viewProps}>
{children}
</View>
);
};
// ────────────────────────────────────────────────────────────────────────────────
// usage
// const [dimensions, setDimensions] = useState<{
// height: number;
// width: number;
// }>({width: 0, height: 0});
//
// <GetDimensions onDimensions={setDimensions}>
// {children}
// </GetDimensions>
Upvotes: 3
Reputation: 25423
You can easily get the size of the View
by onLayout
props.
import React from 'react'
import { View } from 'react-native'
export default function index() {
const onLayout=(event)=> {
const {x, y, height, width} = event.nativeEvent.layout;
}
return (
<View onLayout={onLayout}>
<OtherComponent />
</View>
)
}
The onLayout
handler will also be invoked whenever the view is resized.
The main caveat is that the onLayout
handler is first invoked one frame after your component has mounted, so you may want to hide your UI until you have computed your layout.
Upvotes: 35
Reputation: 1189
You can directly use the Dimensions
module and calc your views sizes.
Actually, Dimensions
give to you the main window sizes.
import { Dimensions } from 'Dimensions';
Dimensions.get('window').height;
Dimensions.get('window').width;
Hope to help you!
Update: Today using native StyleSheet
with Flex arranging on your views help to write clean code with elegant layout solutions in wide cases instead computing your view sizes...
Although building a custom grid components, which responds to main window resize events, could produce a good solution in simple widget components
Upvotes: -11
Reputation: 955
for me setting the Dimensions to use % is what worked for me
width:'100%'
Upvotes: -9
Reputation: 370
Here is the code to get the Dimensions of the complete view of the device.
var windowSize = Dimensions.get("window");
Use it like this:
width=windowSize.width,heigth=windowSize.width/0.565
Upvotes: -10
Reputation: 511
Basically if you want to set size and make it change then set it to state on layout like this:
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, View } from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'yellow',
},
View1: {
flex: 2,
margin: 10,
backgroundColor: 'red',
elevation: 1,
},
View2: {
position: 'absolute',
backgroundColor: 'orange',
zIndex: 3,
elevation: 3,
},
View3: {
flex: 3,
backgroundColor: 'green',
elevation: 2,
},
Text: {
fontSize: 25,
margin: 20,
color: 'white',
},
});
class Example extends Component {
constructor(props) {
super(props);
this.state = {
view2LayoutProps: {
left: 0,
top: 0,
width: 50,
height: 50,
}
};
}
onLayout(event) {
const {x, y, height, width} = event.nativeEvent.layout;
const newHeight = this.state.view2LayoutProps.height + 1;
const newLayout = {
height: newHeight ,
width: width,
left: x,
top: y,
};
this.setState({ view2LayoutProps: newLayout });
}
render() {
return (
<View style={styles.container}>
<View style={styles.View1}>
<Text>{this.state.view2LayoutProps.height}</Text>
</View>
<View onLayout={(event) => this.onLayout(event)}
style={[styles.View2, this.state.view2LayoutProps]} />
<View style={styles.View3} />
</View>
);
}
}
AppRegistry.registerComponent(Example);
You can create many more variation of how it should be modified, by using this in another component which has Another view as wrapper and create an onResponderRelease callback, which could pass the touch event location into the state, which could be then passed to child component as property, which could override onLayout updated state, by placing {[styles.View2, this.state.view2LayoutProps, this.props.touchEventTopLeft]}
and so on.
Upvotes: 19
Reputation: 2286
This is the only thing that worked for me:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Image
} from 'react-native';
export default class Comp extends Component {
find_dimesions(layout){
const {x, y, width, height} = layout;
console.warn(x);
console.warn(y);
console.warn(width);
console.warn(height);
}
render() {
return (
<View onLayout={(event) => { this.find_dimesions(event.nativeEvent.layout) }} style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('Comp', () => Comp);
Upvotes: 43
Reputation: 5211
Maybe you can use measure
:
measureProgressBar() {
this.refs.welcome.measure(this.logProgressBarLayout);
},
logProgressBarLayout(ox, oy, width, height, px, py) {
console.log("ox: " + ox);
console.log("oy: " + oy);
console.log("width: " + width);
console.log("height: " + height);
console.log("px: " + px);
console.log("py: " + py);
}
Upvotes: 14