Concepts
/
Building Search UI
/
Algolia Places
Aug. 20, 2019
Algolia Places
On this page
Using Places on Android
You can use Algolia Places on Android by creating your own SearcherPlaces
.
This is the recommended implementation, implementing the Searcher
interface and leveraging our ClientPlaces
:
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class SearcherPlaces(
val client: ClientPlaces = ClientPlaces(),
val language: Language = Language.English,
val query: PlacesQuery = PlacesQuery(),
val requestOptions: RequestOptions? = null,
override val coroutineScope: CoroutineScope = SearcherScope(),
override val dispatcher: CoroutineDispatcher = Dispatchers.Main
) : Searcher<ResponseSearchPlacesMono> {
private val sequencer = Sequencer()
override val isLoading = SubscriptionValue(false)
override val error = SubscriptionValue<Throwable?>(null)
override val response = SubscriptionValue<ResponseSearchPlacesMono?>(null)
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
error.value = throwable
}
override fun setQuery(text: String?) {
query.query = text
}
override suspend fun search(): ResponseSearchPlacesMono {
withContext(dispatcher) { isLoading.value = true }
val response = client.searchPlaces(language, query, requestOptions)
withContext(dispatcher) {
isLoading.value = false
this@SearcherPlaces.response.value = response
}
return response
}
override fun searchAsync(): Job {
return coroutineScope.launch(dispatcher + exceptionHandler) {
withContext(Dispatchers.Default) { search() }
}.also {
sequencer.addOperation(it)
}
}
override fun cancel() {
sequencer.cancelAll()
}
}
Now we have a Searcher
to find Places, we need to show search results.
Create a RecyclerView.Adapter
and ViewHolder
to display the results in a list:
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class PlacesViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(place: PlaceLanguage) {
println(place.highlightResultOrNull)
val name = place.highlightResultOrNull
?.toHighlights("locale_names")
?.firstOrNull()
?.tokenize() ?: place.localNames.first()
val county = place.highlightResultOrNull
?.toHighlights("county")
?.firstOrNull()
?.tokenize() ?: place.county.first()
val postCode = place.postCodeOrNull?.firstOrNull()?.let { ", $it" } ?: ""
view.placeName.text = TextUtils.concat(name, ", ", county, postCode)
}
fun HighlightResult.tokenize(): SpannedString {
return HighlightTokenizer()(value).toSpannedString()
}
}
class PlacesAdapter : ListAdapter<PlaceLanguage, PlacesViewHolder>(PlacesAdapter), HitsView<PlaceLanguage> {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlacesViewHolder {
return PlacesViewHolder(parent.inflate(R.layout.place_item))
}
override fun onBindViewHolder(holder: PlacesViewHolder, position: Int) {
val item = getItem(position)
if (item != null) holder.bind(item)
}
override fun setHits(hits: List<PlaceLanguage>) {
submitList(hits)
}
companion object : DiffUtil.ItemCallback<PlaceLanguage>() {
override fun areItemsTheSame(oldItem: PlaceLanguage, newItem: PlaceLanguage): Boolean {
return oldItem::class == newItem::class
}
override fun areContentsTheSame(oldItem: PlaceLanguage, newItem: PlaceLanguage): Boolean {
return oldItem == newItem
}
}
}
Finally, create your activity with a SearchView
and a RecyclerView
to provide a search autocomplete experience:
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class PlacesActivity : AppCompatActivity() {
val query = PlacesQuery(
type = PlaceType.City,
hitsPerPage = 10,
aroundLatLngViaIP = false,
countries = listOf(Country.France)
)
val searcher = SearcherPlaces(query = query, language = Language.English)
val searchBox = SearchBoxConnector(searcher)
val adapter = PlacesAdapter()
val connection = ConnectionHandler(searchBox)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.places_activity)
connection += searchBox.connectView(SearchBoxViewAppCompat(searchView))
connection += searcher.connectHitsView(adapter) { hits -> hits.hits }
placesList.let {
it.itemAnimator = null
it.adapter = adapter
it.layoutManager = LinearLayoutManager(this)
it.autoScrollToStart(adapter)
}
searcher.searchAsync()
}
override fun onDestroy() {
super.onDestroy()
connection.disconnect()
}
}