As @Megaetron pointed out in his answer, even though reflexion seems to be the way, @shlee1's answer doesn't work right away and does nothing, even if no exception is thrown.
But instead of defining a custom Spinner, using an AppCompatSpinner when setting the height fixed it for me. So I ended up setting the dropdown height like so:
val spinner = findViewById<Spinner>(R.id.spinner)
try {
// Here use AppCompatSpinner instead of Spinner
val popup = AppCompatSpinner::class.java.getDeclaredField("mPopup")
popup.isAccessible = true
val popupWindow = popup.get(spinner) as ListPopupWindow
// Set popupWindow height to 500px
popupWindow.height = 500
} catch (e: Exception) {
e.printStackTrace()
}
Now, while this effectively sets the dropdown height to 500px, OP wanted to display only n items, which we are not currently doing by setting a raw height value.
One way to display exactly n items is to measure one item's height, and set the dropdown height so that it equals n * one_item_height.
To do that I created an extension method to calculate the height of one dropdown item. The measuredHeight value doesn't take paddings and margins into account, so those must be added to the measured height:
fun Spinner.measureDropdownItemHeight(
layoutInflater: LayoutInflater,
@LayoutRes layoutRes: Int
): Int {
val itemView = layoutInflater.inflate(layoutRes, this, false)
itemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
val itemHeight = itemView.measuredHeight
.plus(paddingTop).plus(paddingBottom)
.plus(marginTop).plus(marginBottom)
return itemHeight
}
With all this, you can now set your dropdown height to the value you juste measured with this extension method:
fun Spinner.setMaxVisibleItems(
layoutInflater: LayoutInflater,
@LayoutRes layoutRes: Int,
itemCount: Int
) {
val itemHeight = measureDropdownItemHeight(layoutInflater, layoutRes)
val maxHeight = itemHeight * itemCount
try {
val popup = AppCompatSpinner::class.java.getDeclaredField("mPopup")
popup.isAccessible = true
val popupWindow = popup.get(this) as ListPopupWindow
popupWindow.height = maxHeight
} catch (e: Exception) {
e.printStackTrace()
}
}
Which you then call like:
val spinner = findViewById<Spinner>(R.id.spinner)
val itemLayout = android.R.layout.simple_spinner_dropdown_item // Or any custom layout
val maxVisibleItemCount = 5 // Replace to display your desired number of items
spinner.setMaxVisibleItems(layoutInflater, itemLayout, maxVisibleItemCount)
Note that if you call setMaxVisibleItems with the actual item count being larger than your provided maxVisibleItemCount, your dropdown will have some blank space at the bottom. So it's better to check that it's not the case without calling it:
if (actualItemCount > maxVisibleItemCount)
spinner.setMaxVisibleItems(layoutInflater, itemLayout, maxVisibleItemCount)