You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中调整Google Maps API按钮的位置?

解决Google Map指南针与我的位置按钮位置调整问题

问题描述

需要调整Google Map左上角的指南针按钮和右上角的我的位置按钮到顶部以外的区域,同时让地图延伸至屏幕顶部,避免整体下移地图导致的空间浪费,当前默认按钮位于状态栏下方,操作不便。

解决方案

Google Maps Compose库本身不支持直接修改默认控件的位置,可行方案是禁用默认按钮,通过Compose布局自定义按钮并自由摆放,具体步骤如下:

1. 禁用默认控件

修改MapUiSettings,关闭默认的我的位置按钮和指南针按钮:

val uiSettings by remember {
    mutableStateOf(
        MapUiSettings(
            myLocationButtonEnabled = false, // 关闭默认我的位置按钮
            compassEnabled = false, // 关闭默认指南针按钮
            mapToolbarEnabled = true
        )
    )
}

2. 自定义按钮并布局

用Box包裹GoogleMap,在Box层添加自定义按钮,通过Modifier的align和padding控制位置(示例放在底部区域):

Box(modifier = Modifier.fillMaxSize()) {
    // 原GoogleMap代码保持不变
    GoogleMap(
        modifier = Modifier.fillMaxSize(),
        cameraPositionState = cameraPositionState,
        properties = properties,
        uiSettings = uiSettings
    ) {
        Marker(
            state = MarkerState(position = lastKnownLocation),
            title = "lastKnownLocation",
            snippet = "Marker in lastKnownLocation"
        )
    }

    // 自定义我的位置按钮(右下角)
    IconButton(
        onClick = {
            cameraPositionState.move(
                CameraUpdateFactory.newLatLngZoom(lastKnownLocation, zoom)
            )
        },
        modifier = Modifier
            .align(Alignment.BottomEnd)
            .padding(16.dp)
            .background(Color.White, CircleShape)
    ) {
        Icon(Icons.Filled.MyLocation, contentDescription = "我的位置", tint = Color.Black)
    }

    // 自定义指南针按钮(左下角)
    var isCompassVisible by remember { mutableStateOf(false) }
    // 监听相机朝向,偏离正北时显示指南针
    LaunchedEffect(cameraPositionState.position.bearing) {
        isCompassVisible = cameraPositionState.position.bearing != 0f
    }

    if (isCompassVisible) {
        IconButton(
            onClick = {
                // 重置相机朝向为正北
                cameraPositionState.move(
                    CameraUpdateFactory.newCameraPosition(
                        cameraPositionState.position.copy(bearing = 0f)
                    )
                )
            },
            modifier = Modifier
                .align(Alignment.BottomStart)
                .padding(16.dp)
                .background(Color.White, CircleShape)
        ) {
            Icon(Icons.Filled.CompassCalibration, contentDescription = "指南针", tint = Color.Black)
        }
    }
}

3. 完善位置更新逻辑(可选)

如果需要实时获取最新位置,添加位置回调确保lastKnownLocation始终更新:

// 在MapsScreen中添加以下代码
val locationRequest = LocationRequest.create().apply {
    interval = 10000
    fastestInterval = 5000
    priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}

val locationCallback = remember {
    object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            locationResult.lastLocation?.let { location ->
                lastKnownLocation = LatLng(location.latitude, location.longitude)
            }
        }
    }
}

LaunchedEffect(Unit) {
    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.ACCESS_COARSE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
    }
}

// 页面销毁时移除回调
DisposableEffect(Unit) {
    onDispose {
        fusedLocationClient.removeLocationUpdates(locationCallback)
    }
}

完整修改后的代码

@Composable
fun MapsScreen() {
    val context = LocalContext.current

    val isDarkTheme =
        when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
            Configuration.UI_MODE_NIGHT_YES -> true
            else -> false
        }

    val mapStyleTheme = if (isDarkTheme) {
        R.raw.mapstyle_night
    } else {
        R.raw.mapstyle_day
    }

    val fusedLocationClient: FusedLocationProviderClient =
        LocationServices.getFusedLocationProviderClient(context)
    var lastKnownLocation by remember {
        mutableStateOf(LatLng(0.0, 0.0))
    }
    var zoom by remember { mutableFloatStateOf(10f) }

    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.ACCESS_COARSE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        return
    }

    val cameraPositionState = rememberCameraPositionState {
        fusedLocationClient.lastLocation
            .addOnSuccessListener { location: Location? ->
                location?.let {
                    lastKnownLocation = LatLng(location.latitude, location.longitude)
                    zoom = 15f
                    position = CameraPosition.fromLatLngZoom(lastKnownLocation, zoom)
                }
            }
    }

    val uiSettings by remember {
        mutableStateOf(
            MapUiSettings(
                myLocationButtonEnabled = false,
                compassEnabled = false,
                mapToolbarEnabled = true,
            )
        )
    }

    val properties by remember {
        mutableStateOf(
            MapProperties(
                mapType = MapType.NORMAL,
                isMyLocationEnabled = true,
                mapStyleOptions = MapStyleOptions.loadRawResourceStyle(
                    context,
                    mapStyleTheme
                )
            )
        )
    }

    // 位置更新逻辑
    val locationRequest = LocationRequest.create().apply {
        interval = 10000
        fastestInterval = 5000
        priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    }

    val locationCallback = remember {
        object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                locationResult.lastLocation?.let { location ->
                    lastKnownLocation = LatLng(location.latitude, location.longitude)
                }
            }
        }
    }

    LaunchedEffect(Unit) {
        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
    }

    DisposableEffect(Unit) {
        onDispose {
            fusedLocationClient.removeLocationUpdates(locationCallback)
        }
    }

    Box(modifier = Modifier.fillMaxSize()) {
        GoogleMap(
            modifier = Modifier.fillMaxSize(),
            cameraPositionState = cameraPositionState,
            properties = properties,
            uiSettings = uiSettings
        ) {
            Marker(
                state = MarkerState(position = lastKnownLocation),
                title = "lastKnownLocation",
                snippet = "Marker in lastKnownLocation"
            )
        }

        IconButton(
            onClick = {
                cameraPositionState.move(
                    CameraUpdateFactory.newLatLngZoom(lastKnownLocation, zoom)
                )
            },
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp)
                .background(Color.White, CircleShape)
        ) {
            Icon(Icons.Filled.MyLocation, contentDescription = "我的位置", tint = Color.Black)
        }

        var isCompassVisible by remember { mutableStateOf(false) }
        LaunchedEffect(cameraPositionState.position.bearing) {
            isCompassVisible = cameraPositionState.position.bearing != 0f
        }

        if (isCompassVisible) {
            IconButton(
                onClick = {
                    cameraPositionState.move(
                        CameraUpdateFactory.newCameraPosition(
                            cameraPositionState.position.copy(bearing = 0f)
                        )
                    )
                },
                modifier = Modifier
                    .align(Alignment.BottomStart)
                    .padding(16.dp)
                    .background(Color.White, CircleShape)
            ) {
                Icon(Icons.Filled.CompassCalibration, contentDescription = "指南针", tint = Color.Black)
            }
        }
    }
}

说明

  • 该方案完全自定义按钮的位置、样式和交互逻辑,地图可保持fillMaxSize延伸至屏幕顶部,无空间浪费。
  • 按钮样式可通过Modifier.background、tint等属性调整,适配App主题。
  • 指南针仅在相机偏离正北时显示,与默认控件行为一致。

内容的提问来源于stack exchange,提问作者Ulises Cortés

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 06:46:00