Skip to content

Commit c590fd9

Browse files
committed
Fix Fit.LAYOUT artboard oversized on Android
Set layoutScaleFactorAutomatic to device density at init time. RN's deferred layout means onMeasure (which normally sets this) may not fire before the render thread uses it, leaving the default of 1.0 and sizing the artboard in pixels instead of dp. Fixes #206
1 parent 950026c commit c590fd9

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

android/src/main/java/com/rive/RiveReactNativeView.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) {
8181

8282
init {
8383
riveAnimationView = ReactNativeRiveAnimationView(context)
84+
// RN's deferred layout means onMeasure may not fire before the render
85+
// thread runs resizeArtboard(). Without this, layoutScaleFactorAutomatic
86+
// defaults to 1.0 and Fit.LAYOUT sizes the artboard in pixels instead of dp.
87+
// See: https://github.com/rive-app/rive-android/issues/446
88+
riveAnimationView.layoutScaleFactorAutomatic = resources.displayMetrics.density
8489
addView(riveAnimationView)
8590
}
8691

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* Reproducer for https://github.com/rive-app/rive-nitro-react-native/issues/206
3+
*
4+
* [Android] Fit.Layout RiveView sometimes renders too big when another RiveView
5+
* is already running in the background. Looks like density scaling is applied
6+
* incorrectly — the layout graphic overflows well beyond the expected area.
7+
*
8+
* Repro steps:
9+
* 1. Run on Android
10+
* 2. Tap "Show Layout Views" button — this mounts two Fit.Layout RiveViews
11+
* while the background Fit.Cover RiveView is already rendering
12+
* 3. If the layout views look oversized/stretched, the bug is present
13+
* 4. Try restarting the app (not just reload) — repro rate is ~50%
14+
*
15+
* Expected: both layout views should fit neatly in their containers
16+
* Actual (Android, intermittent): layout views overflow their bounds
17+
*/
18+
19+
import { useState } from 'react';
20+
import { View, Text, StyleSheet, Pressable, ScrollView } from 'react-native';
21+
import { RiveView, Fit, useRiveFile } from '@rive-app/react-native';
22+
import type { Metadata } from '../shared/metadata';
23+
24+
function LayoutBox({ label }: { label: string }) {
25+
const { riveFile } = useRiveFile(
26+
require('../../assets/rive/layouts_demo.riv')
27+
);
28+
29+
return (
30+
<View style={styles.layoutBox}>
31+
<Text style={styles.boxLabel}>{label}</Text>
32+
<View style={styles.riveContainer}>
33+
{riveFile && (
34+
<RiveView
35+
file={riveFile}
36+
fit={Fit.Layout}
37+
autoPlay
38+
style={styles.riveLayout}
39+
/>
40+
)}
41+
</View>
42+
</View>
43+
);
44+
}
45+
46+
export default function Issue206Page() {
47+
const [showLayoutViews, setShowLayoutViews] = useState(false);
48+
49+
const { riveFile: bgFile } = useRiveFile(
50+
require('../../assets/rive/bouncing_ball.riv')
51+
);
52+
53+
return (
54+
<View style={styles.container}>
55+
{/* Background RiveView — always running (Fit.Cover) */}
56+
{bgFile && (
57+
<RiveView
58+
file={bgFile}
59+
fit={Fit.Cover}
60+
autoPlay
61+
style={StyleSheet.absoluteFill}
62+
/>
63+
)}
64+
65+
<ScrollView
66+
style={styles.overlay}
67+
contentContainerStyle={styles.overlayContent}
68+
>
69+
<Text style={styles.title}>Issue #206</Text>
70+
<Text style={styles.subtitle}>
71+
Fit.Layout too big on Android when background RiveView is active.
72+
Restart app (not reload) to re-test — repro rate ~50%.
73+
</Text>
74+
75+
<Pressable
76+
style={styles.button}
77+
onPress={() => setShowLayoutViews((v) => !v)}
78+
>
79+
<Text style={styles.buttonText}>
80+
{showLayoutViews ? 'Hide' : 'Show'} Layout Views
81+
</Text>
82+
</Pressable>
83+
84+
{showLayoutViews && (
85+
<View style={styles.fields}>
86+
<LayoutBox label="Layout View 1" />
87+
<LayoutBox label="Layout View 2" />
88+
</View>
89+
)}
90+
</ScrollView>
91+
</View>
92+
);
93+
}
94+
95+
Issue206Page.metadata = {
96+
name: 'Issue #206',
97+
description:
98+
'[Android] Fit.Layout too big when background RiveView is active',
99+
} satisfies Metadata;
100+
101+
const styles = StyleSheet.create({
102+
container: {
103+
flex: 1,
104+
backgroundColor: '#0c1027',
105+
},
106+
overlay: {
107+
flex: 1,
108+
},
109+
overlayContent: {
110+
padding: 24,
111+
paddingTop: 60,
112+
},
113+
title: {
114+
color: '#fff',
115+
fontSize: 28,
116+
fontWeight: '600',
117+
marginBottom: 8,
118+
},
119+
subtitle: {
120+
color: '#94A3B8',
121+
fontSize: 14,
122+
lineHeight: 20,
123+
marginBottom: 24,
124+
},
125+
button: {
126+
backgroundColor: '#443ABC',
127+
paddingVertical: 14,
128+
paddingHorizontal: 32,
129+
borderRadius: 12,
130+
alignSelf: 'center',
131+
marginBottom: 24,
132+
},
133+
buttonText: {
134+
color: '#fff',
135+
fontSize: 16,
136+
fontWeight: '600',
137+
},
138+
fields: {
139+
gap: 16,
140+
},
141+
layoutBox: {
142+
borderRadius: 16,
143+
borderWidth: 1,
144+
borderColor: 'rgba(255,255,255,0.1)',
145+
overflow: 'hidden',
146+
},
147+
boxLabel: {
148+
color: '#fff',
149+
fontSize: 12,
150+
padding: 8,
151+
position: 'absolute',
152+
zIndex: 1,
153+
top: 0,
154+
left: 0,
155+
},
156+
riveContainer: {
157+
height: 120,
158+
borderRadius: 16,
159+
overflow: 'hidden',
160+
},
161+
riveLayout: {
162+
width: '100%',
163+
height: '100%',
164+
},
165+
});

0 commit comments

Comments
 (0)