Custom View Androidban — rajzolás és eseménykezelés

Szerző: IT Sectr Megjelenés: 2026-07-20 Olvasási idő: 9 perc

Custom View — egy egyedi felületi komponens Androidban, amely a View osztály vagy alosztályai (Button, TextView, ImageView) örökítésével és a kulcsfontosságú metódusok felülírásával jön létre: onDraw a rajzoláshoz, onMeasure a méretek meghatározásához és onTouchEvent az érintések kezeléséhez. A Google Android Developer Guide (2024) szerint a Custom View akkor használatos, amikor a szabványos Android SDK komponensek nem biztosítják a kívánt viselkedést vagy megjelenést — például egyedi animációkhoz, nem szabványos formákhoz, speciális diagramokhoz és játékelemekhez.

Főbb pontok

  • Custom View — a View vagy alosztályainak örökítésével jön létre az onDraw, onMeasure, onTouchEvent felülírásával.
  • A rajzolás a Canvas API-n keresztül történik — geometriai alakzatok, szöveg, útvonalak, Bitmap és animációk.
  • onMeasure felel a View helyes méreteiért, figyelembe véve a layout paramétereket és a paddinget.
  • Egyedi XML-attribútumok a res/values/attrs.xml fájlban deklarálódnak és a View konstruktorában olvasódnak be.
  • Az érintések kezelése onTouchEvent segítségével lehetővé teszi a gesztusok, drag-and-drop és multitouch támogatását.

Mi az a Custom View Androidban?

Custom View — egy osztály, amely az android.view.View (vagy egyik alosztálya) osztályból örököl, és felülírja a rendszermetódusokat saját rajzolási, mérési és eseménykezelési logika megvalósításához. A Custom View az Android alapvető mechanizmusa egyedi UI-komponensek létrehozásához.

Az Android két megközelítést kínál a Custom View létrehozásához: öröklés a View-ből (teljesen egyedi rajzolás onDraw segítségével) és öröklés egy meglévő View-alosztályból (például Button vagy TextView felülírása funkcionalitás hozzáadásához az alapviselkedés megtartása mellett).

Az alapkonstruktor Custom View esetén Context és AttributeSet paramétereket kell fogadnia — ez lehetővé teszi a rendszer számára, hogy View-t hozzon létre XML-kódból. Ha a View csak kódból lesz használva, a Context paraméteres konstruktor elegendő. A harmadik, stílusos konstruktor az Android témák támogatásához szükséges.

kotlin
class CustomView
    @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.BLUE
        strokeWidth = 4f
        style = Paint.Style.FILL
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawCircle(width / 2f,
            height / 2f,
            50f, paint)
    }
}

Minimális Custom View egy konstruktorból és az onDraw-ból áll. Az Android Layout rendszerben való helyes működéshez azonban az onMeasure-t is felül kell írni — különben a View nulla méretekkel jelenhet meg, vagy helytelenül reagálhat a wrap_content layout paraméterekre.

A Google I/O 2023 szerint az egyedi View-k a Google Play top 100 alkalmazásának 65%-ában használatosak. A Custom View létrehozásának leggyakoribb okai: egyedi animációk (35%), egyedi grafikonok és diagramok (25%), speciális vezérlőelemek (20%) és márkázott komponensek (20%).

onMeasure és onLayout: méretek kezelése

onMeasure — az a metódus, amelyben a View közli a rendszerrel a kívánt méreteit a kapott MeasureSpec (szülői korlátozások) alapján. Az onMeasure helyes megvalósítása nélkül a View nulla magasságú lehet wrap_content esetén, vagy a teljes képernyőt elfoglalhatja match_parent esetén.

MeasureSpec egy módból (EXACTLY, AT_MOST, UNSPECIFIED) és egy értékből áll. EXACTLY — a szülő pontos méretet adott meg (match_parent vagy rögzített érték). AT_MOST — a szülő maximumot adott meg (wrap_content). UNSPECIFIED — korlátozás nélkül (ScrollView, ListView).

kotlin
override fun onMeasure(
    widthMeasureSpec: Int,
    heightMeasureSpec: Int
) {
    val desiredWidth = paddingLeft + paddingRight
        + DEFAULT_WIDTH
    val desiredHeight = paddingTop + paddingBottom
        + DEFAULT_HEIGHT

    val measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
    val modeWidth = MeasureSpec.getMode(widthMeasureSpec)

    val resultWidth = when (modeWidth) {
        MeasureSpec.EXACTLY -> measuredWidth
        MeasureSpec.AT_MOST ->
            desiredWidth.coerceAtMost(measuredWidth)
        else -> desiredWidth
    }

    val resultHeight = resolveSize(desiredHeight,
        heightMeasureSpec)

    setMeasuredDimension(resultWidth, resultHeight)
}

resolveSize — egy hasznos Android segédmetódus, amely leegyszerűsíti az onMeasure megvalósítását. Fogadja a kívánt méretet és a MeasureSpec-et, és visszaadja a helyes értéket: EXACTLY esetén — pontos méret, AT_MOST esetén — a kívánt és a maximum közötti minimum, UNSPECIFIED esetén — kívánt méret.

onLayout View (nem ViewGroup) esetén általában nem kerül felülírásra — a szülő hívja meg a gyermekelemek pozicionálásához. ViewGroup esetén az onLayout kötelező — ebben kell meghívni a layout()-ot minden gyermek View-ra.

onDraw és Canvas API: rajzolás

onDraw — a Custom View szíve. Ezt a metódust az Android hívja meg a View minden újrarajzolásakor. Egy Canvas objektumot kap, amelyen keresztül az összes rajzolás történik: geometriai alakzatok, szöveg, útvonalak, Bitmap és animációk.

Canvas API metódusokat biztosít az összes alapvető művelethez: drawCircle, drawRect, drawLine, drawPath, drawText, drawBitmap, drawArc, drawOval. A stílus beállításához a Paint objektumot használjuk — amely meghatározza a színt, vastagságot, kitöltési stílust, betűtípust, árnyékokat és effekteket.

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    // Háttér
    canvas.drawRect(0f, 0f,
        width.toFloat(), height.toFloat(), bgPaint)

    // Szöveg
    canvas.drawText("Egyedi nézet",
        paddingLeft.toFloat(),
        paddingTop.toFloat() + textPaint.textSize,
        textPaint)

    // Kör
    canvas.drawCircle(
        width / 2f,
        height / 2f,
        radius, circlePaint)

    // Útvonal (Bezier-görbe)
    val path = Path().apply {
        moveTo(0f, height.toFloat())
        quadTo(width / 2f, 0f,
            width.toFloat(), height.toFloat())
    }
    canvas.drawPath(path, pathPaint)
}

Paint — a rajzolási stílus konfigurátora. Az Anti-aliasing (Paint.ANTI_ALIAS_FLAG) kötelező a sima élekhez. A Subpixel text (SUBPIXEL_TEXT_FLAG) javítja a szöveg minőségét. A Style.FILL, STROKE és FILL_AND_STROKE határozzák meg, hogy az alakzat kitöltött, körvonalazott vagy mindkettő legyen.

Fontos: ne hozzon létre Paint, Path, Rect objektumokat az onDraw-ban — ez allokációkhoz vezet a rajzolási ciklusban, és garbage collection-t indít el, ami csökkenti az FPS-t. Hozzon létre minden rajzolási objektumot a konstruktorban vagy az init metódusban. Animációhoz használjon ValueAnimator-t vagy ObjectAnimator-t invalidate()-val az újrarajzolás elindításához.

Egyedi XML-attribútumok az attrs.xml-ben

Egyedi attribútumok lehetővé teszik a Custom View konfigurálását XML-kódból, akárcsak a szabványos android:layout_width vagy android:background attribútumok. Az attribútumok a res/values/attrs.xml fájlban deklarálódnak névvel, típussal és opcionális alapértékkel.

Attribútumtípusok: string, integer, float, boolean, color, dimension, enum, flag, fraction, reference (hivatkozás erőforrásra). Minden típus esetén az Android automatikusan elemzi az értéket az XML-ből és továbbítja a TypedArray-be.

xml
<!-- res/values/attrs.xml -->
<resources>
    <declare-styleable name="CustomView">
        <attr name="circleColor"
            format="color" />
        <attr name="circleRadius"
            format="dimension" />
        <attr name="labelText"
            format="string" />
        <attr name="showAnimation"
            format="boolean" />
    </declare-styleable>
</resources>

A Custom View konstruktorában az attribútumok a context.obtainStyledAttributes segítségével olvashatók be, amely egy TypedArray-t ad vissza. A TypedArray típusos hozzáférési metódusokat biztosít: getColor, getDimension, getString, getBoolean, getInt. Olvasás után a TypedArray-n recycle()-t kell hívni az erőforrások felszabadításához.

kotlin
init {
    val typedArray = context.obtainStyledAttributes(
        attrs,
        R.styleable.CustomView
    )

    circleColor = typedArray.getColor(
        R.styleable.CustomView_circleColor,
        Color.BLUE
    )
    circleRadius = typedArray.getDimension(
        R.styleable.CustomView_circleRadius,
        50f
    )
    labelText = typedArray.getString(
        R.styleable.CustomView_labelText
    ) ?: "Default"

    typedArray.recycle()
}

Használat XML-ben: adja hozzá az app névteret (xmlns:app="http://schemas.android.com/apk/res-auto") és használja az egyedi attribútumokat app:circleColor="@color/red" formában. Az Android Studio automatikusan kiegészíti és ellenőrzi az attribútumtípusokat, ha a deklaráció az attrs.xml-ben helyes.

Érintések kezelése onTouchEvent-tel

onTouchEvent — a metódus, amely a View minden érintésekor meghívódik. Egy MotionEvent objektumot kap az esemény típusáról (ACTION_DOWN, ACTION_MOVE, ACTION_UP, ACTION_CANCEL), koordinátákról, nyomásról és ujjak számáról (multitouch).

Az összetett gesztusok (swipe, nagyítás, hosszú lenyomás) kezeléséhez használjon GestureDetector-t vagy ScaleGestureDetector-t az onTouchEvent-tel kombinálva. A GestureDetector leegyszerűsíti az onSingleTapUp, onFling, onLongPress, onDoubleTap és más szabványos gesztusok felismerését.

kotlin
private val gestureDetector = GestureDetector(
    context, object : GestureDetector.SimpleOnGestureListener() {
    override fun onSingleTapUp(e: MotionEvent): Boolean {
        handleTap(e.x, e.y)
        return true
    }
})

override fun onTouchEvent(event: MotionEvent): Boolean {
    val handled = gestureDetector.onTouchEvent(event)

    when (event.action) {
        MotionEvent.ACTION_MOVE -> {
            currentX = event.x
            currentY = event.y
            invalidate()
            return true
        }
    }

    return handled || super.onTouchEvent(event)
}

Hit-testing — annak meghatározása, hogy az érintés a View egy adott területére esett-e. Téglalap alakú területekhez használjon Rect.contains(x, y) metódust. Körökhöz — távolság ellenőrzése a középponttól: sqrt(dx^2 + dy^2) < radius. Tetszőleges alakzatokhoz — Path.op() vagy Region.contains().

Multitouch a MotionEvent.getPointerCount() és getPointerId(index) segítségével kezelhető. Minden ujj egy egyedi ID-t kap, amely ACTION_DOWN-tól ACTION_POINTER_UP-ig megmarad. Az ACTION_MOVE az összes aktív ujj adatait tartalmazhatja — használjon getHistoricalX/Y-t a mozgás interpolálásához.

Custom View teljesítményoptimalizálása

Custom View teljesítménybeli szűk keresztmetszetté válhat, ha az onDraw gyakran kerül meghívásra (animáció, görgetés) vagy nehéz műveleteket tartalmaz. Az Android több mechanizmust kínál az egyedi komponensek rajzolásának optimalizálásához.

Layer type összetett rajzoláshoz

View.setLayerType lehetővé teszi a View renderelésének átkapcsolását szoftveres (LAYER_TYPE_SOFTWARE) vagy hardveres (LAYER_TYPE_HARDWARE) rétegre. A Software layer hasznos olyan összetett Canvas grafikához, amelyet a hardveres gyorsítás nem támogat — például drawTextOnPath vagy összetett Path effektek.

csak a szükséges terület invalidate

invalidate(Rect) — csak a View megadott területét rajzolja újra, nem a teljes komponenst. Ez kritikus fontosságú nagy Custom View-k (grafikák, térképek, rajzvásznak) esetén, ahol minden egyes képkocka teljes újrarajzolása FPS-csökkenést okoz. A vsync-el való szinkronizáláshoz használjon postInvalidateOnAnimation()-t.

kotlin
class EfficientCustomView
    @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    // Objektumok egyszer létrehozva a konstruktorban
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val rect = Rect()
    private val position = PointF()

    fun updatePosition(x: Float, y: Float) {
        position.set(x, y)
        // Csak a pont körüli terület újrarajzolása
        rect.set(
            (x - 10).toInt(),
            (y - 10).toInt(),
            (x + 10).toInt(),
            (y + 10).toInt()
        )
        invalidate(rect)
    }
}

Hardveres gyorsítás alapértelmezetten be van kapcsolva Android 3.0+ (API 11+) verziókon. A drawCircle, drawRect, drawBitmap Canvas műveletek hardveresen gyorsítottak és a GPU-n futnak. Azonban a drawTextOnPath, drawVertices, összetett clipping műveletek nem gyorsítottak — ezekhez használjon LAYER_TYPE_SOFTWARE-t.

Az Android Performance Patterns (Google, 2023) szerint a Custom View lassúságának fő okai: allokációk az onDraw-ban (objektumok létrehozása minden képkockában), a teljes View újrarajzolása egyetlen elem változásakor és a LayerType.HARDWARE hiánya statikus tartalom esetén. E három probléma kijavítása a legtöbb forgatókönyvben 30-ról 60-ra növeli az FPS-t.

MetódusRendeltetésHívási gyakoriság
onDrawView tartalmának rajzolásaMinden invalidate() híváskor
onMeasureView méreteinek meghatározásaA layout változásakor
onLayoutGyermek View-k pozicionálásaonMeasure után
onTouchEventÉrintések kezeléseMinden érintéskor
onSizeChangedReakció méretváltozásraAz első layoutnál és változásnál

Gyakran ismételt kérdések

Mikor használjunk Custom View-t Compose helyett?

Custom View (Canvas API) akkor indokolt, ha maximális egyedi rajzolási teljesítményre van szükség (grafikák, videószerkesztők, térképek), integrációra meglévő View-alapú kóddal, vagy API 21 alatti Android verziók támogatására. A Jetpack Compose modern megközelítés a legtöbb új projekthez, deklaratív UI-t használ, és jobban megfelel dinamikus interfészekhez.

Kötelező felülírni az onMeasure-t?

Igen, ha a View wrap_content-et használ az XML-kódban. Az onMeasure nélkül a wrap_content match_parent-ként fog működni, mivel a View.onMeasure alapértelmezett megvalósítása nem állít be alapértelmezett méretet. Ha a View mindig rögzített méretű vagy match_parent, az onMeasure kihagyható.

Hogyan animáljunk egy Custom View-t?

Használjon ValueAnimator-t vagy ObjectAnimator-t a View tulajdonságainak (szín, sugár, pozíció) módosításához, és hívja meg az invalidate()-t az animáció callback-jében az újrarajzoláshoz. A ValueAnimator a főszálon működik és szinkronizálva van a vsync-el. Összetett animációkhoz (fizika, részecskék) használjon Choreographer.FrameCallback-t vagy Android Animation Framework-öt.

Hogyan kezeljük a multitouch-ot Custom View-ban?

Az onTouchEvent-ben használjon MotionEvent.getPointerCount()-et az ujjak számának meghatározásához és getPointerId(i)-t az egyes ujjak egyedi ID alapján történő követéséhez. ACTION_POINTER_DOWN / ACTION_POINTER_UP — az ujj hozzáadásának/eltávolításának eseményei. Nagyításhoz használjon ScaleGestureDetector-t az onTouchEvent-tel párban — leegyszerűsíti a pinch-to-zoom érzékelését.

Használható-e Custom View Jetpack Compose-ban?

Igen, az AndroidView segítségével — egy composable függvény, amely View-alapú elemet illeszt a Compose hierarchiába. Az AndroidView fogad egy gyárat a View létrehozásához és egy update callback-et az állapot szinkronizálásához. Ez lehetővé teszi a meglévő Custom View-k újrafelhasználását új Compose projektekben teljes újraírás nélkül.

Összefoglalás

  • Custom View — egyedi Android komponens, amely a View örökítésével jön létre az onDraw, onMeasure és onTouchEvent felülírásával egyedi rajzoláshoz és kezeléshez.
  • onDraw és Canvas API — a rajzolás alapja: geometriai alakzatok, szöveg, Bitmap, útvonalak — konfigurációval a Paint segítségével színhez, stílushoz és betűtípushoz.
  • onMeasure fontos a wrap_content helyes működéséhez; MeasureSpec (EXACTLY, AT_MOST, UNSPECIFIED) és resolveSize használatával számítja ki a méreteket.
  • Egyedi attribútumok az attrs.xml és TypedArray segítségével lehetővé teszik a Custom View konfigurálását XML-kódból autocomplete támogatással az Android Studio-ban.
  • onTouchEvent GestureDetector-rel biztosítja az érintések, gesztusok és multitouch kezelését, a hit-testing pedig meghatározza az interakció területét.
  • Teljesítmény: hozzon létre objektumokat egyszer a konstruktorban, használjon invalidate(Rect)-et részleges újrarajzoláshoz és LayerType-t összetett effektekhez.
  • A Custom View integrálódik a Jetpack Compose-vel az AndroidView segítségével, lehetővé téve a kész komponensek újrafelhasználását új Compose projektekben.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is