Skip to content

Commit 8e6e0e6

Browse files
authored
feat: Add a ScaleBar composable widget (#157)
* feat: Add a ScaleBar composable component * chore: Fix typo in docs * docs: Add documentation in README * docs: Tweak docs * docs: Tweak docs * chore: Make DarkGray public for ScaleBar configuration For example, if users want to specify it or different colors based on isSystemInDarkTheme() * docs: Tweak docs * chore: Allow setting width and height * refactor: Rename duration variable and change to Int This more closely matches other compose duration parameters * refactor: Convert unit conversion functions to extension functions * refactor: ScaleBar into maps-compose-widgets package * chore: Switch to local module so artifact is always tied to same version as maps-compose
1 parent ff55775 commit 8e6e0e6

File tree

16 files changed

+566
-3
lines changed

16 files changed

+566
-3
lines changed

README.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,38 @@ and don't use `Marker` composables (unless you don't care about `onClick`
199199
events). Clustering is the only use-case tested with `MapEffect`, there may be
200200
gotchas depending on what features you use in the utility library.
201201

202+
## Widgets
203+
204+
This library also provides optional composable widgets in the `maps-compose-widgets` library that you can use alongside the `GoogleMap` composable.
205+
206+
### ScaleBar
207+
208+
This widget shows the current scale of the map in feet and meters when zoomed into the map, changing to miles and kilometers, respectively, when zooming out. A `DisappearingScaleBar` is also included, which appears when the zoom level of the map changes, and then disappears after a configurable timeout period.
209+
210+
The [ScaleBarActivity](app/src/main/java/com/google/maps/android/compose/ScaleBarActivity.kt) demonstrates both of these, with the `DisappearingScaleBar` in the upper left corner and the normal base `ScaleBar` in the upper right:
211+
212+
![maps-compose-scale-bar-cropped](https://user-images.githubusercontent.com/928045/175665891-a0635004-2201-4392-83b3-0c6553b96926.gif)
213+
214+
Both versions of this widget leverage the `CameraPositionState` in `maps-compose` and therefore are very simple to configure with their defaults:
215+
216+
```kotlin
217+
ScaleBar(
218+
modifier = Modifier
219+
.padding(top = 5.dp, end = 15.dp)
220+
.align(Alignment.TopEnd),
221+
cameraPositionState = cameraPositionState
222+
)
223+
224+
DisappearingScaleBar(
225+
modifier = Modifier
226+
.padding(top = 5.dp, end = 15.dp)
227+
.align(Alignment.TopStart),
228+
cameraPositionState = cameraPositionState
229+
)
230+
```
231+
232+
The colors of the text, line, and shadow are also all configurable (e.g., based on `isSystemInDarkTheme()` on a dark map). Similarly, the `DisappearingScaleBar` animations can be configured.
233+
202234
## Sample App
203235

204236
This repository includes a [sample app](app).
@@ -218,7 +250,10 @@ dependencies {
218250
implementation 'com.google.android.gms:play-services-maps:18.0.2'
219251
220252
// Also include Compose version `1.2.0-alpha03` or higher - for example:
221-
implementation "androidx.compose.foundation:foundation:2.4.0-alpha03"
253+
implementation 'androidx.compose.foundation:foundation:2.4.0-alpha03'
254+
255+
// Optionally, you can include the widgets library if you want to use ScaleBar, etc.
256+
implementation 'com.google.maps.android:maps-compose-widgets:1.0.0'
222257
}
223258
```
224259

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ dependencies {
5959
// declaration if you want to test the sample app with a Maven Central release of the library.
6060
//implementation "com.google.maps.android:maps-compose:2.2.1"
6161
implementation project(':maps-compose')
62+
//implementation "com.google.maps.android:maps-compose-widgets:1.0.0"
63+
implementation project(':maps-compose-widgets')
6264
implementation 'com.google.android.gms:play-services-maps:18.0.2'
6365
}
6466

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
<activity
5353
android:name=".LocationTrackingActivity"
5454
android:exported="false"/>
55+
<activity
56+
android:name=".ScaleBarActivity"
57+
android:exported="false"/>
5558

5659
<!-- Used by createComponentActivity() for unit testing -->
5760
<activity android:name="androidx.activity.ComponentActivity" />

app/src/main/java/com/google/maps/android/compose/MainActivity.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ class MainActivity : ComponentActivity() {
8181
}) {
8282
Text(getString(R.string.location_tracking_activity))
8383
}
84+
Spacer(modifier = Modifier.padding(5.dp))
85+
Button(
86+
onClick = {
87+
context.startActivity(Intent(context, ScaleBarActivity::class.java))
88+
}) {
89+
Text(getString(R.string.scale_bar_activity))
90+
}
8491
}
8592
}
8693
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.maps.android.compose
16+
17+
import android.os.Bundle
18+
import androidx.activity.ComponentActivity
19+
import androidx.activity.compose.setContent
20+
import androidx.compose.animation.AnimatedVisibility
21+
import androidx.compose.animation.EnterTransition
22+
import androidx.compose.animation.fadeOut
23+
import androidx.compose.foundation.background
24+
import androidx.compose.foundation.layout.Box
25+
import androidx.compose.foundation.layout.fillMaxSize
26+
import androidx.compose.foundation.layout.padding
27+
import androidx.compose.foundation.layout.wrapContentSize
28+
import androidx.compose.material.CircularProgressIndicator
29+
import androidx.compose.material.MaterialTheme
30+
import androidx.compose.runtime.getValue
31+
import androidx.compose.runtime.mutableStateOf
32+
import androidx.compose.runtime.remember
33+
import androidx.compose.runtime.setValue
34+
import androidx.compose.ui.Alignment
35+
import androidx.compose.ui.Modifier
36+
import androidx.compose.ui.unit.dp
37+
import com.google.android.gms.maps.model.CameraPosition
38+
import com.google.android.gms.maps.model.LatLng
39+
import com.google.maps.android.compose.widgets.DisappearingScaleBar
40+
import com.google.maps.android.compose.widgets.ScaleBar
41+
42+
private const val TAG = "ScaleBarActivity"
43+
44+
private const val zoom = 8f
45+
private val singapore = LatLng(1.35, 103.87)
46+
private val defaultCameraPosition = CameraPosition.fromLatLngZoom(singapore, zoom)
47+
48+
class ScaleBarActivity : ComponentActivity() {
49+
50+
override fun onCreate(savedInstanceState: Bundle?) {
51+
super.onCreate(savedInstanceState)
52+
53+
setContent {
54+
var isMapLoaded by remember { mutableStateOf(false) }
55+
56+
// To control and observe the map camera
57+
val cameraPositionState = rememberCameraPositionState {
58+
position = defaultCameraPosition
59+
}
60+
61+
Box(Modifier.fillMaxSize()) {
62+
GoogleMap(
63+
modifier = Modifier.matchParentSize(),
64+
cameraPositionState = cameraPositionState,
65+
onMapLoaded = {
66+
isMapLoaded = true
67+
}
68+
)
69+
DisappearingScaleBar(
70+
modifier = Modifier
71+
.padding(top = 5.dp, end = 15.dp)
72+
.align(Alignment.TopStart),
73+
cameraPositionState = cameraPositionState
74+
)
75+
ScaleBar(
76+
modifier = Modifier
77+
.padding(top = 5.dp, end = 15.dp)
78+
.align(Alignment.TopEnd),
79+
cameraPositionState = cameraPositionState
80+
)
81+
if (!isMapLoaded) {
82+
AnimatedVisibility(
83+
modifier = Modifier
84+
.matchParentSize(),
85+
visible = !isMapLoaded,
86+
enter = EnterTransition.None,
87+
exit = fadeOut()
88+
) {
89+
CircularProgressIndicator(
90+
modifier = Modifier
91+
.background(MaterialTheme.colors.background)
92+
.wrapContentSize()
93+
)
94+
}
95+
}
96+
}
97+
}
98+
}
99+
}

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@
2121
<string name="map_in_column_activity">Map In Column</string>
2222
<string name="map_clustering_activity">Map Clustering</string>
2323
<string name="location_tracking_activity">Location Tracking</string>
24+
<string name="scale_bar_activity">Scale Bar</string>
2425
</resources>

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ plugins {
2121
}
2222

2323
ext.projectArtifactId = { project ->
24-
if (project.name == 'maps-compose') {
24+
if (project.name == 'maps-compose' || project.name == 'maps-compose-widgets') {
2525
return project.name
2626
} else {
2727
return null

maps-compose-widgets/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

maps-compose-widgets/build.gradle

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
plugins {
2+
id 'kotlin-android'
3+
id 'kotlin-parcelize'
4+
}
5+
6+
android {
7+
compileSdk 31
8+
9+
defaultConfig {
10+
minSdk 21
11+
targetSdk 31
12+
versionCode 1
13+
versionName "1.0"
14+
}
15+
16+
compileOptions {
17+
sourceCompatibility JavaVersion.VERSION_1_8
18+
targetCompatibility JavaVersion.VERSION_1_8
19+
}
20+
21+
composeOptions {
22+
kotlinCompilerExtensionVersion "$compose_version"
23+
}
24+
25+
buildFeatures {
26+
buildConfig false
27+
compose true
28+
}
29+
30+
kotlinOptions {
31+
jvmTarget = '1.8'
32+
freeCompilerArgs += '-Xexplicit-api=strict'
33+
freeCompilerArgs += '-Xopt-in=kotlin.RequiresOptIn'
34+
}
35+
}
36+
37+
dependencies {
38+
implementation "androidx.compose.foundation:foundation:$compose_version"
39+
implementation project(':maps-compose')
40+
implementation 'androidx.compose.material:material:1.1.1'
41+
implementation 'androidx.core:core-ktx:1.7.0'
42+
implementation 'com.google.android.gms:play-services-maps:18.0.2'
43+
implementation 'com.google.maps.android:maps-ktx:3.4.0'
44+
implementation 'com.google.maps.android:maps-utils-ktx:3.3.0'
45+
46+
testImplementation 'junit:junit:4.13.2'
47+
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
48+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
49+
implementation "androidx.core:core-ktx:1.7.0"
50+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
51+
}

maps-compose-widgets/consumer-rules.pro

Whitespace-only changes.

0 commit comments

Comments
 (0)