onMeasure(): apa itu, mode MeasureSpec dan penimpaan metode

Penulis: IT Sectr Diterbitkan: 2026-07-22 Waktu membaca: 9 mnt

onMeasure() — adalah metode protected dari kelas android.view.View, yang dipanggil oleh sistem Android untuk menentukan dimensi View. Sistem meneruskan dua objek MeasureSpec ke metode, masing-masing berisi mode pengukuran (EXACTLY, AT_MOST, atau UNSPECIFIED) dan ukuran yang diusulkan oleh kontainer induk. Menurut Android Developers Documentation (2026), menimpa onMeasure dengan penanganan MeasureSpec yang benar adalah langkah wajib untuk semua View dan ViewGroup kustom yang memerlukan kontrol presisi atas dimensi.

Poin Penting

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec) — metode View untuk mengukur dimensi dengan penerusan MeasureSpec dari induk
  • MeasureSpec — nilai 32-bit yang mengkodekan mode pengukuran (UNSPECIFIED, EXACTLY, AT_MOST) dan ukuran
  • setMeasuredDimension(int w, int h) — panggilan wajib di dalam onMeasure, yang menetapkan dimensi akhir View
  • Algoritma dua lintasan pengukuran: induk mengukur anak, kemudian anak melaporkan dimensinya, dan induk mengambil keputusan akhir
  • measureChildWithMargins — metode bantu untuk mengukur View anak di ViewGroup kustom

Apa itu onMeasure()?

onMeasure(int widthMeasureSpec, int heightMeasureSpec) — adalah metode kelas View yang dipanggil oleh sistem Android untuk menentukan lebar dan tinggi tampilan. Pengembang menimpa metode ini untuk menunjukkan ukuran yang seharusnya dimiliki View berdasarkan batasan yang diteruskan dalam MeasureSpec. Tanpa penimpaan onMeasure yang benar, View kustom mungkin ditampilkan secara tidak benar atau tidak ditampilkan sama sekali.

Sistem memanggil onMeasure dalam fase measure dari siklus hidup View, yang mendahului fase layout (onLayout) dan draw (onDraw). Jika View tidak menimpa onMeasure, implementasi dari superkelas digunakan, yang menetapkan ukuran default berdasarkan background drawable atau layout_params. Panggilan super.onMeasure(widthMeasureSpec, heightMeasureSpec) hanya berfungsi untuk subkelas View standar, seperti TextView atau ImageView.

Persyaratan utama untuk onMeasure — panggilan setMeasuredDimension(int, int) harus wajib ada di akhir metode. Jika panggilan ini tidak ada, sistem menghasilkan pengecualian IllegalStateException dengan pesan bahwa View tidak menetapkan dimensi yang diukur. Dimensi akhir tersedia melalui getter getMeasuredWidth() dan getMeasuredHeight() setelah fase measure selesai.

Mode MeasureSpec: tiga nilai kunci

MeasureSpec — adalah bilangan bulat 32-bit, di mana 2 bit atas mengkodekan mode pengukuran, dan 30 bit bawah — ukuran. Mode menentukan seberapa bebas View dalam memilih ukurannya. Android menyediakan tiga mode: EXACTLY, AT_MOST, dan UNSPECIFIED. Setiap mode menentukan logika pemrosesan yang berbeda dalam onMeasure.

Mode MeasureSpecNilaiPerilaku
EXACTLYInduk menentukan ukuran pastiView harus pas persis dengan ukuran yang diteruskan, jika tidak ingin melampaui batas
AT_MOSTInduk menetapkan ukuran maksimumView dapat memilih ukuran berapa pun dari 0 hingga maksimum yang diteruskan
UNSPECIFIEDInduk tidak memberikan batasanView dapat memilih ukuran yang diinginkan tanpa batasan atas

Untuk mengekstrak mode dan ukuran dari MeasureSpec, digunakan metode statis kelas MeasureSpec: MeasureSpec.getMode(int) mengembalikan salah satu dari tiga mode (EXACTLY, AT_MOST, UNSPECIFIED), dan MeasureSpec.getSize(int) mengembalikan ukuran numerik dalam piksel. Untuk membuat MeasureSpec sendiri, gunakan MeasureSpec.makeMeasureSpec(int size, int mode). Ketiga metode ini mencakup semua skenario bekerja dengan dimensi dalam onMeasure.

Logika pemrosesan MeasureSpec yang khas

Pola standar pemrosesan MeasureSpec: jika mode EXACTLY — gunakan ukuran yang diteruskan sebagai final; jika AT_MOST — pilih minimum dari ukuran yang diinginkan (konten View) dan maksimum yang diteruskan; jika UNSPECIFIED — gunakan ukuran View yang diinginkan tanpa batasan. Pola ini menjamin perilaku yang benar di bawah batasan induk apa pun.

kotlin
override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
    val desiredWidth = 200
    val desiredHeight = 100

    val widthMode = MeasureSpec.getMode(widthMeasureSpec)
    val widthSize = MeasureSpec.getSize(widthMeasureSpec)
    val heightMode = MeasureSpec.getMode(heightMeasureSpec)
    val heightSize = MeasureSpec.getSize(heightMeasureSpec)

    val width = when (widthMode) {
        MeasureSpec.EXACTLY -> widthSize
        MeasureSpec.AT_MOST -> minOf(desiredWidth, widthSize)
        else -> desiredWidth
    }
    val height = when (heightMode) {
        MeasureSpec.EXACTLY -> heightSize
        MeasureSpec.AT_MOST -> minOf(desiredHeight, heightSize)
        else -> desiredHeight
    }
    setMeasuredDimension(width, height)
}

Penggunaan resolveSize

Untuk menyederhanakan logika standar, Android menyediakan metode resolveSizeAndState, yang menerima ukuran yang diinginkan, MeasureSpec, dan mengembalikan ukuran akhir dengan mode yang benar. Metode ini mengimplementasikan pola yang dijelaskan di atas dalam satu baris kode. Juga tersedia fungsi resolveSize(int size, int measureSpec), yang mengembalikan ukuran murni tanpa bit status.

Algoritma pengukuran dua lintasan

Android menggunakan algoritma pengukuran dua lintasan (two-pass measure), yang menjamin bahwa setiap View dalam hierarki menerima dimensi yang benar dengan mempertimbangkan batasan induk dan preferensi elemen anak. Pada lintasan pertama, induk meneruskan MeasureSpec dengan batasan ke View anak, dan View anak menghitung dimensi yang mereka inginkan. Pada lintasan kedua, induk membuat keputusan akhir tentang dimensi.

Untuk ViewGroup, proses pengukuran lebih kompleks: induk harus terlebih dahulu mengukur semua elemen anaknya, kemudian menentukan ukurannya sendiri berdasarkan dimensi mereka. Panggilan measureChildren(int widthMeasureSpec, int heightMeasureSpec) mengiterasi semua View anak dan memanggil untuk masing-masing measure(child, childWidthSpec, childHeightSpec). Setelah mengukur semua elemen anak, ViewGroup memanggil setMeasuredDimension dengan dimensinya sendiri.

Nuansa penting: metode measure (publik, final) tidak dapat ditimpa — sebagai gantinya, onMeasure yang ditimpa. Ini menjamin bahwa sistem dapat melakukan tindakan layanan sebelum dan sesudah panggilan onMeasure, seperti memeriksa perubahan dimensi dan menghitung area kotor untuk penggambaran selanjutnya. Jika View memiliki ukuran tetap, penimpaan onMeasure mungkin tidak diperlukan.

Bendera MEASURED_SIZE_STATE

MeasureSpec tidak hanya mencakup ukuran dan mode, tetapi juga bit status (state bits), yang dapat diakses melalui MeasureSpec.getMode(). Setelah panggilan setMeasuredDimension, status menjadi bagian dari dimensi yang diukur dari View dan dapat diperiksa melalui getMeasuredState(). Ini digunakan di ScrollView dan kontainer yang dapat digulir lainnya untuk penerusan batasan yang benar ke anak.

Contoh penimpaan onMeasure di Kotlin

Mari kita lihat contoh praktis pembuatan View kustom dengan penimpaan onMeasure untuk tampilan persegi. Kelas SquareView mewarisi View dan menjamin bahwa lebar dan tinggi selalu sama, terlepas dari MeasureSpec yang diteruskan. Dalam onMeasure, sisi yang lebih kecil ditentukan dan ukuran persegi ditetapkan.

kotlin
class SquareView(context: Context)
    : View(context) {

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        val widthSize =
            MeasureSpec.getSize(widthMeasureSpec)
        val heightSize =
            MeasureSpec.getSize(heightMeasureSpec)
        val size = minOf(widthSize, heightSize)
        setMeasuredDimension(size, size)
    }
}

ViewGroup kustom dengan pengukuran elemen anak

ViewGroup memerlukan logika onMeasure yang lebih kompleks, karena pertama-tama elemen anak diukur, kemudian ukuran ViewGroup sendiri ditentukan. Contoh CascadeLayout mendistribusikan elemen anak secara kaskade dengan jarak. Setelah mengukur semua elemen anak melalui measureChildWithMargins, lebar dan tinggi total dihitung.

kotlin
class CascadeLayout(context: Context)
    : ViewGroup(context) {

    private val cascadeOffset = 40

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        var maxWidth = 0
        var totalHeight = 0
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            measureChildWithMargins(child,
                widthMeasureSpec,
                cascadeOffset * i,
                heightMeasureSpec, 0)
            maxWidth = maxOf(maxWidth,
                child.measuredWidth +
                cascadeOffset * i)
            totalHeight += child.measuredHeight
        }
        setMeasuredDimension(
            resolveSize(maxWidth, widthMeasureSpec),
            resolveSize(totalHeight, heightMeasureSpec))
    }

    override fun generateLayoutParams(attrs: AttributeSet?)
        : LayoutParams = MarginLayoutParams(context, attrs)

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var top = t
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val left = l + cascadeOffset * i
            child.layout(left, top,
                left + child.measuredWidth,
                top + child.measuredHeight)
            top += child.measuredHeight
        }
    }
}

Kesalahan umum saat menimpa onMeasure

Melewatkan panggilan setMeasuredDimension — kesalahan paling umum. Jika pengembang menimpa onMeasure tetapi tidak memanggil setMeasuredDimension, aplikasi akan crash dengan IllegalStateException. Ini terutama sering terjadi ketika ada percabangan bersyarat dalam metode dan di salah satu cabang panggilan tidak ada. Setiap cabang kode dalam onMeasure harus diakhiri dengan panggilan setMeasuredDimension.

Mengabaikan mode AT_MOST — kesalahan paling umum kedua. Jika View dalam mode AT_MOST selalu menggunakan ukuran yang diteruskan alih-alih perhitungan berdasarkan konten, kontainer induk tidak akan dapat mendistribusikan ruang dengan benar. Misalnya, TextView dalam AT_MOST harus menghitung lebar teks dan menggunakan minimum dari lebar yang diinginkan dan yang diteruskan. Mengabaikan AT_MOST menyebabkan View menempati semua ruang yang tersedia bahkan dengan konten kecil.

Membuat objek di dalam onMeasure — kesalahan kinerja klasik. Karena onMeasure dapat dipanggil berkali-kali (pada setiap permintaan layout), membuat objek (Paint, Rect, String) di dalam metode ini mengotori memori dan memicu pengumpulan sampah. Semua objek harus dibuat sekali di konstruktor View, dan dalam onMeasure hanya logika perhitungan dimensi yang boleh dijalankan. Aturan yang sama berlaku untuk onDraw dan onLayout.

Mengukur View anak di ViewGroup

measureChildWithMargins — adalah metode protected dari ViewGroup yang mengukur satu View anak dengan mempertimbangkan MarginLayoutParams-nya. Metode ini menerima MeasureSpec induk dan pergeseran akumulasi dalam lebar dan tinggi. Secara otomatis menyesuaikan MeasureSpec untuk View anak, mengurangi padding induk dan margin anak, dan meneruskan MeasureSpec yang disesuaikan ke child.measure().

Untuk logika pengukuran lanjutan, ViewGroup dapat menimpa measureChild(View child, int parentWidthSpec, int parentHeightSpec) atau bekerja langsung dengan MeasureSpec untuk setiap elemen anak. Misalnya, LinearLayout dalam onMeasure mengiterasi semua View anak, mengukur masing-masing dengan mempertimbangkan layout_weight-nya, dan mendistribusikan ruang yang tersisa secara proporsional. Pendekatan ini memungkinkan implementasi algoritma tata letak arbitrer.

Caching hasil pengukuran melalui mekanisme cache ukuran (measure cache) tersedia melalui bendera setMeasureWithLargestChildEnabled di ViewGroup tertentu. Namun, dalam kebanyakan kasus, onMeasure dipanggil ulang pada setiap perubahan tata letak, dan caching tidak diterapkan. Di ViewGroup kustom, disarankan untuk meminimalkan perhitungan dalam onMeasure, bukan mengandalkan cache.

Pertanyaan yang Sering Diajukan

Apakah wajib menimpa onMeasure untuk View kustom?

Ya, jika View kustom mewarisi langsung dari kelas View. Jika pewarisan berasal dari TextView, ImageView, atau Button dengan ukuran standarnya, onMeasure tidak perlu ditimpa. Untuk ViewGroup, penimpaan onMeasure selalu diperlukan — jika tidak, elemen anak tidak akan diukur dengan benar.

Apa yang terjadi jika setMeasuredDimension tidak dipanggil?

Sistem Android melempar IllegalStateException dengan teks “The View did not call setMeasuredDimension”. Pengecualian ini muncul dalam metode measure() setelah onMeasure selesai, jika dimensi akhir tetap nol. Pengecualian akan membuat aplikasi crash jika tidak ditangani melalui try-catch.

Apa perbedaan antara getWidth dan getMeasuredWidth?

getMeasuredWidth() mengembalikan ukuran yang ditetapkan dalam onMeasure (fase pengukuran). getWidth() mengembalikan ukuran sebenarnya yang diterima View dalam onLayout setelah mempertimbangkan semua koreksi penentuan posisi. Untuk sebagian besar View, nilai-nilai ini sama, tetapi dalam ViewGroup kustom, mereka bisa berbeda.

Bisakah animasi atau status View diubah dalam onMeasure?

Tidak, onMeasure ditujukan semata-mata untuk menghitung dimensi. Mengubah status, memulai animasi, bekerja dengan jaringan, atau memperbarui data dalam metode ini melanggar arsitektur Android dan dapat menyebabkan panggilan measure rekursif, karena perubahan status dapat memicu requestLayout.

Bagaimana onMeasure berinteraksi dengan ConstraintLayout?

ConstraintLayout secara independen mengelola pengukuran elemen anak berdasarkan batasan yang diberikan. Jika View kustom di dalam ConstraintLayout menimpa onMeasure, ia harus memproses MeasureSpec yang diteruskan dari ConstraintLayout dengan benar, jika tidak, batasan mungkin tidak berfungsi. ConstraintLayout menggunakan algoritma dua lintasan dengan WidgetContainer-nya sendiri untuk perhitungan.

Ringkasan

  • onMeasure() — metode View untuk menentukan dimensi, dipanggil oleh sistem Android dalam fase measure
  • MeasureSpec mengkodekan mode pengukuran (EXACTLY, AT_MOST, UNSPECIFIED) dan ukuran yang diteruskan dari induk
  • setMeasuredDimension — panggilan wajib di akhir onMeasure yang menetapkan dimensi akhir
  • resolveSize — metode bantu yang mengimplementasikan logika pemrosesan MeasureSpec standar dalam satu baris
  • Algoritma dua lintasan menjamin pengukuran yang benar dalam hierarki induk-anak
  • measureChildWithMargins digunakan untuk mengukur View anak di ViewGroup kustom dengan mempertimbangkan jarak
  • Membuat objek di dalam onMeasure sangat tidak disarankan karena risiko GC dan kehilangan bingkai

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga