You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Toolbar双折叠状态实现:仿状态栏滑动展开技术问询

Two-Stage Collapsible Toolbar for Android (Status Bar-like Interaction)

Got it, let's build this two-state Toolbar that mimics the system status bar's behavior—first expanding to a compact view with your essential buttons when you start scrolling down from the top, then fully opening up to show all content on further scrolling. We'll use CollapsingToolbarLayout paired with NestedScrollView for seamless scroll interactions.

1. Layout Setup

First, let's define the layout structure. We'll use CoordinatorLayout as the root (required for scroll-driven UI interactions), then nest an AppBarLayout and CollapsingToolbarLayout to handle the Toolbar's multi-stage expansion. We'll explicitly define three Toolbar states:

  • Collapsed: Minimal height matching the system status bar
  • Compact: Standard action bar height with your go-to buttons
  • Expanded: Full height with extra content/titles
<?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">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        app:elevation="0dp">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <!-- Full expanded content (hidden initially) -->
            <LinearLayout
                android:id="@+id/full_toolbar_content"
                android:layout_width="match_parent"
                android:layout_height="120dp"
                android:orientation="vertical"
                android:padding="16dp"
                android:visibility="gone">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Full Toolbar Title"
                    android:textSize="24sp"
                    android:textStyle="bold"/>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:gravity="end"
                    android:orientation="horizontal">

                    <Button
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="Extra Action 1"/>
                    <Button
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="8dp"
                        android:text="Extra Action 2"/>
                </LinearLayout>
            </LinearLayout>

            <!-- Compact toolbar view (shown after first scroll) -->
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/compact_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@color/primary"
                app:layout_collapseMode="pin">

                <!-- Add your essential action buttons here -->
                <ImageView
                    android:id="@+id/btn_home"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@drawable/ic_home"/>
                <ImageView
                    android:id="@+id/btn_search"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="16dp"
                    android:src="@drawable/ic_search"/>
            </androidx.appcompat.widget.Toolbar>

        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <!-- Scrollable main content -->
    <androidx.core.widget.NestedScrollView
        android:id="@+id/scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Scroll down to expand the toolbar..."
                android:textSize="18sp"/>

            <!-- Add long dummy content to enable scrolling -->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="@string/lorem_ipsum_long"/>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

2. Scroll Logic Implementation

Next, we'll listen to the NestedScrollView's scroll events to handle transitions between the three states. We'll calculate key heights upfront, then update the Toolbar's height and visibility based on scroll direction and distance.

import android.os.Bundle
import android.view.View
import android.view.ViewTreeObserver
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.NestedScrollView
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout

class TwoStageToolbarActivity : AppCompatActivity() {

    private lateinit var appBarLayout: AppBarLayout
    private lateinit var collapsingToolbar: CollapsingToolbarLayout
    private lateinit var compactToolbar: androidx.appcompat.widget.Toolbar
    private lateinit var fullToolbarContent: View
    private lateinit var scrollView: NestedScrollView

    private var statusBarHeight = 0
    private var compactHeight = 0
    private var expandedHeight = 0

    // Track current state: 0 = collapsed, 1 = compact, 2 = expanded
    private var currentState = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_two_stage_toolbar)

        initViews()
        calculateHeights()
        setupScrollListener()
        // Start with fully collapsed state (only status bar visible)
        setToolbarHeight(statusBarHeight)
        compactToolbar.visibility = View.GONE
    }

    private fun initViews() {
        appBarLayout = findViewById(R.id.app_bar_layout)
        collapsingToolbar = findViewById(R.id.collapsing_toolbar)
        compactToolbar = findViewById(R.id.compact_toolbar)
        fullToolbarContent = findViewById(R.id.full_toolbar_content)
        scrollView = findViewById(R.id.scroll_view)
    }

    private fun calculateHeights() {
        // Get system status bar height
        val statusBarResId = resources.getIdentifier("status_bar_height", "dimen", "android")
        if (statusBarResId > 0) {
            statusBarHeight = resources.getDimensionPixelSize(statusBarResId)
        }

        // Get compact toolbar height (standard action bar size)
        compactHeight = resources.getDimensionPixelSize(R.dimen.abc_action_bar_default_height_material)

        // Get expanded toolbar height after the view is laid out
        fullToolbarContent.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                expandedHeight = fullToolbarContent.height
                fullToolbarContent.viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        })
    }

    private fun setupScrollListener() {
        scrollView.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
            val isScrollingDown = scrollY > oldScrollY // Downward scroll = content moves up, scrollY increases

            when (currentState) {
                0 -> { // Collapsed → Compact
                    if (isScrollingDown && scrollY > 50) { // Trigger after small downward scroll
                        setToolbarHeight(compactHeight)
                        compactToolbar.visibility = View.VISIBLE
                        currentState = 1
                    }
                }
                1 -> { // Compact → Expanded or back to Collapsed
                    if (isScrollingDown && scrollY > 200) { // Further scroll to expand fully
                        setToolbarHeight(expandedHeight)
                        fullToolbarContent.visibility = View.VISIBLE
                        currentState = 2
                    } else if (!isScrollingDown && scrollY <= 50) { // Scroll up to collapse
                        setToolbarHeight(statusBarHeight)
                        compactToolbar.visibility = View.GONE
                        currentState = 0
                    }
                }
                2 -> { // Expanded → Compact
                    if (!isScrollingDown && scrollY <= 200) { // Scroll up to return to compact view
                        setToolbarHeight(compactHeight)
                        fullToolbarContent.visibility = View.GONE
                        currentState = 1
                    }
                }
            }
        })
    }

    private fun setToolbarHeight(height: Int) {
        val params = collapsingToolbar.layoutParams as AppBarLayout.LayoutParams
        params.height = height
        collapsingToolbar.layoutParams = params
    }
}

3. Polishing & Tweaks

  • Smooth Animations: Replace the direct setToolbarHeight() calls with a ValueAnimator for smoother transitions:
    private fun animateToolbarHeight(fromHeight: Int, toHeight: Int) {
        val animator = android.animation.ValueAnimator.ofInt(fromHeight, toHeight)
        animator.duration = 300
        animator.addUpdateListener { animation ->
            val params = collapsingToolbar.layoutParams as AppBarLayout.LayoutParams
            params.height = animation.animatedValue as Int
            collapsingToolbar.layoutParams = params
        }
        animator.start()
    }
    
  • Adjust Scroll Thresholds: Tweak the scrollY values (50, 200) to make transitions feel natural for your content.
  • State Persistence: Save currentState in onSaveInstanceState() and restore it in onCreate() to retain state across screen rotations.
  • Short Content Handling: If your main content isn't long enough to scroll, add logic to disable transitions or force the compact/expanded state.

内容的提问来源于stack exchange,提问作者asad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:59:45