Skip to content

Instantly share code, notes, and snippets.

/**
* Hides the system bars and makes the Activity "fullscreen". If this should be the default
* state it should be called from [Activity.onWindowFocusChanged] if hasFocus is true.
* It is also recommended to take care of cutout areas. The default behavior is that the app shows
* in the cutout area in portrait mode if not in fullscreen mode. This can cause "jumping" if the
* user swipes a system bar to show it. It is recommended to set [WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER],
* call [showBelowCutout] from [Activity.onCreate]
* (see [Android Developers article about cutouts](https://developer.android.com/guide/topics/display-cutout#never_render_content_in_the_display_cutout_area)).
* @see showSystemUI
* @see addSystemUIVisibilityListener
/**
* Registers a listener which is informed when UI is shown (true) or hidden (false).
*/
fun Window.addSystemUIVisibilityListener(visibilityListener: (Boolean) -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
decorView.setOnApplyWindowInsetsListener { v, insets ->
val suppliedInsets = v.onApplyWindowInsets(insets)
// only check for statusBars() and navigationBars(), because captionBar() is not always
// available and isVisible() could return false, although showSystemUI() had been called:
visibilityListener(suppliedInsets.isVisible(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()))
/**
* Shows the system bars and returns back from fullscreen.
* @see hideSystemUI
* @see addSystemUIVisibilityListener
*/
fun Activity.showSystemUI() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// show app content in fullscreen, i. e. behind the bars when they are shown (alternative to
// deprecated View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION and View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
window.setDecorFitsSystemWindows(false)
/**
* Should be called from [Activity.onCreate].
* @see hideSystemUI
* @see showSystemUI
*/
fun Activity.showBelowCutout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
}
}
@milhauscz
milhauscz / MaterialSpinner.kt
Last active April 21, 2021 13:48
Custom implementation of an Android spinner (combo box) with material theme and data binding support by extending AutocompleteTextView
/**
* Custom implementation of an Android spinner (combo box) with material theme by using AutocompleteTextView.
* Recommended to use together with [MaterialSpinnerAdapter].
*
* Supports listening to and updating the selected position e. g. from a LiveData object bound via
* the `selectedPosition` two-way binding adapter. The empty text value can be bound via 'emptyText'
* attr (custom styleable must be declared in attrs.xml).
*
* If [MaterialSpinnerAdapter] is used, its [MaterialSpinnerAdapter.selectedItemPosition] is
* automatically updated when selection changes.
@milhauscz
milhauscz / MaterialSpinnerAdapter.kt
Last active July 10, 2022 11:00
Custom adapter for MaterialSpinner
/**
* Custom adapter for [MaterialSpinner]. Just implement [getView] to provide the given View, e. g.
* by using [inflater]. In case of data binding, the binding object can be set as the given View's
* tag to be able to retrieve it when reusing the given View and implement custom viewholder pattern.
* [selectedItemPosition] can be used for updating the selected View's UI. When used together with
* [MaterialSpinner], [selectedItemPosition] is updated automatically and [notifyDataSetChanged] is called.
*
* Created by Milos Cernilovsky on 4/21/21
*/
abstract class MaterialSpinnerAdapter<T>(context: Context, private val objects: List<T>) : BaseAdapter(), Filterable {
@milhauscz
milhauscz / ExampleSpinnerArrayAdapter.kt
Created April 21, 2021 12:41
Example implementation of MaterialSpinnerAdapter
class ExampleSpinnerArrayAdapter(context: Context, objects: List<ExampleItem>) : MaterialSpinnerAdapter<ExampleItem>(context, objects) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
// implement viewholder pattern
val view: View
// automatically generated binding class
val binding: ExampleItemBinding
if (convertView == null) {
// view is new - we have to inflate the binding object and set it as a tag
binding = ExampleItemBinding.inflate(inflater, parent, false)
view = binding.root
@milhauscz
milhauscz / example_item.xml
Created April 21, 2021 12:45
Example of a material spinner item layout
<?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.example.ExampleItem" />
@milhauscz
milhauscz / ExampleItemBindingAdapter.kt
Last active April 21, 2021 15:14
Example of a custom binding adapter for Material Spinner
@BindingAdapter("items")
fun bindItems(view: MaterialSpinner, items: List<ExampleItem>?) {
if (items == null) return
view.setAdapter(ExampleSpinnerArrayAdapter(view.context, items))
}
@milhauscz
milhauscz / example_spinner.xml
Last active April 21, 2021 14:56
Example of an XML layout using MaterialSpinner
<?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">
<data>
<import type="com.google.android.material.textfield.TextInputLayout" />
<variable
name="viewModel"
type="com.example.SpinnerViewModel" />