Skip to content

Module Search

Tomas Valenta edited this page Nov 5, 2019 · 17 revisions

Specialized module for simple implementation of Search functionality including SearchToolbar and SearchResultList component. Every feature is customizable by end developer (can be turned on/off or styled).

Before you start, please use README to learn how to integrate this module into your project.

Search - Default

The most basic example. It shows the Search module with zero lines of configuration.

Preview

Code

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/searchFragment"
        class="com.sygic.maps.module.search.SearchFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>

or runtime:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    ...

    val searchFragment = SearchFragment()
    searchFragment.maxResultsCount = 8
    searchFragment.searchLocation = GeoCoordinates(48.145900, 17.126851)
    supportFragmentManager.beginTransaction().replace(R.id.container, searchFragment).commit()
}

Sample


Search - From Browse Map

It shows the Search module integrated into the Browse Map module. Note: setting up the SearchModuleConnectionProvider will automatically display the SearchFab button into the Browse Map fragment.

Preview

Code

Xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragmentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Activity

class SearchFromBrowseMapActivity : CommonSampleActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_search_from_browse_map)

        val viewModel = ViewModelProviders.of(this).get(SearchFromBrowseMapActivityViewModel::class.java).apply {
            showToastObservable.observe(this@SearchFromBrowseMapActivity, Observer { longToast(it) })
        }

        if (savedInstanceState == null) {
            setFragmentModuleConnection(
                placeBrowseMapFragment().apply {
                    cameraDataModel.zoomLevel = 11F
                    cameraDataModel.position = GeoCoordinates(48.145764, 17.126015)
                }, viewModel
            )
        } else {
            setFragmentModuleConnection(
                supportFragmentManager.findFragmentByTag(BROWSE_MAP_FRAGMENT_TAG) as BrowseMapFragment, viewModel
            )
        }
    }

    // Note: You can also create this Fragment just like in other examples directly in an XML layout file, but
    // performance or other issues may occur (https://stackoverflow.com/a/14810676/3796931).
    private fun placeBrowseMapFragment() =
        BrowseMapFragment().also {
            supportFragmentManager
                ?.beginTransaction()
                ?.replace(R.id.fragmentContainer, it, BROWSE_MAP_FRAGMENT_TAG)
                ?.commit()
        }

    private fun setFragmentModuleConnection(
        fragment: BrowseMapFragment,
        moduleConnectionProvider: ModuleConnectionProvider
    ) = fragment.setSearchConnectionProvider(moduleConnectionProvider)
}

ViewModel

class SearchFromBrowseMapActivityViewModel : ViewModel(), ModuleConnectionProvider {

    val showToastObservable: LiveData<String> = SingleLiveEvent()

    override val fragment: Fragment
        get() {
            val searchFragment = SearchFragment()
            searchFragment.setResultCallback { showToastObservable.asSingleEvent().value = it.joinToString() }
            return searchFragment
        }
}

Sample


Search - From Browse Map with Pins

It shows the Search module integrated into the Browse Map module with pins added after a search result is confirmed. Note: setting up the SearchModuleConnectionProvider will automatically display the SearchFab button into the Browse Map fragment.

Preview

Code

Xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragmentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Activity

class SearchFromBrowseMapWithPinsActivity : CommonSampleActivity() {

    private lateinit var viewModel: SearchFromBrowseMapWitchPinsActivityViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_search_from_browse_map_pins)

        viewModel = ViewModelProviders.of(this)
            .get(SearchFromBrowseMapWitchPinsActivityViewModel::class.java)

        val browseMapFragment = if (savedInstanceState == null) {
            placeBrowseMapFragment().apply { cameraDataModel.zoomLevel = 2F }
        } else {
            supportFragmentManager.findFragmentByTag(BROWSE_MAP_FRAGMENT_TAG) as BrowseMapFragment
        }

        setFragmentModuleConnection(browseMapFragment, viewModel)
    }

    // Note: You can also create this Fragment just like in other examples directly in an XML layout file, but
    // performance or other issues may occur (https://stackoverflow.com/a/14810676/3796931).
    private fun placeBrowseMapFragment() =
        BrowseMapFragment().also {
            supportFragmentManager
                ?.beginTransaction()
                ?.replace(R.id.fragmentContainer, it, BROWSE_MAP_FRAGMENT_TAG)
                ?.runOnCommit {
                    viewModel.mapDataModel = it.mapDataModel
                    viewModel.cameraDataModel = it.cameraDataModel
                }
                ?.commit()
        }

    private fun setFragmentModuleConnection(
        fragment: BrowseMapFragment,
        moduleConnectionProvider: ModuleConnectionProvider
    ) = fragment.setSearchConnectionProvider(moduleConnectionProvider)
}

ViewModel

private const val CAMERA_RECTANGLE_MARGIN = 80

class SearchFromBrowseMapWitchPinsActivityViewModel : ViewModel(), ModuleConnectionProvider {

    var mapDataModel: SimpleMapDataModel? = null
    var cameraDataModel: SimpleCameraDataModel? = null

    private val callback: ((results: List<GeocodingResult>) -> Unit) = { results ->
        mapDataModel?.removeAllMapMarkers()

        if (results.isNotEmpty()) {
            results.toGeoCoordinatesList().let { geoCoordinatesList ->
                if (geoCoordinatesList.isNotEmpty()) {

                    if (geoCoordinatesList.size == 1) {
                        mapDataModel?.addMapMarker(geoCoordinatesList.first())
                        cameraDataModel?.position = geoCoordinatesList.first()
                        cameraDataModel?.zoomLevel = 10F
                    } else {
                        val geoBoundingBox = GeoBoundingBox(geoCoordinatesList.first(), geoCoordinatesList.first())
                        geoCoordinatesList.forEach { geoCoordinates ->
                            mapDataModel?.addMapMarker(geoCoordinates)
                            geoBoundingBox.union(geoCoordinates)
                        }
                        cameraDataModel?.setMapRectangle(geoBoundingBox, CAMERA_RECTANGLE_MARGIN)
                    }
                }
            }
        }
    }

    override val fragment: Fragment
        get() {
            val searchFragment = SearchFragment()
            searchFragment.searchLocation = cameraDataModel?.position ?: GeoCoordinates.Invalid
            searchFragment.setResultCallback(callback)
            return searchFragment
        }
}

Sample


Search - Pre-filled input

The same as the default example, but with a pre-filled search input field.

Preview

Code

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/searchFragment"
        class="com.sygic.maps.module.search.SearchFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:sygic_initial_search_input="London Eye" />

</FrameLayout>

or runtime:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    ...

    val searchFragment = SearchFragment()
    searchFragment.searchInput = "London Eye"
    supportFragmentManager.beginTransaction().replace(R.id.container, searchFragment).commit()
}

Sample