UI libraries / InstantSearch Android / Widgets
Signature
HierarchicalConnector(
    searcher: HitsSearcher,
    attribute: Attribute,
    filterState: FilterState,
    hierarchicalAttributes: List<Attribute>,
    separator: String
)

About this widget

Hierarchical Menu is a filtering view that displays a hierarchy of facets that lets users refine the search results.

Examples

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
class MyActivity : AppCompatActivity() {
    val searcher = HitsSearcher(
        applicationID = ApplicationID("YourApplicationID"),
        apiKey = APIKey("YourSearchOnlyAPIKey"),
        indexName = IndexName("YourIndexName")
    )
    val hierarchicalCategory = Attribute("categories")
    val hierarchicalCategoryLvl0 = Attribute("$categories.lvl0")
    val hierarchicalCategoryLvl1 = Attribute("$categories.lvl1")
    val hierarchicalCategoryLvl2 = Attribute("$categories.lvl2")
    val hierarchicalAttributes = listOf(
        hierarchicalCategoryLvl0,
        hierarchicalCategoryLvl1,
        hierarchicalCategoryLvl2
    )
    val filterState = FilterState()
    val separator = " > "
    val hierarchical = HierarchicalConnector(
        searcher = searcher,
        attribute = hierarchicalCategory,
        filterState = filterState,
        hierarchicalAttributes = hierarchicalAttributes,
        separator = separator
    )
    val connection = ConnectionHandler(hierarchical, searcher.connectFilterState(filterState))
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view : HierarchicalView = HierarchicalAdapter() // your HierarchicalView implementation
        connection += hierarchical.connectView(view, HierarchicalPresenterImpl(separator))
        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        connection.disconnect()
        searcher.cancel()
    }
}

Low-level API

If you want to fully control the Hierarchical Menu components and connect them manually, use the following components:

  • Searcher: The Searcher that handles your searches.
  • FilterState: The current state of the filters.
  • HierarchicalViewModel: The logic applied to the hierarchical facets.
  • HierarchicalView: The concrete hierarchical view.
  • HierarchicalPresenter: The presenter that controls the sorting and other settings of the hierarchical menu.
  • HierarchicalItem: A single node in the hierarchy.
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
class MyActivity : AppCompatActivity() {
    val searcher = HitsSearcher(
        applicationID = ApplicationID("YourApplicationID"),
        apiKey = APIKey("YourSearchOnlyAPIKey"),
        indexName = IndexName("YourIndexName")
    )
    val filterState = FilterState()

    val hierarchicalCategory = Attribute("categories")
    val hierarchicalCategoryLvl0 = Attribute("$categories.lvl0")
    val hierarchicalCategoryLvl1 = Attribute("$categories.lvl1")
    val hierarchicalCategoryLvl2 = Attribute("$categories.lvl2")
    val hierarchicalAttributes = listOf(
        hierarchicalCategoryLvl0,
        hierarchicalCategoryLvl1,
        hierarchicalCategoryLvl2
    )
    val separator = " > "
    val viewModel = HierarchicalViewModel(hierarchicalCategory, hierarchicalAttributes, separator)
    val connection = ConnectionHandler()

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

        val view: HierarchicalView = HierarchicalAdapter() // your `HierarchicalView` implementation

        connection += searcher.connectFilterState(filterState)
        connection += viewModel.connectFilterState(filterState)
        connection += viewModel.connectSearcher(searcher)
        connection += viewModel.connectView(view, HierarchicalPresenterImpl(separator))

        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        searcher.cancel()
        connection.disconnect()
    }
}

Compose UI

InstantSearch provides HierarchicalState as a state model, which implements the HierarchicalView interface. You need to connect HierarchicalState to the HierarchicalConnector or HierarchicalViewModel like any other HierarchicalView implementation.

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
50
51
52
53
class MyActivity : AppCompatActivity() {
    val searcher = HitsSearcher(
        applicationID = ApplicationID("YourApplicationID"),
        apiKey = APIKey("YourSearchOnlyAPIKey"),
        indexName = IndexName("YourIndexName")
    )
    val hierarchicalCategory = Attribute("categories")
    val hierarchicalCategoryLvl0 = Attribute("$categories.lvl0")
    val hierarchicalCategoryLvl1 = Attribute("$categories.lvl1")
    val hierarchicalCategoryLvl2 = Attribute("$categories.lvl2")
    val hierarchicalAttributes = listOf(
        hierarchicalCategoryLvl0,
        hierarchicalCategoryLvl1,
        hierarchicalCategoryLvl2
    )
    val filterState = FilterState()
    val separator = " > "
    val hierarchicalState = HierarchicalState()
    val hierarchical = HierarchicalConnector(
        searcher = searcher,
        attribute = hierarchicalCategory,
        filterState = filterState,
        hierarchicalAttributes = hierarchicalAttributes,
        separator = separator
    )
    val connections = ConnectionHandler(hierarchical)

    init {
        connections += searcher.connectFilterState(filterState)
        connections += hierarchical.connectView(hierarchicalState, HierarchicalPresenterImpl(separator))
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LazyColumn {
                items(hierarchicalState.hierarchicalItems) { item ->
                    HierarchicalItem( // your own UI composable to display `HierarchicalItem`
                        item = item,
                        onClick = { hierarchicalState.onSelectionChanged?.invoke(item.facet.value) }
                    )
                }
            }
        }
        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        connections.disconnect()
        searcher.cancel()
    }
}

Parameters

Parameter Description
searcher
type: HitsSearcher
Required

The Searcher that handles your searches.

attribute
type: Attribute
Required

The attribute to filter.

filterState
type: FilterState
Required

The FilterState that holds your filters.

hierarchicalAttributes
type: List<Attribute>
Required

The names of the hierarchical attributes that you need to target, in ascending order.

separator
type: String
Required

The string separating the facets in the hierarchical facets. Usually something like “ > “. Note that you should not forget the spaces in between if there are some in your separator.

View

Parameter Description
view
type: HierarchicalView
Required

The view that renders the hierarchical menu.

presenter
type: HierarchicalPresenter
Required

How to display the list of HierarchicalItem. Takes a list of HierarchicalItem and returns a new list of HierarchicalItem. A HierarchicalItem contains a Facet, its level, and a displayName.

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
val view: HierarchicalView = HierarchicalAdapter()
val separator = " > "
val presenter = HierarchicalPresenterImpl(separator)
hierarchical.connectView(view, presenter)

// Example of `HierarchicalView` implementation
class HierarchicalAdapter : HierarchicalView,
    ListAdapter<HierarchicalItem, HierarchicalViewHolder>(DiffUtilItem()) {
    override var onSelectionChanged: Callback<String>? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HierarchicalViewHolder {
        return HierarchicalViewHolder(parent.inflate(R.layout.list_item_selectable, false))
    }

    override fun onBindViewHolder(holder: HierarchicalViewHolder, position: Int) {
        val item = getItem(position)
        holder.bind(item) { onSelectionChanged?.invoke(item.facet.value) }
    }

    override fun setTree(tree: List<HierarchicalItem>) {
        submitList(tree)
    }
}

class DiffUtilItem<T : Filter> : DiffUtil.ItemCallback<SelectableItem<T>>() {
    override fun areItemsTheSame(oldItem: SelectableItem<T>, newItem: SelectableItem<T>): Boolean {
        return oldItem.first == newItem.first
    }

    override fun areContentsTheSame(oldItem: SelectableItem<T>, newItem: SelectableItem<T>): Boolean {
        return oldItem == newItem
    }
}
Did you find this page helpful?