按照建议,我通过应用自定义适配器解决了这个问题。但是这个适配器类似于 Google 的 Google Place API 版本。感谢this guide
扩展功能
首先你需要为TextAutoSuggestionRequest类添加这个扩展函数,将回调转换为协程,以便像同步代码一样使用它
suspend fun TextAutoSuggestionRequest.await(): MutableList<AutoSuggest> {
return suspendCoroutine { continuation ->
execute { suggestions, errorCode ->
if (errorCode == ErrorCode.NONE) {
continuation.resume(suggestions)
} else {
continuation.resumeWithException(Exception("Error code: $errorCode"))
}
}
}
}
locationServicesAddress()
然后添加这个转换器。我通过标准定位服务而不是 Here GeoCoder 将地理坐标转换为文本,因为我不喜欢它返回地址的方式。
fun locationServiceAddress(context: Context, coordinate: GeoCoordinate): String {
val googleGeoCoder = Geocoder(context)
val addresses = googleGeoCoder.getFromLocation(coordinate.latitude, coordinate.longitude, 1)
return addresses[0].getAddressLine(0)
}
虽然为了简单起见,您可以将 Here GeoCoder 与另一个扩展功能一起使用:
suspend fun ReverseGeocodeRequest.await(): String {
return suspendCoroutine { continuation ->
execute { location, errorCode ->
if (errorCode == ErrorCode.NONE) {
continuation.resume(location.address.text)
} else {
continuation.resumeWithException(Exception("Error code: $errorCode"))
}
}
}
}
SuggestionsAdapter.kt
添加此适配器
请注意,如果您尝试在getFilter() 中返回object : Filter() {},它将无法正常工作,因为请求将堆叠在该对象中而不是中断(重新创建类)
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Filter
import android.widget.TextView
import androidx.lifecycle.MutableLiveData
import com.here.android.mpa.common.GeoCoordinate
import com.here.android.mpa.search.AutoSuggestPlace
import com.here.android.mpa.search.TextAutoSuggestionRequest
import kotlinx.coroutines.*
import timber.log.Timber
data class AddressItem(val coordinate: GeoCoordinate, val addressText: String)
class SuggestionsAdapter(context: Context, private val resourceId: Int, private val coordinate: GeoCoordinate) : ArrayAdapter<AddressItem>(context, resourceId, ArrayList<AddressItem>()) {
companion object {
private val _isFetching = MutableLiveData<Boolean>()
val isFetching: LiveData<Boolean>
get() = _isFetching
}
private var suggestions = ArrayList<AddressItem>()
private val customFilter = CustomFilter()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var view = convertView
if (view == null) {
view = LayoutInflater.from(parent.context!!).inflate(resourceId, parent, false)
}
val item = getItem(position)
if (item != null) {
val addressText = view!!.findViewById<TextView>(R.id.item_address_text)
addressText.text = item.addressText
}
return view!!
}
override fun getItem(position: Int): AddressItem? {
return try {
suggestions[position]
} catch (e: Exception) {
Timber.d("Item is NULL")
null
}
}
override fun getCount(): Int {
return suggestions.size
}
override fun getItemId(position: Int) = position.toLong()
override fun getFilter(): Filter = customFilter
inner class CustomFilter : Filter() {
override fun convertResultToString(resultValue: Any?): CharSequence {
if (resultValue != null) {
val address = resultValue as AddressItem
return address.addressText
}
return "" // if item is null
}
override fun performFiltering(prefix: CharSequence?): FilterResults {
val results = FilterResults()
val suggestions = ArrayList<AddressItem>()
if (prefix == null || prefix.isEmpty()) {
results.values = ArrayList<AddressItem>()
results.count = 0
} else {
val request = TextAutoSuggestionRequest(prefix.toString()).setSearchCenter(coordinate)
Timber.d("Start perform filtering")
runBlocking {
Timber.d("Blocking coroutine scope started")
withContext(Dispatchers.Main) {
isFetching.value = true
}
// Get places on IO thread
val requestResult = withContext(Dispatchers.IO) {
Timber.d("Getting places on IO thread")
request.await()
}
var i = 0
for (place in requestResult) {
i++
// If there are more than 10 suggestions break the loop because the more addresses found the more time need to process them to a string
if (i == 10) {
break
}
if (place is AutoSuggestPlace) {
val item = withContext(Dispatchers.IO) {
AddressItem(place.position, locationServiceAddress(context, place.position))
}
suggestions.add(item)
}
}
Timber.d("Blocking coroutine scope finished")
withContext(Dispatchers.Main) {
isFetching.value = false
}
results.apply {
values = suggestions
count = suggestions.size
}
Timber.d("Filtered results: ${suggestions}")
}
}
return results
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
try {
if (results?.count!! > 0 && results?.values != null) {
suggestions = results.values as ArrayList<AddressItem>
notifyDataSetChanged()
} else {
suggestions = ArrayList()
notifyDataSetInvalidated()
}
} catch (e: Exception) {
Timber.d("Caught exception: ${e.message}")
}
}
}
}
并在此处设置 SupporMapFragment.init() 回调(如果 Error.NONE)像这样
val adapter = SuggestionsAdapter(context!!, R.layout.item_address, map.center)
binding.searchBox.setAdapter(adapter)
然后你可以观察isFetching来反映加载状态