如何在 Android 应用中添加优步汽车动画?
如今,谷歌地图正被用于许多应用程序中。许多应用程序需要谷歌地图服务用于多种用途。此类应用程序的示例是我们的日常生活应用程序,例如 Zomato、Swiggy 和 amazon,使用谷歌地图进行送货,而 uber 和 ola 等应用程序使用地图来跟踪乘客和司机的实时位置,在上面的所有示例中看到各种类型的动画出现在映射线上,例如在 uber 中显示汽车以跟踪驾驶员的位置并显示在客户屏幕上,在本文中,我们将了解如何在 android 中使用谷歌地图添加这些动画.
- 开始使用 Google 地图平台
- 将谷歌地图与安卓集成
- 在地图中添加动画
开始使用Google 地图平台
第 1 步:转到 https://console.developers.google.com/ 并单击项目下拉按钮并选择一个项目或创建一个。
第 2 步:单击网站左上角的菜单按钮,然后选择 API 和服务,然后会出现另一个下拉列表,从中选择凭据。
第 3 步:进入凭证页面后,单击“创建凭证”按钮,然后单击 API 密钥,然后会出现一个显示 API 密钥的弹出窗口。确保将 API 密钥复制并粘贴到记事本或类似的东西中。
第 4 步:现在返回 API 和服务选项卡并单击“库”按钮,然后搜索 google map SDK for android,然后单击启用。
将谷歌地图与安卓集成
步骤 1:创建一个新项目
要在 Android Studio 中创建新项目,请参阅如何在 Android Studio 中创建/启动新项目。请注意,选择Kotlin作为编程语言。
第 2 步:更新 Android 清单文件
转到 AndroidManifests.xml 文件并在
XML
XML
Kotlin
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var googleMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val map = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
map.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
this.googleMap = googleMap
}
Kotlin
private fun moveView(ll: LatLng) {
googleMap.moveCamera(CameraUpdateFactory.newLatLng(ll))
}
private fun animateView(ll: LatLng) {
val cameraPosition = CameraPosition.Builder().target(ll).zoom(15.5f).build()
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
Kotlin
private lateinit var defaultLocation: LatLng
override fun onMapReady(googleMap: GoogleMap) {
this.googleMap = googleMap
defaultLoc = LatLng(28.435350000000003, 77.11368)
displayDefaultLocation(defaultLoc)
}
Kotlin
fun getLocations(): ArrayList {
val locationList = ArrayList()
locationList.add(LatLng(28.4356, 77.11498))
locationList.add(LatLng(28.435660000000002, 77.11519000000001))
locationList.add(LatLng(28.43568, 77.11521))
locationList.add(LatLng(28.436580000000003, 77.11499))
locationList.add(LatLng(28.436590000000002, 77.11507))
locationList.add(LatLng(28.436970000000002, 77.11272000000001))
locationList.add(LatLng(28.43635, 77.11289000000001))
locationList.add(LatLng(28.4353, 77.11317000000001))
locationList.add(LatLng(28.435280000000002, 77.11332))
locationList.add(LatLng(28.435350000000003, 77.11368))
return locationList
}
Kotlin
fun getStartingLocationBitmap(): Bitmap {
val height = 40
val width = 40
val bitmap = Bitmap.createBitmap(height, width, Bitmap.Config.RGB_565)
val canvas = Canvas(bitmap)
val paint = Paint()
paint.color = Color.BLACK
paint.style = Paint.Style.FILL
paint.isAntiAlias = true
canvas.drawRect(0F, 0F, width.toFloat(), height.toFloat(), paint)
return bitmap
}
Kotlin
fun getCarRotation(startLL: LatLng, endLL: LatLng): Float {
val latDifference: Double = abs(startLL.latitude - endLL.latitude)
val lngDifference: Double = abs(startLL.longitude - endLL.longitude)
var rotation = -1F
when {
startLL.latitude < endLL.latitude && startLL.longitude < endLL.longitude -> {
rotation = Math.toDegrees(atan(lngDifference / latDifference)).toFloat()
}
startLL.latitude >= endLL.latitude && startLL.longitude < endLL.longitude -> {
rotation = (90 - Math.toDegrees(atan(lngDifference / latDifference)) + 90).toFloat()
}
startLL.latitude >= endLL.latitude && startLL.longitude >= endLL.longitude -> {
rotation = (Math.toDegrees(atan(lngDifference / latDifference)) + 180).toFloat()
}
startLL.latitude < endLL.latitude && startLL.longitude >= endLL.longitude -> {
rotation =
(90 - Math.toDegrees(atan(lngDifference / latDifference)) + 270).toFloat()
}
}
return rotation
}
Kotlin
fun polyAnimator(): ValueAnimator {
val valueAnimator = ValueAnimator.ofInt(0, 100)
valueAnimator.interpolator = LinearInterpolator()
valueAnimator.duration = 4000
return valueAnimator
}
fun carAnimator(): ValueAnimator {
val valueAnimator = ValueAnimator.ofFloat(0f, 1f)
valueAnimator.duration = 3000
valueAnimator.interpolator = LinearInterpolator()
return valueAnimator
}
Kotlin
private fun getCarMarker(ll: LatLng): Marker {
val bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(MapUtils.getBitmap(this))
return googleMap.addMarker(
MarkerOptions().position(ll).flat(true).icon(bitmapDescriptor)
)
}
private fun getOriginMarker(ll: LatLng): Marker {
val bitmapDescriptor =
BitmapDescriptorFactory.fromBitmap(MapUtils.getStartingLocationBitmap())
return googleMap.addMarker(
MarkerOptions().position(ll).flat(true).icon(bitmapDescriptor)
)
}
Kotlin
private fun displayPath(latLngList: ArrayList) {
val builder = LatLngBounds.Builder()
for (latLng in latLngList) {
builder.include(latLng)
}
val boundBuilds = builder.build()
googleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(boundBuilds, 2))
val polyOptions = PolylineOptions()
polyOptions.color(Color.GRAY)
polyOptions.width(5f)
polyOptions.addAll(latLngList)
greyLine = googleMap.addPolyline(polyOptions)
val blackPolyOptions = PolylineOptions()
blackPolyOptions.color(Color.BLACK)
blackPolyOptions.width(5f)
blackLine = googleMap.addPolyline(blackPolyOptions)
oMarker = getOriginMarker(latLngList[0])
oMarker?.setAnchor(0.5f, 0.5f)
dMarker = getOriginMarker(latLngList[latLngList.size - 1])
dMarker?.setAnchor(0.5f, 0.5f)
val polyAnimator = AnimationUtils.polyAnimator()
polyAnimator.addUpdateListener { valueAnimator ->
val precent = (valueAnimator.animatedValue as Int)
val indexNumber = (greyLine?.points!!.size) * (precent / 100.0f).toInt()
blackLine?.points = greyLine?.points!!.subList(0, indexNumber)
}
polyAnimator.start()
}
Kotlin
private fun updateCarLoc(ll: LatLng) {
if (movingMarker == null) {
movingMarker = getCarMarker(ll)
}
if (previousLL == null) {
currentLL = ll
previousLL = currentLL
movingMarker?.position = currentLL
movingMarker?.setAnchor(0.5f, 0.5f)
animateView(currentLL!!)
} else {
previousLL = currentLL
currentLL = ll
val valueAnimator = AnimationUtils.carAnimator()
valueAnimator.addUpdateListener { va ->
if (currentLL != null && previousLL != null) {
val multiplier = va.animatedFraction
val nxtLoc = LatLng(
multiplier * currentLL!!.latitude + (1 - multiplier) * previousLL!!.latitude,
multiplier * currentLL!!.longitude + (1 - multiplier) * previousLL!!.longitude
)
movingMarker?.position = nxtLoc
val rotation = MapUtils.getCarRotation(previousLL!!, nxtLoc)
if (!rotation.isNaN()) {
movingMarker?.rotation = rotation
}
movingMarker?.setAnchor(0.5f, 0.5f)
animateView(nxtLoc)
}
}
valueAnimator.start()
}
}
Kotlin
private fun displayMovingCar(cabLatLngList: ArrayList) {
myHandler = Handler()
var index = 0
myRunnable = Runnable {
run {
if (index < 10) {
updateCarLoc(cabLatLngList[index])
myHandler.postDelayed(myRunnable, 3000)
++index
} else {
myHandler.removeCallbacks(myRunnable)
Toast.makeText(this@MainActivity, "Trip Ends", Toast.LENGTH_LONG).show()
}
}
}
myHandler.postDelayed(myRunnable, 5000)
}
第 3 步:在 activity_main.xml 文件中添加 Google 地图片段
我们将添加将在其中显示地图的谷歌地图片段。在添加下面的代码之前删除activity_main.xml中的所有代码
XML
第 4 步:在 MainActivity 文件中设置谷歌地图
只需定义 Google Map 片段并实现 OnMapReadyCallBack 接口并覆盖 onMapReady()函数,如下所示
科特林
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var googleMap: GoogleMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val map = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
map.getMapAsync(this)
}
override fun onMapReady(googleMap: GoogleMap) {
this.googleMap = googleMap
}
第 5 步:在地图上显示默认位置
默认情况下,地图可以显示任何随机位置,但我们可以随意显示位置。在这个项目中,当应用程序启动时,我们将使用 LatLng(28.435350000000003, 77.11368) 位置作为默认位置。所以,我们需要制作两个函数 moveCamera() 和 animateCamera()。 moveCamera() 方法将用于将相机重新定位到某个纬度和经度。虽然 animateCamera() 方法将用于动画相机从当前位置到某个新位置的移动。
科特林
private fun moveView(ll: LatLng) {
googleMap.moveCamera(CameraUpdateFactory.newLatLng(ll))
}
private fun animateView(ll: LatLng) {
val cameraPosition = CameraPosition.Builder().target(ll).zoom(15.5f).build()
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
制作另一个函数showDefaultLocaitonOnMap() 并在 onMapReady() 中调用它。
private fun displayDefaultLocation(ll: LatLng) {
moveView(ll)
animateView(ll)
}
科特林
private lateinit var defaultLocation: LatLng
override fun onMapReady(googleMap: GoogleMap) {
this.googleMap = googleMap
defaultLoc = LatLng(28.435350000000003, 77.11368)
displayDefaultLocation(defaultLoc)
}
第 6 步:创建实用程序
创建两个新的 Kotlin 目标文件,
- MapUtils.kt
- AnimationUtils.kt
第 7 步:使用 MapUtils 文件
在我们的项目中,我们将使用 LatLng 的一些硬编码值。因此,在 MapUtils 类中,让我们创建一个函数getLocations(),它将返回对应于路径的 LatLng 列表。
科特林
fun getLocations(): ArrayList {
val locationList = ArrayList()
locationList.add(LatLng(28.4356, 77.11498))
locationList.add(LatLng(28.435660000000002, 77.11519000000001))
locationList.add(LatLng(28.43568, 77.11521))
locationList.add(LatLng(28.436580000000003, 77.11499))
locationList.add(LatLng(28.436590000000002, 77.11507))
locationList.add(LatLng(28.436970000000002, 77.11272000000001))
locationList.add(LatLng(28.43635, 77.11289000000001))
locationList.add(LatLng(28.4353, 77.11317000000001))
locationList.add(LatLng(28.435280000000002, 77.11332))
locationList.add(LatLng(28.435350000000003, 77.11368))
return locationList
}
现在,我们有一个 LatLng 列表,在 LatLng 的帮助下,我们可以在 Origin 和 Destination 之间绘制一条路径。现在我们将创建一个函数getStartingLocationBitmap(),它将返回一个位图。函数如下
科特林
fun getStartingLocationBitmap(): Bitmap {
val height = 40
val width = 40
val bitmap = Bitmap.createBitmap(height, width, Bitmap.Config.RGB_565)
val canvas = Canvas(bitmap)
val paint = Paint()
paint.color = Color.BLACK
paint.style = Paint.Style.FILL
paint.isAntiAlias = true
canvas.drawRect(0F, 0F, width.toFloat(), height.toFloat(), paint)
return bitmap
}
现在创建一个名为 getCarRotation() 的新函数,它接受两个位置,即开始和结束,然后返回它们之间的角度,从它将返回的角度,我们将确定汽车图标的方向。
科特林
fun getCarRotation(startLL: LatLng, endLL: LatLng): Float {
val latDifference: Double = abs(startLL.latitude - endLL.latitude)
val lngDifference: Double = abs(startLL.longitude - endLL.longitude)
var rotation = -1F
when {
startLL.latitude < endLL.latitude && startLL.longitude < endLL.longitude -> {
rotation = Math.toDegrees(atan(lngDifference / latDifference)).toFloat()
}
startLL.latitude >= endLL.latitude && startLL.longitude < endLL.longitude -> {
rotation = (90 - Math.toDegrees(atan(lngDifference / latDifference)) + 90).toFloat()
}
startLL.latitude >= endLL.latitude && startLL.longitude >= endLL.longitude -> {
rotation = (Math.toDegrees(atan(lngDifference / latDifference)) + 180).toFloat()
}
startLL.latitude < endLL.latitude && startLL.longitude >= endLL.longitude -> {
rotation =
(90 - Math.toDegrees(atan(lngDifference / latDifference)) + 270).toFloat()
}
}
return rotation
}
上面提到的每个函数都将放在 MapUtils 文件中。
第 8 步:使用 AnimationUtils 文件
创建两个函数 polyAnimator() 和 carAnimator,它们将返回一个插值器值,如下所示
科特林
fun polyAnimator(): ValueAnimator {
val valueAnimator = ValueAnimator.ofInt(0, 100)
valueAnimator.interpolator = LinearInterpolator()
valueAnimator.duration = 4000
return valueAnimator
}
fun carAnimator(): ValueAnimator {
val valueAnimator = ValueAnimator.ofFloat(0f, 1f)
valueAnimator.duration = 3000
valueAnimator.interpolator = LinearInterpolator()
return valueAnimator
}
第 9 步:使用 MainActivity
创建两个新函数 getCarMarker() 和 getOrigonMarker() 谁将返回一个标记对象,它将用于更新汽车位置。创建如下所示的函数
科特林
private fun getCarMarker(ll: LatLng): Marker {
val bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(MapUtils.getBitmap(this))
return googleMap.addMarker(
MarkerOptions().position(ll).flat(true).icon(bitmapDescriptor)
)
}
private fun getOriginMarker(ll: LatLng): Marker {
val bitmapDescriptor =
BitmapDescriptorFactory.fromBitmap(MapUtils.getStartingLocationBitmap())
return googleMap.addMarker(
MarkerOptions().position(ll).flat(true).icon(bitmapDescriptor)
)
}
现在创建一个名为 displayPath() 的函数,它将获取一个 LatLng 列表,该函数将用于在起点和终点之间绘制一条路径。
科特林
private fun displayPath(latLngList: ArrayList) {
val builder = LatLngBounds.Builder()
for (latLng in latLngList) {
builder.include(latLng)
}
val boundBuilds = builder.build()
googleMap.animateCamera(CameraUpdateFactory.newLatLngBounds(boundBuilds, 2))
val polyOptions = PolylineOptions()
polyOptions.color(Color.GRAY)
polyOptions.width(5f)
polyOptions.addAll(latLngList)
greyLine = googleMap.addPolyline(polyOptions)
val blackPolyOptions = PolylineOptions()
blackPolyOptions.color(Color.BLACK)
blackPolyOptions.width(5f)
blackLine = googleMap.addPolyline(blackPolyOptions)
oMarker = getOriginMarker(latLngList[0])
oMarker?.setAnchor(0.5f, 0.5f)
dMarker = getOriginMarker(latLngList[latLngList.size - 1])
dMarker?.setAnchor(0.5f, 0.5f)
val polyAnimator = AnimationUtils.polyAnimator()
polyAnimator.addUpdateListener { valueAnimator ->
val precent = (valueAnimator.animatedValue as Int)
val indexNumber = (greyLine?.points!!.size) * (precent / 100.0f).toInt()
blackLine?.points = greyLine?.points!!.subList(0, indexNumber)
}
polyAnimator.start()
}
创建函数updateCarLoc() 它将检查汽车是否正在移动,如果汽车尚未移动,则此函数将使汽车移动。
科特林
private fun updateCarLoc(ll: LatLng) {
if (movingMarker == null) {
movingMarker = getCarMarker(ll)
}
if (previousLL == null) {
currentLL = ll
previousLL = currentLL
movingMarker?.position = currentLL
movingMarker?.setAnchor(0.5f, 0.5f)
animateView(currentLL!!)
} else {
previousLL = currentLL
currentLL = ll
val valueAnimator = AnimationUtils.carAnimator()
valueAnimator.addUpdateListener { va ->
if (currentLL != null && previousLL != null) {
val multiplier = va.animatedFraction
val nxtLoc = LatLng(
multiplier * currentLL!!.latitude + (1 - multiplier) * previousLL!!.latitude,
multiplier * currentLL!!.longitude + (1 - multiplier) * previousLL!!.longitude
)
movingMarker?.position = nxtLoc
val rotation = MapUtils.getCarRotation(previousLL!!, nxtLoc)
if (!rotation.isNaN()) {
movingMarker?.rotation = rotation
}
movingMarker?.setAnchor(0.5f, 0.5f)
animateView(nxtLoc)
}
}
valueAnimator.start()
}
}
最后,创建函数displayMovingCar(),我们将在其中调用 updateCarLoc() 直到位置的端点。我们还将使用处理程序来显示延迟。
科特林
private fun displayMovingCar(cabLatLngList: ArrayList) {
myHandler = Handler()
var index = 0
myRunnable = Runnable {
run {
if (index < 10) {
updateCarLoc(cabLatLngList[index])
myHandler.postDelayed(myRunnable, 3000)
++index
} else {
myHandler.removeCallbacks(myRunnable)
Toast.makeText(this@MainActivity, "Trip Ends", Toast.LENGTH_LONG).show()
}
}
}
myHandler.postDelayed(myRunnable, 5000)
}
输出:
在输出中,我们可以看到一辆移动的汽车从一个点移动到另一个点。