Skip to content

Commit d8dc564

Browse files
committed
Change label style
1 parent 4ec13ec commit d8dc564

4 files changed

Lines changed: 65 additions & 22 deletions

File tree

‎app/src/androidTest/java/org/nitri/opentopo/overlay/MarkerAnnotationGeometryTest.kt‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.nitri.opentopo.overlay
22

33
import android.graphics.Canvas
4+
import android.graphics.Color
45
import androidx.test.ext.junit.runners.AndroidJUnit4
56
import androidx.test.platform.app.InstrumentationRegistry
67
import org.junit.Assert.*
@@ -33,7 +34,7 @@ class MarkerAnnotationGeometryTest {
3334
val projection = mock(Projection::class.java)
3435
`when`(projection.orientation).thenReturn(0f)
3536

36-
renderer.draw(canvas, projection, 500f, 500f, "Test", 50, 1.0f, 1000, 1000)
37+
renderer.draw(canvas, projection, 500f, 500f, "Test", Color.RED, 50, 1.0f, 1000, 1000)
3738
assertNotNull(renderer.getLastGeometry())
3839

3940
renderer.clear()
@@ -50,7 +51,7 @@ class MarkerAnnotationGeometryTest {
5051

5152
for (orientation in orientations) {
5253
`when`(projection.orientation).thenReturn(orientation)
53-
renderer.draw(canvas, projection, 500f, 500f, "Label", 50, 1.0f, 1000, 1000)
54+
renderer.draw(canvas, projection, 500f, 500f, "Label", Color.RED, 50, 1.0f, 1000, 1000)
5455

5556
val geometry = renderer.getLastGeometry()!!
5657

@@ -90,7 +91,7 @@ class MarkerAnnotationGeometryTest {
9091
for (orientation in orientations) {
9192
for ((x, y) in corners) {
9293
`when`(projection.orientation).thenReturn(orientation)
93-
renderer.draw(canvas, projection, x, y, "Long Label Content", 50, 1.0f, viewWidth, viewHeight)
94+
renderer.draw(canvas, projection, x, y, "Long Label Content", Color.RED, 50, 1.0f, viewWidth, viewHeight)
9495

9596
val geometry = renderer.getLastGeometry()!!
9697

@@ -110,7 +111,7 @@ class MarkerAnnotationGeometryTest {
110111
`when`(projection.orientation).thenReturn(0f)
111112

112113
val longText = "This is a very long marker name that should definitely be ellipsized to stay within the 120dp limit."
113-
renderer.draw(canvas, projection, 500f, 500f, longText, 50, 1.0f, 1000, 1000)
114+
renderer.draw(canvas, projection, 500f, 500f, longText, Color.RED, 50, 1.0f, 1000, 1000)
114115

115116
val geometry = renderer.getLastGeometry()!!
116117

@@ -122,4 +123,17 @@ class MarkerAnnotationGeometryTest {
122123
assertTrue("Label width ${geometry.localBounds.width()} should be constrained near ${maxAllowedContentWidth + horizontalPadding}",
123124
geometry.localBounds.width() <= maxAllowedContentWidth + horizontalPadding + EPSILON)
124125
}
126+
127+
@Test
128+
fun haloColor_adaptiveSelection() {
129+
// Red is dark (luminance ~0.21) -> should have light halo
130+
val darkColor = Color.RED
131+
val haloForDark = renderer.haloColorFor(darkColor)
132+
assertEquals(255, Color.red(haloForDark)) // White/near-white
133+
134+
// Yellow is light (luminance ~0.92) -> should have dark halo
135+
val lightColor = Color.YELLOW
136+
val haloForLight = renderer.haloColorFor(lightColor)
137+
assertEquals(32, Color.red(haloForLight)) // Dark charcoal
138+
}
125139
}

‎app/src/main/java/org/nitri/opentopo/overlay/CustomMarker.kt‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class CustomMarker(private val mapView: MapView) : Marker(mapView) {
1010
var labelText: String = ""
1111
var labelVisible: Boolean = false
1212
var minimumLabelZoom: Double = 14.0
13+
var markerColor: Int = org.nitri.opentopo.ui.color.DEFAULT_MARKER_COLOR
1314

1415
private val annotationRenderer = MarkerAnnotationRenderer(mapView.context)
1516

@@ -28,7 +29,7 @@ class CustomMarker(private val mapView: MapView) : Marker(mapView) {
2829
val x = mPositionPixels.x.toFloat()
2930
val y = mPositionPixels.y.toFloat()
3031

31-
annotationRenderer.draw(canvas, pj, x, y, labelText, mIcon.intrinsicHeight, mAnchorV, mapView.width, mapView.height)
32+
annotationRenderer.draw(canvas, pj, x, y, labelText, markerColor, mIcon.intrinsicHeight, mAnchorV, mapView.width, mapView.height)
3233
}
3334

3435
internal fun shouldDrawLabel(zoom: Double, markerDisplayed: Boolean): Boolean {

‎app/src/main/java/org/nitri/opentopo/overlay/MarkerAnnotationRenderer.kt‎

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import android.graphics.Color
66
import android.graphics.Matrix
77
import android.graphics.Paint
88
import android.graphics.RectF
9+
import android.graphics.Typeface
910
import android.text.TextPaint
1011
import android.text.TextUtils
1112
import android.util.TypedValue
13+
import androidx.core.graphics.ColorUtils
1214
import org.osmdroid.views.Projection
1315

1416
internal class MarkerAnnotationRenderer(context: Context) {
@@ -19,15 +21,20 @@ internal class MarkerAnnotationRenderer(context: Context) {
1921
val screenBounds: RectF
2022
)
2123

22-
private val labelTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
24+
private val fillTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
2325
textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, context.resources.displayMetrics)
24-
color = Color.BLACK
2526
textAlign = Paint.Align.CENTER
27+
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
2628
}
2729

28-
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
29-
color = Color.WHITE
30-
alpha = 220
30+
private val haloTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
31+
textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, context.resources.displayMetrics)
32+
textAlign = Paint.Align.CENTER
33+
style = Paint.Style.STROKE
34+
strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1.5f, context.resources.displayMetrics)
35+
strokeJoin = Paint.Join.ROUND
36+
strokeCap = Paint.Cap.ROUND
37+
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
3138
}
3239

3340
private val backgroundRect = RectF()
@@ -40,12 +47,13 @@ internal class MarkerAnnotationRenderer(context: Context) {
4047
private val paddingHPx = 4f * density
4148
private val paddingVPx = 2f * density
4249
private val gapPx = 2f * density
43-
private val cornerRadiusPx = 3f * density
50+
private val haloStrokeWidth = haloTextPaint.strokeWidth
4451

4552
private val futureIconSlotWidth = 0f
4653
private val iconTextGap = 4f * density
4754

4855
private var cachedLabelText: String? = null
56+
private var cachedMarkerColor: Int? = null
4957
private var cachedDisplayText = ""
5058
private var cachedTextWidth = 0f
5159
private var cachedTextHeight = 0f
@@ -71,9 +79,9 @@ internal class MarkerAnnotationRenderer(context: Context) {
7179
if (text == cachedLabelText) return
7280

7381
cachedLabelText = text
74-
cachedDisplayText = TextUtils.ellipsize(text, labelTextPaint, maxLabelWidthPx, TextUtils.TruncateAt.END).toString()
75-
cachedTextWidth = labelTextPaint.measureText(cachedDisplayText)
76-
labelTextPaint.getFontMetrics(cachedFontMetrics)
82+
cachedDisplayText = TextUtils.ellipsize(text, fillTextPaint, maxLabelWidthPx, TextUtils.TruncateAt.END).toString()
83+
cachedTextWidth = fillTextPaint.measureText(cachedDisplayText)
84+
fillTextPaint.getFontMetrics(cachedFontMetrics)
7785
cachedTextHeight = cachedFontMetrics.descent - cachedFontMetrics.ascent
7886

7987
cachedContentWidth = if (cachedTextWidth > 0 && futureIconSlotWidth > 0) {
@@ -86,8 +94,24 @@ internal class MarkerAnnotationRenderer(context: Context) {
8694
cachedAnnotationHeight = cachedTextHeight + paddingVPx * 2
8795
}
8896

89-
fun draw(canvas: Canvas, pj: Projection, x: Float, y: Float, text: String, iconHeight: Int, anchorV: Float, viewWidth: Int, viewHeight: Int) {
97+
private fun updateColors(markerColor: Int) {
98+
if (markerColor == cachedMarkerColor) return
99+
cachedMarkerColor = markerColor
100+
fillTextPaint.color = markerColor
101+
haloTextPaint.color = haloColorFor(markerColor)
102+
}
103+
104+
internal fun haloColorFor(markerColor: Int): Int {
105+
return if (ColorUtils.calculateLuminance(markerColor) > 0.65) {
106+
Color.argb(210, 32, 32, 32)
107+
} else {
108+
Color.argb(230, 255, 255, 255)
109+
}
110+
}
111+
112+
fun draw(canvas: Canvas, pj: Projection, x: Float, y: Float, text: String, textColor: Int, iconHeight: Int, anchorV: Float, viewWidth: Int, viewHeight: Int) {
90113
updateCache(text)
114+
updateColors(textColor)
91115

92116
val annotationBottomY = y - iconHeight * anchorV - gapPx
93117
val annotationLeft = x - cachedAnnotationWidth / 2
@@ -98,11 +122,12 @@ internal class MarkerAnnotationRenderer(context: Context) {
98122
// Rotation matrix
99123
annotationMatrix.setRotate(-pj.orientation, x, y)
100124

101-
// Calculate transformed bounds for clamping
102-
cornerPoints[0] = backgroundRect.left; cornerPoints[1] = backgroundRect.top
103-
cornerPoints[2] = backgroundRect.right; cornerPoints[3] = backgroundRect.top
104-
cornerPoints[4] = backgroundRect.right; cornerPoints[5] = backgroundRect.bottom
105-
cornerPoints[6] = backgroundRect.left; cornerPoints[7] = backgroundRect.bottom
125+
// Calculate transformed bounds for clamping, including halo stroke
126+
val haloAllowance = haloStrokeWidth / 2f
127+
cornerPoints[0] = backgroundRect.left - haloAllowance; cornerPoints[1] = backgroundRect.top - haloAllowance
128+
cornerPoints[2] = backgroundRect.right + haloAllowance; cornerPoints[3] = backgroundRect.top - haloAllowance
129+
cornerPoints[4] = backgroundRect.right + haloAllowance; cornerPoints[5] = backgroundRect.bottom + haloAllowance
130+
cornerPoints[6] = backgroundRect.left - haloAllowance; cornerPoints[7] = backgroundRect.bottom + haloAllowance
106131

107132
annotationMatrix.mapPoints(transformedPoints, cornerPoints)
108133

@@ -140,12 +165,14 @@ internal class MarkerAnnotationRenderer(context: Context) {
140165
canvas.save()
141166
canvas.concat(annotationMatrix)
142167

143-
canvas.drawRoundRect(backgroundRect, cornerRadiusPx, cornerRadiusPx, backgroundPaint)
168+
// canvas.drawRoundRect(backgroundRect, cornerRadiusPx, cornerRadiusPx, backgroundPaint) // Removed bubble
144169

145170
val textStartX = annotationLeft + paddingHPx + futureIconSlotWidth + (if (futureIconSlotWidth > 0 && cachedTextWidth > 0) iconTextGap else 0f)
146171
val textX = textStartX + cachedTextWidth / 2
147172
val textY = annotationTop + paddingVPx - cachedFontMetrics.ascent
148-
canvas.drawText(cachedDisplayText, textX, textY, labelTextPaint)
173+
174+
canvas.drawText(cachedDisplayText, textX, textY, haloTextPaint)
175+
canvas.drawText(cachedDisplayText, textX, textY, fillTextPaint)
149176

150177
canvas.restore()
151178
isDrawn = true

‎app/src/main/java/org/nitri/opentopo/overlay/OverlayHelper.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ class OverlayHelper(
284284
mapMarker.title = it.name
285285
mapMarker.labelText = it.name
286286
mapMarker.labelVisible = showLabels
287+
mapMarker.markerColor = it.color
287288
mapMarker.id = it.toString()
288289
mapMarker.relatedObject = it
289290
mapMarker.isDraggable = true

0 commit comments

Comments
 (0)