📜  如何在 Android 应用中添加优步汽车动画?

📅  最后修改于: 2022-05-13 01:55:43.269000             🧑  作者: Mango

如何在 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 文件并在 标记内添加以下代码。请记住我们复制了 API 密钥,请确保将其粘贴到下方,如下所示。

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)
   }

输出:

在输出中,我们可以看到一辆移动的汽车从一个点移动到另一个点。

输出