Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách lấy chiều cao ActionBar/Toolbar trên Android

Giới thiệu

Chiều cao ActionBar/Toolbar thường cần để tính layout, scroll offset, hoặc custom view. Giá trị chuẩn: ?attr/actionBarSize (thường 56dp trên phone, 64dp trên tablet/landscape). Bài viết hướng dẫn lấy giá trị pixel tại runtime và các best practices.

Các cách lấy chiều cao

1. Từ Theme Attribute (Khuyến nghị - Không cần runtime)

Dùng trong XML layout:

<View
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/primary" />


<dimen name="toolbar_height">?attr/actionBarSize</dimen>

2. Từ Toolbar View (Runtime - đã layout)


// Java
Toolbar toolbar = findViewById(R.id.toolbar);
int height = toolbar.getHeight(); // Chỉ > 0 sau onLayout

// Kotlin
val height = binding.toolbar.height

3. Từ Theme/Resources (Runtime - trước khi layout)


// Java
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
    int actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
}

// Kotlin
val tv = TypedValue()
val actionBarHeight = if (theme.resolveAttribute(R.attr.actionBarSize, tv, true)) {
    TypedValue.complexToDimensionPixelSize(tv.data, resources.displayMetrics)
} else 0

4. Từ WindowInsets (Android 11+ / API 30+)


// Lấy system bar insets (status bar + navigation bar + ime)
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets ->
    val statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
    val navigationBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
    // Toolbar height thường = actionBarSize, không lấy từ insets
    insets
}

Ví dụ hoàn chỉnh

Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:title="My Toolbar"
            app:titleTextColor="@android:color/white" />
    </com.google.android.material.appbar.AppBarLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="24dp"
        android:gravity="center"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <Button
            android:id="@+id/btnGetHeight"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Lấy chiều cao Toolbar" />

        <TextView
            android:id="@+id/tvResult"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:textSize="18sp"
            android:textStyle="bold" />
    </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

MainActivity.kt

package com.example.toolbarheight

import android.os.Bundle
import android.util.TypedValue
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.toolbarheight.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setSupportActionBar(binding.toolbar)
        supportActionBar?.setDisplayShowTitleEnabled(true)

        binding.btnGetHeight.setOnClickListener { showToolbarHeight() }
    }

    private fun showToolbarHeight() {
        // Cách 1: Từ Toolbar view (cần layout đã xong)
        val toolbarHeight = binding.toolbar.height
        
        // Cách 2: Từ theme attribute (luôn đúng, không cần layout)
        val tv = TypedValue()
        val themeHeight = if (theme.resolveAttribute(R.attr.actionBarSize, tv, true)) {
            TypedValue.complexToDimensionPixelSize(tv.data, resources.displayMetrics)
        } else 0

        // Cách 3: Từ dimens resource (nếu định nghĩa)
        val dimenHeight = resources.getDimensionPixelSize(R.dimen.toolbar_height) // Nếu có dimen

        binding.tvResult.text = """
            Toolbar.getHeight(): $toolbarHeight px
            Theme actionBarSize: $themeHeight px
            Dimen resource: $dimenHeight px
        """.trimIndent()
    }
}

ActionBar vs Toolbar

Đặc điểmActionBar (Hệ thống)Toolbar (Widget)
Vị tríFixed top (window decor)Bất kỳ đâu trong layout
Tùy biếnHạn chếHoàn toàn (View bình thường)
Lấy heightKhó (phải query window)Dễ (toolbar.height)
Animation/CollapseKhôngCó (CollapsingToolbarLayout)
Khuyến nghịKhông dùng nữaDùng Toolbar + NoActionBar theme

Status Bar & Navigation Bar Height

Thường cần khi làm full-screen, edge-to-edge:


// Status bar height
val statusBarHeight = resources.getDimensionPixelSize(
    resources.getIdentifier("status_bar_height", "dimen", "android")
)

// Navigation bar height (API 30+)
val navBarHeight = WindowInsetsCompat.getInsets(rootView, WindowInsetsCompat.Type.navigationBars()).bottom

// Hoặc dùng InsetsListener (API 30+)
ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets ->
    val statusBar = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
    val navBar = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
    // Cập nhật padding/margin cho view
    insets
}

Edge-to-Edge (Android 15+ / API 35)

Từ Android 15, app mặc định edge-to-edge. Toolbar cần handle insets:


// Theme


// Layout - Toolbar với top padding = status bar height

Lưu ý quan trọng

  • toolbar.getHeight() trả về 0 nếu gọi trong onCreate() (chưa layout). Dùng doOnLayout { } hoặc post { }.
  • ?attr/actionBarSize là giá trị chuẩn từ theme - luôn đúng, không cần runtime.
  • Trên tablet/landscape: actionBarSize có thể = 64dp.
  • Custom Toolbar height: Override trong theme 72dp.
  • Khi dùng CollapsingToolbarLayout, height thay đổi khi scroll - lắng nghe OnOffsetChangedListener.