improvements to marker animations

This commit is contained in:
Johan von Forstner
2020-06-27 18:45:33 +02:00
parent 71f9a25c5a
commit 8abd5219aa
2 changed files with 56 additions and 57 deletions

View File

@@ -91,6 +91,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
private var clusterMarkers: List<Marker> = emptyList()
private var searchResultMarker: Marker? = null
private var connectionErrorSnackbar: Snackbar? = null
private var previousChargepointIds: Set<Long>? = null
private lateinit var clusterIconGenerator: ClusterIconGenerator
private lateinit var chargerIconGenerator: ChargerIconGenerator
@@ -643,39 +644,41 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
val chargers = chargepoints.filterIsInstance<ChargeLocation>()
val chargepointIds = chargers.map { it.id }.toSet()
// remove markers that disappeared
markers.entries.toList().forEach {
if (!chargepointIds.contains(it.value.id)) {
if (it.key.isVisible) {
val tint = getMarkerTint(it.value)
val highlight = it.value == vm.chargerSparse.value
val fault = it.value.faultReport != null
animator.animateMarkerDisappear(it.key, tint, highlight, fault)
} else {
it.key.remove()
if (chargers != markers.values) {
// remove markers that disappeared
val bounds = map.projection.visibleRegion.latLngBounds
markers.entries.toList().forEach {
if (!chargepointIds.contains(it.value.id)) {
// animate marker if it is visible, otherwise remove immediately
if (bounds.contains(it.key.position)) {
val tint = getMarkerTint(it.value)
val highlight = it.value == vm.chargerSparse.value
val fault = it.value.faultReport != null
animator.animateMarkerDisappear(it.key, tint, highlight, fault)
} else {
it.key.remove()
}
markers.remove(it.key)
}
markers.remove(it.key)
}
}
// add new markers
chargers.filter {
!markers.containsValue(it)
}.forEach { charger ->
val tint = getMarkerTint(charger)
val highlight = charger == vm.chargerSparse.value
val fault = charger.faultReport != null
val marker = map.addMarker(
MarkerOptions()
.position(LatLng(charger.coordinates.lat, charger.coordinates.lng))
.icon(
chargerIconGenerator.getBitmapDescriptor(
tint, highlight = highlight,
fault = charger.faultReport != null
)
// add new markers
val map1 = markers.values.map { it.id }
chargers.forEach { charger ->
if (!map1.contains(charger.id)) {
val tint = getMarkerTint(charger)
val highlight = charger == vm.chargerSparse.value
val fault = charger.faultReport != null
val marker = map.addMarker(
MarkerOptions()
.position(LatLng(charger.coordinates.lat, charger.coordinates.lng))
.visible(false)
)
)
animator.animateMarkerAppear(marker, tint, highlight, fault)
markers[marker] = charger
animator.animateMarkerAppear(marker, tint, highlight, fault)
markers[marker] = charger
}
}
previousChargepointIds = chargepointIds
}
clusterMarkers = clusters.map { cluster ->
map.addMarker(

View File

@@ -19,7 +19,7 @@ fun getMarkerTint(charger: ChargeLocation): Int = when {
}
class MarkerAnimator(val gen: ChargerIconGenerator) {
val animatingMarkers = hashMapOf<Marker, ValueAnimator>()
private val animatingMarkers = hashMapOf<String, ValueAnimator>()
fun animateMarkerAppear(
marker: Marker,
@@ -27,18 +27,13 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
highlight: Boolean,
fault: Boolean
) {
animatingMarkers[marker]?.cancel()
animatingMarkers.remove(marker)
animatingMarkers[marker.id]?.cancel()
animatingMarkers.remove(marker.id)
val anim = ValueAnimator.ofInt(0, 20).apply {
duration = 250
interpolator = LinearOutSlowInInterpolator()
addUpdateListener { animationState ->
if (!marker.isVisible) {
cancel()
animatingMarkers.remove(marker)
return@addUpdateListener
}
val scale = animationState.animatedValue as Int
marker.setIcon(
gen.getBitmapDescriptor(
@@ -48,12 +43,15 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
fault = fault
)
)
marker.isVisible = true
}
addListener(onEnd = {
animatingMarkers.remove(marker)
animatingMarkers.remove(marker.id)
}, onCancel = {
animatingMarkers.remove(marker.id)
})
}
animatingMarkers[marker] = anim
animatingMarkers[marker.id] = anim
anim.start()
}
@@ -63,18 +61,13 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
highlight: Boolean,
fault: Boolean
) {
animatingMarkers[marker]?.cancel()
animatingMarkers.remove(marker)
animatingMarkers[marker.id]?.cancel()
animatingMarkers.remove(marker.id)
val anim = ValueAnimator.ofInt(20, 0).apply {
duration = 200
interpolator = FastOutLinearInInterpolator()
addUpdateListener { animationState ->
if (!marker.isVisible) {
cancel()
animatingMarkers.remove(marker)
return@addUpdateListener
}
val scale = animationState.animatedValue as Int
marker.setIcon(
gen.getBitmapDescriptor(
@@ -86,32 +79,35 @@ class MarkerAnimator(val gen: ChargerIconGenerator) {
)
}
addListener(onEnd = {
animatingMarkers.remove(marker)
marker.remove()
animatingMarkers.remove(marker.id)
}, onCancel = {
marker.remove()
animatingMarkers.remove(marker.id)
})
}
animatingMarkers[marker] = anim
animatingMarkers[marker.id] = anim
anim.start()
}
fun animateMarkerBounce(marker: Marker) {
animatingMarkers[marker]?.cancel()
animatingMarkers.remove(marker)
animatingMarkers[marker.id]?.cancel()
animatingMarkers.remove(marker.id)
val anim = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 700
interpolator = BounceInterpolator()
addUpdateListener { state ->
if (!marker.isVisible) {
cancel()
animatingMarkers.remove(marker)
return@addUpdateListener
}
val t = max(1f - state.animatedValue as Float, 0f) / 2
marker.setAnchor(0.5f, 1.0f + t)
}
addListener(onEnd = {
animatingMarkers.remove(marker.id)
}, onCancel = {
animatingMarkers.remove(marker.id)
})
}
animatingMarkers[marker] = anim
animatingMarkers[marker.id] = anim
anim.start()
}
}