diff --git a/android/rctmgl/src/main/java-v10/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.kt b/android/rctmgl/src/main/java-v10/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.kt index a3945c154..dc784c789 100644 --- a/android/rctmgl/src/main/java-v10/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.kt +++ b/android/rctmgl/src/main/java-v10/com/mapbox/rctmgl/components/mapview/RCTMGLMapView.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.BitmapFactory import android.graphics.PointF import android.graphics.RectF +import android.util.Log import android.view.Gravity import android.view.View import android.view.ViewGroup @@ -113,6 +114,9 @@ open class RCTMGLMapView(private val mContext: Context, var mManager: RCTMGLMapV var tintColor: Int? = null private set + val mapView: MapView + get() = this + val pointAnnotationManager: PointAnnotationManager? get() { if (mPointAnnotationManager == null) { @@ -902,17 +906,17 @@ open class RCTMGLMapView(private val mContext: Context, var mManager: RCTMGLMapV } }) - RCTMGLMarkerViewManager.markerViewContainerSizeFixer(this, viewAnnotationManager) + RCTMGLMarkerViewManager.markerViewContainerSizeFixer(this, this.viewAnnotationManager) } // region Ornaments private fun toGravity(kind: String, viewPosition: Int): Int { return when (viewPosition) { - 0 -> (Gravity.TOP or Gravity.LEFT) - 1 -> (Gravity.TOP or Gravity.RIGHT) - 2 -> (Gravity.BOTTOM or Gravity.LEFT) - 3 -> (Gravity.BOTTOM or Gravity.RIGHT) + 0 -> (Gravity.TOP or Gravity.START) + 1 -> (Gravity.TOP or Gravity.END) + 2 -> (Gravity.BOTTOM or Gravity.START) + 3 -> (Gravity.BOTTOM or Gravity.END) else -> { Logger.e( "MapView", @@ -991,6 +995,7 @@ open class RCTMGLMapView(private val mContext: Context, var mManager: RCTMGLMapV fadeWhenFacingNorth = mCompassFadeWhenNorth updateOrnament("compass", mCompassSettings, this.toGenericOrnamentSettings()) } + workaroundToRelayoutChildOfMapView() } var mScaleBarSettings = OrnamentSettings(enabled = false) @@ -1016,9 +1021,25 @@ open class RCTMGLMapView(private val mContext: Context, var mManager: RCTMGLMapV } private fun updateScaleBar() { - scalebar.updateSettings { + mapView.scalebar.updateSettings { updateOrnament("scaleBar", mScaleBarSettings, this.toGenericOrnamentSettings()) } + workaroundToRelayoutChildOfMapView() + } + + fun workaroundToRelayoutChildOfMapView() { + if (mapView.width == 0 && mapView.height == 0) { + return + } + + mapView.requestLayout(); + mapView.forceLayout(); + + mapView.measure( + MeasureSpec.makeMeasureSpec(mapView.measuredWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(mapView.measuredHeight, MeasureSpec.EXACTLY) + ); + mapView.layout(mapView.left, mapView.top, mapView.right, mapView.bottom) } // endregion @@ -1124,16 +1145,7 @@ open class RCTMGLMapView(private val mContext: Context, var mManager: RCTMGLMapV logo.updateSettings { updateOrnament("logo", mLogoSettings, this.toGenericOrnamentSettings()) } - - logo.updateSettings { - println(String.format("logo :: position - before 0x%08x", position)) - //position = Gravity.BOTTOM or Gravity.RIGHT - println(String.format("eq bottom|right %b", position == (Gravity.BOTTOM or Gravity.RIGHT))) - if (position == Gravity.BOTTOM or Gravity.RIGHT) { - position = Gravity.BOTTOM or Gravity.RIGHT - } - println(String.format("logo :: position - after 0x%08x", position)) - } + workaroundToRelayoutChildOfMapView() } /* fun setReactLogoEnabled(logoEnabled: Boolean?) { @@ -1208,7 +1220,11 @@ open class RCTMGLMapView(private val mContext: Context, var mManager: RCTMGLMapV } override fun handleLifecycleEvent(event: Lifecycle.Event) { - lifecycleRegistry.handleLifecycleEvent(event) + try { + lifecycleRegistry.handleLifecycleEvent(event) + } catch (e: RuntimeException) { + Log.e("RCTMGLMapView", "onAttachedToWindow error: $e") + } } override fun getLifecycle(): Lifecycle { @@ -1228,8 +1244,8 @@ open class RCTMGLMapView(private val mContext: Context, var mManager: RCTMGLMapV fun OrnamentSettings.setPosAndMargins(posAndMargins: ReadableMap?) { if (posAndMargins == null) { return } - val bottom_mask = 1; - val right_mask = 2; + val bottom_mask = 2; + val right_mask = 1; var margins = WritableNativeMap() var position = 0; @@ -1281,7 +1297,7 @@ fun ScaleBarSettings.toGenericOrnamentSettings() = object : GenericOrnamentSetti } fun CompassSettings.toGenericOrnamentSettings() = object : GenericOrnamentSettings { - private val settings = this@toGenericOrnamentSettings + private var settings = this@toGenericOrnamentSettings override fun setHMargins(left: Float?, right: Float?) { left?.let { settings.marginLeft = it } right?.let { settings.marginRight = it } @@ -1295,11 +1311,13 @@ fun CompassSettings.toGenericOrnamentSettings() = object : GenericOrnamentSettin set(value) { settings.enabled = value } override var position: Int get() = settings.position - set(value) { settings.position = value } + set(value) { + settings.position = value + } } fun LogoSettings.toGenericOrnamentSettings() = object : GenericOrnamentSettings { - private val settings = this@toGenericOrnamentSettings + private var settings = this@toGenericOrnamentSettings override fun setHMargins(left: Float?, right: Float?) { left?.let { settings.marginLeft = it } right?.let { settings.marginRight = it } diff --git a/example/android/app/src/main/java/com/rnmapboxglexample/MainActivity.java b/example/android/app/src/main/java/com/rnmapboxglexample/MainActivity.java index 39e8fb48b..005096e1a 100644 --- a/example/android/app/src/main/java/com/rnmapboxglexample/MainActivity.java +++ b/example/android/app/src/main/java/com/rnmapboxglexample/MainActivity.java @@ -3,9 +3,16 @@ import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; +import android.os.Bundle; public class MainActivity extends ReactActivity { + //react-native-screens override + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(null); + } + /** * Returns the name of the main component registered from JavaScript. This is used to schedule * rendering of the component. diff --git a/example/package.json b/example/package.json index 4a7406d70..1d6a0f3dd 100644 --- a/example/package.json +++ b/example/package.json @@ -17,6 +17,7 @@ "dependencies": { "@mapbox/geo-viewport": "^0.5.0", "@mapbox/mapbox-sdk": "^0.13.0", + "@react-native-async-storage/async-storage": "^1.17.11", "@react-navigation/compat": "^5.3.20", "@react-navigation/native": "^6.0.11", "@react-navigation/native-stack": "^6.7.0", diff --git a/example/src/scenes/GroupAndItem.tsx b/example/src/scenes/GroupAndItem.tsx index c3b3a9855..45443e131 100644 --- a/example/src/scenes/GroupAndItem.tsx +++ b/example/src/scenes/GroupAndItem.tsx @@ -1,6 +1,6 @@ import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { Icon } from '@rneui/base'; -import React, { useCallback } from 'react'; +import React, { useState, useCallback, useEffect } from 'react'; import { FlatList, StyleSheet, @@ -8,6 +8,7 @@ import { TouchableOpacity, View, } from 'react-native'; +import AsyncStorage from '@react-native-async-storage/async-storage'; import MapHeader from '../examples/common/MapHeader'; import Page from '../examples/common/Page'; @@ -82,6 +83,8 @@ import Markers from '../examples/V10/Markers'; import QueryTerrainElevation from '../examples/V10/QueryTerrainElevation'; import TerrainSkyAtmosphere from '../examples/V10/TerrainSkyAtmosphere'; +const MostRecentExampleKey = '@recent_example'; + const styles = StyleSheet.create({ exampleList: { flex: 1, @@ -90,7 +93,7 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 16, - paddingVertical: 32, + paddingVertical: 16, }, exampleListItemBorder: { borderBottomColor: '#ccc', @@ -114,10 +117,47 @@ interface ExampleNode { navigationType: NavigationType; path: string[]; + updateIfNeeded(updated: () => void): void; setParent(parent: string[]): void; find(path: string[]): ExampleNode | undefined; } +class MostRecentExampleItem implements ExampleNode { + label: string; + navigationType: NavigationType; + path: string[]; + + constructor() { + this.label = 'Most recent'; + this.navigationType = 'Item'; + this.path = []; + } + + setParent(parent: string[]) { + this.path = [...parent, this.label]; + } + + find(path: string[]) { + if (path.length === 1 && path[0] === this.label) { + return this; + } else { + return undefined; + } + } + + updateIfNeeded(updated: () => void): void { + (async () => { + const pathJSON = await AsyncStorage.getItem(MostRecentExampleKey); + if (pathJSON) { + const path = JSON.parse(pathJSON); + this.label = `Most recent: ${path.slice(-1)}`; + this.path = path; + updated(); + } + })(); + } +} + class ExampleItem implements ExampleNode { label: string; Component: ItemComponent; @@ -142,6 +182,13 @@ class ExampleItem implements ExampleNode { return undefined; } } + + async getPath(): Promise { + return this.path; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + updateIfNeeded(_updated: () => void): void {} } type RootStackParamList = { @@ -186,6 +233,13 @@ class ExampleGroup implements ExampleNode { return undefined; } } + + async getPath(): Promise { + return this.path; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + updateIfNeeded(_updated: () => void): void {} } const BugReportPage = @@ -198,6 +252,7 @@ const BugReportPage = ); const Examples = new ExampleGroup('React Native Mapbox', [ + new MostRecentExampleItem(), new ExampleItem('Bug Report Template', BugReportPage(BugReportExample)), new ExampleItem('Bug Report Template TS', BugReportPage(BugReportExampleTS)), new ExampleGroup('V10', [ @@ -294,8 +349,11 @@ function ExampleGroupComponent({ showBack?: boolean; title: string; }) { - function itemPress(item: ExampleNode) { - navigation.push(item.navigationType, { path: item.path }); + async function itemPress(item: ExampleNode) { + const { path } = item; + if (path.length > 0) { + navigation.push(item.navigationType, { path }); + } } function renderItem({ item }: { item: ExampleNode }) { @@ -319,6 +377,15 @@ function ExampleGroupComponent({ } : {}; + const [, updateState] = useState(); + const forceUpdate = useCallback(() => updateState({}), []); + + useEffect(() => { + items.forEach((item) => { + item.updateIfNeeded(forceUpdate); + }); + }, [items, forceUpdate]); + return ( @@ -356,6 +423,12 @@ export const Item = ({ route, navigation }: ItemProps) => { navigation.goBack(); }, [navigation]); + useEffect(() => { + AsyncStorage.setItem( + MostRecentExampleKey, + JSON.stringify(route.params.path), + ); + }, [route.params.path]); const { path } = route.params; const item = Examples.find(path); if (!(item instanceof ExampleItem)) {