Android 15, edge to edge, CoordinatorLayout & tabs -> Toolbar above status bar

1 week ago 8
ARTICLE AD BOX

In my app I have an activity showing a CoordinatorLayout having ViewPager2 showing 3 tabs, each having a RecyclerView. When scrolling up, the top MaterialToolBar goes under the (opaque) status bar, and my tab titles stops moving when their top touches the status bar bottom.

Updated the app to target Android 16, and now the AppBarLayout is displayed above the status bar.

Here's a sample code:

Activity:

class MyAdapter(private val items: List<String>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() { class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val textView: TextView = view.findViewById(android.R.id.text1) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { // Using a simple built-in Android layout for the demo val view = LayoutInflater.from(parent.context) .inflate(android.R.layout.simple_list_item_1, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.textView.text = items[position] // Setting a minimum height to ensure the list is long enough to scroll holder.itemView.minimumHeight = 200 } override fun getItemCount() = items.size } class TabListFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val recyclerView = inflater.inflate(R.layout.activity_test_fragment, container, false) as RecyclerView // Setup Dummy Data recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.adapter = MyAdapter((1..50).map { "Item $it" }) // 5. Handle Bottom Insets for the List // Ensures the last item in the list is fully visible above the nav bar ViewCompat.setOnApplyWindowInsetsListener(recyclerView) { v, insets -> val navBars = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) v.updatePadding(bottom = navBars.bottom + 100) // 100px extra for FAB clearance insets } return recyclerView } } class TestActivity : AppCompatActivity() { private lateinit var ui: ActivityTestBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) ui = ActivityTestBinding.inflate(layoutInflater) setContentView(ui.root) // Set action bar: setSupportActionBar(ui.toolBar) supportActionBar?.setDisplayHomeAsUpEnabled(true) // Setup ViewPager and Tabs ui.viewPager.adapter = object : FragmentStateAdapter(this) { override fun getItemCount(): Int = 3 override fun createFragment(position: Int) = TabListFragment() } TabLayoutMediator(ui.tabLayout, ui.viewPager) { tab, position -> tab.text = "TAB ${position + 1}" }.attach() } }

Activity XML:

<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?android:attr/windowBackground"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" app:liftOnScroll="true" app:liftOnScrollTargetViewId="@id/recyclerView" app:elevation="6dp"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.MaterialComponents.Light" app:elevation="0dp" app:layout_scrollFlags="scroll|enterAlways|snap" /> <com.google.android.material.tabs.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" style="@style/AppTheme.TabLayout.Colored" /> </com.google.android.material.appbar.AppBarLayout> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/validateButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:layout_gravity="bottom|end" app:srcCompat="@drawable/ic_action_accept_light" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>

Tab XML:

<?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:paddingBottom="16dp" />

And here's the result when scrolled:

screenshot

How to get liftOnScroll to work so that AppBarLayout go under the status bar as before?

Read Entire Article