Description
Fit.LAYOUT artboard size is incorrect under two conditions:
-
layoutScaleFactorAutomatic defaults to 1.0 and is only set to device density in onMeasure(). Since the view hasn't been measured yet when setRiveFile() is called, resizeArtboard() computes pixelWidth / 1.0 instead of pixelWidth / density — the artboard ends up density× too wide. iOS avoids this by initializing the scale factor at view init time. Originally reported in rive-nitro-react-native#206, simple workaround possible by setting layoutScaleFactor to density before setRiveFile.
-
When a state machine animates the artboard width: keyframes overwrite m_Width every frame via CoreRegistry::setDouble(artboard, widthPropertyKey, ...) during advance(). Since resizeArtboard() only runs once (one-shot requireArtboardResize flag), the artboard stays at the keyframe value.
For reference, the iOS runtime approaches this differently: it initializes scale factor at view init time and re-applies artboard dimensions every frame in drawRive rather than using a one-shot flag. It also re-applies on size change in drawableSizeDidChange. Not saying this is necessarily the right approach for Android, but it avoids both issues above.
Versions, Device, and Other Information
- Rive Android Runtime Version: 11.3.2
- Rive API: Legacy (View-based)
- Device: Android API 35 emulator (1080x2220, 440dpi), also reported on OnePlus CPH2719, Samsung Galaxy Fold 3
- App Minimum SDK Level: 21
- App Target SDK Level: 36
- Frequency: Issue 1 — 100% when setRiveFile is called before onMeasure. Issue 2 — 9/10 with layout_test.riv.
Reproduction Steps
Issue 1 (scale factor default): Any .riv with Fit.LAYOUT. The view hasn't been measured when setRiveFile is called, so layoutScaleFactorAutomatic is still 1.0. Confirmed via logcat:
configure: reload=true fit=LAYOUT viewSize=0x0px
configure post-setRiveFile: artboardW=1920.0 artboardH=1080.0
onLayout: artboardW=1920.0 artboardH=1080.0 ← still intrinsic size after layout
Reproduced in bare Android by suppressing onMeasure:
✗ Artboard 1080 dp ≠ View 393 dp (2.8x too wide!)
The 2.75x factor matches exactly the device density — 1080px / 1.0 instead of 1080px / 2.75.
Issue 2 (keyframe overwrite): layout_test.riv with Fit.LAYOUT — the state machine animates the artboard width property, overwriting the value set by resizeArtboard() every frame. Reproduced 9/10 launches.
Source .riv/.rev File
- Issue 1: Any
.riv with Fit.LAYOUT (tested with GradientBorder.riv from user's repro)
- Issue 2:
layout_test.riv from rive-android example app
Expected Behavior
Artboard dimensions should match viewWidth / density for Fit.LAYOUT.
Screenshots
Additional context
For reference, iOS handles this in:
Description
Fit.LAYOUTartboard size is incorrect under two conditions:layoutScaleFactorAutomaticdefaults to1.0and is only set to device density inonMeasure(). Since the view hasn't been measured yet whensetRiveFile()is called,resizeArtboard()computespixelWidth / 1.0instead ofpixelWidth / density— the artboard ends up density× too wide. iOS avoids this by initializing the scale factor at view init time. Originally reported in rive-nitro-react-native#206, simple workaround possible by settinglayoutScaleFactorto density beforesetRiveFile.When a state machine animates the artboard width: keyframes overwrite
m_Widthevery frame viaCoreRegistry::setDouble(artboard, widthPropertyKey, ...)duringadvance(). SinceresizeArtboard()only runs once (one-shotrequireArtboardResizeflag), the artboard stays at the keyframe value.For reference, the iOS runtime approaches this differently: it initializes scale factor at view init time and re-applies artboard dimensions every frame in
drawRiverather than using a one-shot flag. It also re-applies on size change indrawableSizeDidChange. Not saying this is necessarily the right approach for Android, but it avoids both issues above.Versions, Device, and Other Information
Reproduction Steps
Issue 1 (scale factor default): Any
.rivwithFit.LAYOUT. The view hasn't been measured whensetRiveFileis called, solayoutScaleFactorAutomaticis still1.0. Confirmed via logcat:Reproduced in bare Android by suppressing
onMeasure:The 2.75x factor matches exactly the device density —
1080px / 1.0instead of1080px / 2.75.Issue 2 (keyframe overwrite):
layout_test.rivwithFit.LAYOUT— the state machine animates the artboard width property, overwriting the value set byresizeArtboard()every frame. Reproduced 9/10 launches.Source
.riv/.revFile.rivwithFit.LAYOUT(tested withGradientBorder.rivfrom user's repro)layout_test.rivfrom rive-android example appExpected Behavior
Artboard dimensions should match
viewWidth / densityforFit.LAYOUT.Screenshots
Additional context
For reference, iOS handles this in:
RiveView.swift L38-44— scale factor initialized at view initdrawRiveL446-452 — artboard dimensions re-applied every framedrawableSizeDidChangeL490-501 — re-applied on size change