Bài viết trước, mình đã hướng dẫn các xây dựng ứng dụng theo dõi vị trí của một xe dựa trên GPS và Firebase. Trong lúc làm bài hướng dẫn đó, mình chợt nảy ra ý tưởng nâng cao hơn chút. Đó là xây dựng ứng dụng theo dõi khoảng cách có thể tính toán được khoảng cách của xe với vị trí hiện tại của bạn.
Tại sao mình lại có ý tưởng đó? Chắc hẳn bạn đã từng sử dụng ứng dụng Uber hay Grab rồi đúng không?
Mình thấy tính năng tính toán khoảng cách khi tài xế bắt đầu di chuyển rất hay ho. Không dài dòng nữa, chúng ta cùng nhau tự xây dựng code ứng dụng theo dõi khoảng cách xe của riêng mình nhé.
Nội dung chính của bài viết
Xây dựng ứng dụng theo dõi khoảng cách
Trong bài viết này, mình sẽ không xây dựng một ứng dụng đồ sộ kiểu như Grab hay Uber. Đơn giản, mình sẽ chỉ hướng dẫn các bạn xây dựng một số tính năng cơ bản như:
- Hiển thị vị trí hiện tại của người dùng lên bản đồ
- Dịch chuyển marker vị trí người dùng trên bản đồ khi người dùng di chuyển
- Tính toán khoảng cách sử dụng Google Distance Matrix API.
#1. Chuẩn bị trước cho ứng dụng theo dõi khoảng cách
- Android Google Maps API key
- Google Distance Matrix API key
Bạn cần đăng ký với Google để có thể lấy hai key này. Các bạn đăng ký tại đây: Maps API key và Distance Matrix API key
#2. Cấu hình dự án ứng dụng theo dõi khoảng cách
Sau khi bạn tạo dự án mới bằng Android Studio, bạn vào build.gradle
để thêm thư viện sau:
// PlayServices dependency implementation 'com.google.android.gms:play-services-maps:16.0.0' implementation 'com.google.android.gms:play-services-location:16.0.0' // Map services dependency implementation 'com.google.maps:google-maps-services:0.2.4'
#3. Hiển thị vị trí hiện tại lên bản đồ
Trong bài viết này, mình sử dụng ViewModel và LiveData để lưu trữ vị trí hiện tại của người dùng.
Vì vậy, nếu hoạt động được tạo lại do thay đổi cấu hình. Như xoay ngang điện thoại, nó sẽ ngay lập tức nhận vị trí trước đó của người dùng
// 1 private val locationLiveData = NonNullMediatorLiveData<Location>() // 2 fun currentLocation() : LiveData<Location> = locatinLiveData // 3 object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult?) { super.onLocationResult(locationResult) locationResult?.let { val location = it.lastLocation val accuracy = currentLocation.accuracy if (!location.hasAccuracy() || accuracy > 35f) return locationLiveData.value = location } }
Mình sẽ giải thích về đoạn code trên:
– Lớp NonNullMediatorLiveData kế thừa từ lớp MediatorLiveData. Mục đích là vừa kế thừa những hàm tính năng của MediatorLiveData, lại vừa hạn chế những lỗi liên quan đến
– LocationCallback được sử dụng để nhận Location. Phương thức onLocationResult được gọi khi vị trí của thiết bị khả dụng. Trong hàm trên, đầu tiên mình kiểm tra xem độ chính xác của vị trí có nằm trong bán kính 35 mét không? Nếu vị trí trong bán kính thì chỉ cần chúng ta thiết lập vị trí vào locationLiveData.
Bạn có thể đọc thêm về độ chính xác vị trí tại đây.
Nhiều bạn sẽ thắc mắc là tại sao mình lại nói lớp NonNullMediatorLiveData kế thừa từ lớp MediatorLiveData mà không thấy khai báo ở đâu cả?
Thực chất mình khai báo kế thừa trong class NonNullMediatorLiveData như bên dưới:
class NonNullMediatorLiveData<T> : MediatorLiveData<T>()
#4. Dịch chuyển marker vị trí người dùng trên bản đồ khi người dùng di chuyển
viewModel.currentLocation() .nonNull() .observe(this) { if(currentMarker != null) animateMarker(currentMarker) else currentMarker = addNewMarker() }
Các bạn thêm đoạn mã này vào Activity.
Lưu ý là bạn có thể thấy một thay đổi nho nhỏ trong việc tracking vị trí. Phương pháp tracking location đó là:
- Đầu tiên, mình kiểm tra xem marker đánh dấu vị trí đã được khởi tạo rồi hay chưa (nếu NULL là chưa khởi tạo).
- Nếu đã khởi tạo thì chúng ta chỉ cần thêm điểm đánh dấu mới vào bằng cách gọi animateMarker(currentMarket). Còn không thì chúng ta tạo mới một marker.
Dưới đây là kotlin extension function mà chúng ta sử dụng ở trên cho ví dụ LiveData.
fun <T> LiveData<T>.nonNull(): NonNullMediatorLiveData<T> { val mediator: NonNullMediatorLiveData<T> = NonNullMediatorLiveData() mediator.addSource(this) { it?.let { mediator.value = it } } return mediator } fun <T> NonNullMediatorLiveData<T>.observe(owner: LifecycleOwner, observer: (t: T) -> Unit) { this.observe(owner, android.arch.lifecycle.Observer { it?.let(observer) }) }
#4. Tính toán khoảng cách sử dụng Google Distance Matrix API
// 1 private val distanceTracker = NonNullMediatorLiveData<Long>() // 2 fun distanceTracker(): LiveData<Long> = distanceTracker // 3 fun startLocationTracking() { locationTrackingCoordinates = locationLiveData.value compositeDisposable.add(Observable.interval(10, TimeUnit.SECONDS) .subscribeOn(appRxScheduler.threadPoolSchedulers()) .subscribe({ _ -> makeDistanceCalculationCall() } , { _ -> startLocationTracking() })) } // 4 private fun makeDistanceCalculationCall() { val tempLocation = locationLiveData.value val origin = arrayOf(locationTrackingCoordinates.latitude.toString() + "," + locationTrackingCoordinates.longitude) val destination = arrayOf(tempLocation.latitude.toString() + "," + tempLocation.longitude.toString()) DistanceMatrixApi.getDistanceMatrix(googleMapHelper.geoContextDistanceApi(), origin, destination) .mode(TravelMode.WALKING) .setCallback(object : PendingResult.Callback<DistanceMatrix> { override fun onResult(result: DistanceMatrix) { locationTrackingCoordinates = tempLocation val temp = result.rows[0].elements[0].distance.inMeters totalDistance += temp distanceTracker.postValue(totalDistance) } override fun onFailure(e: Throwable) { } }) }
Các bạn thêm đoạn mã ở trên vào lớp ViewModel.
- Mình vẫn sử dụng NonNullMedoderLiveData cho distanceTracker để cập nhật tổng khoảng cách bất cứ khi nào tính được khoảng cách mới.
- Chỉ hiển thị public LiveData thông qua hàm
distanceTracker()
. - Hàm
startLocationTracking()
được gọi khi ứng dụng nhận vị trí đầu tiên.
Bạn thấy trong hàm này, chúng ta có một bộ đếm thời gian với khoảng thời gian mười giây. Cứ sau mười giây, chúng ta lại gọi hàm tính khoảng cách từ Google API. Tại sao lại 10s?
Vì chúng ta không thực hiện việc tính theo thời gian thực được nên đành dùng phương pháp tiktok này. Mình nghĩ 10s là thời gian đủ ngắn rồi.
Chúng ta lưu trữ vị trí hiện tại của người dùng trong tempLocation. Sau đó, chúng ta chỉ gọi hàm Distance Matrix.
Lớp DistanceMatrixApi là class trong thự viện mà chúng ta đã thêm trong build.gradle
ở đầu bài viết. Bạn có thể đọc thêm về cách sử dụng thư viện ở đây trên Github.
Trong phương thức onResult()
, chúng ta cần cập nhật locationTrackingCoordinates. Lấy khoảng cách tính bằng mét và đặt tổng khoảng cách thành distanceTracker.
Lưu ý, bạn thấy mình sử dụng phương thức postValue để cập nhật distanceTracker. Lý do là chúng ta cập nhật vị trí từ background thread lên UI nên không thể làm theo cách thông thường được, mà phải thông qua hàm postValue.
Như vậy là chúng gần như đã hoàn thành các tính toán logic rồi đấy.
Công việc cuối cùng là khai báo license khoảng cách vào Activity.
viewModel.distanceTracker() .nonNull() .observe(this) { distanceCoveredTextView.text = it }
Đến đây thì bạn đã hoàn thành xây dựng code ứng dụng theo dõi khoảng cách sử dụng Google API rồi. Nếu bạn vẫn còn chưa hiểu thì có thể download toàn bộ source code của bài viết tại đây
Rất hi vọng, bài viết này sẽ giúp ích cho các bạn trong việc tạo nên ứng dụng theo dõi khoảng cách. Đừng quên like và chia sẻ với bạn nhé.
Mình làm chỉ đường đi cho khách từ vị trí của khách tới spa á anh
Vậy bạn thử tìm hiểu google map api nhé. Cái này google nó có hỗ trợ đấy
Mình cần làm bản đồ chỉ đường đi cho website tới spa nhưng đọc k hiểu lắm bạn có thể giúp k ạ
Bạn có thể nói rõ hơn khúc mắc của bạn không? Chứ chung chung thế này thì mình không biết hỗ trợ gì đâu.