I managed to get the desired result using ListPopupWindow and code is below.
NOTE
Using DataBinding, Navigation Component and Material Toolbar
Step 1
Create a custom layout for a single item which will appear in ListPopupWindow.
item_overflow.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.merisehat.clinicapp.data.models.app.OverflowMenu" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/_5sdp">
<ImageView
android:id="@+id/iv_menuIcon"
android:layout_width="@dimen/_12sdp"
android:layout_height="@dimen/_12sdp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:setImage="@{item.icon}"
tools:src="@drawable/ic_profile" />
<TextView
android:id="@+id/tv_menuName"
style="@style/Theme.TextView.CircularStdLight.Small"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/_5sdp"
android:text="@{item.name}"
app:layout_constraintBottom_toBottomOf="@id/iv_menuIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_menuIcon"
app:layout_constraintTop_toTopOf="@id/iv_menuIcon"
tools:text="Menu Item" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Step 2
Create a custom adapter for a single item which will appear in ListPopupWindow.
OverflowMenuAdapter.kt
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import com.merisehat.clinicapp.data.models.app.OverflowMenu
import com.merisehat.clinicapp.databinding.ItemOverflowBinding
class OverflowMenuAdapter(private val context: Context, private val menuList: List<OverflowMenu>):
ArrayAdapter<OverflowMenu>(context, 0, menuList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val binding: ItemOverflowBinding
val view: View
if (convertView == null) {
binding = ItemOverflowBinding.inflate(LayoutInflater.from(context), parent, false)
view = binding.root
view.tag = binding
} else {
binding = convertView.tag as ItemOverflowBinding
view = convertView
}
binding.item = menuList[position]
binding.executePendingBindings()
return view
}
}
Step 3
Create two drawables files. One for the ListPopupWindow which will customise its background like its radius, color, padding and etc. Second for the background of an item of ListPopupWindow when it is selected/clicked like its radius, color and etc.
background_overflow.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<padding android:top="@dimen/_5sdp" android:right="@dimen/_5sdp" android:bottom="@dimen/_5sdp" android:left="@dimen/_5sdp"/>
<solid android:color="@color/white" />
<corners android:radius="@dimen/overall_corners" />
</shape>
background_selected_overflow.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/green" />
<corners android:radius="@dimen/overall_corners" />
</shape>
Step 4
Create ListPopupWindow instance and show when overflow icon is clicked on Toolbar
MainActivity.kt
private fun showOverflowMenu(anchorView: View) {
val listPopupWindow = ListPopupWindow(this)
listPopupWindow.apply {
setBackgroundDrawable(ContextCompat.getDrawable(this@MainActivity, R.drawable.background_overflow))
setListSelector(ContextCompat.getDrawable(this@MainActivity, R.drawable.background_selected_overflow))
setAdapter(OverflowMenuAdapter(this@MainActivity, getOverflowMenuList()))
this.anchorView = anchorView
width = 220
height = ViewGroup.LayoutParams.WRAP_CONTENT
horizontalOffset = -150
verticalOffset = -10
setOnItemClickListener { _, view, position, _ ->
(view?.findViewById<TextView>(R.id.tv_menuName))?.setTextColor(
ContextCompat.getColor(this@MainActivity, R.color.white)
)
(view?.findViewById<ImageView>(R.id.iv_menuIcon))?.imageTintList = ColorStateList.valueOf(
ContextCompat.getColor(this@MainActivity, R.color.white)
)
when (position) {
0 -> navController.navigate(R.id.action_to_profileFragment)
1 -> navController.navigate(R.id.action_to_historyFragment)
2 -> navController.navigate(R.id.action_to_logoutDialog)
}
lifecycleScope.launch {
delay(200)
dismiss()
}
}
show()
}
}
private fun getOverflowMenuList(): List<OverflowMenu> =
listOf(
OverflowMenu(R.drawable.ic_profile, getString(R.string.title_profile)),
OverflowMenu(R.drawable.ic_history, getString(R.string.title_history)),
OverflowMenu(R.drawable.ic_logout, getString(R.string.title_logout))
)
private val homeMenuProviderCallback = object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.toolbar_app_home, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
R.id.searchFragment -> navController.navigate(R.id.action_to_searchFragment)
R.id.action_overflow -> showOverflowMenu(findViewById(menuItem.itemId))
else -> return false
}
return true
}
}
I hope it helps someone out.