Canvas στο Android — είναι μια κλάση από το πακέτο android.graphics που παρέχει API για 2D σχεδίαση σε View. Το Canvas λειτουργεί σαν καμβάς, πάνω στον οποίο ο προγραμματιστής σχεδιάζει γεωμετρικά σχήματα, κείμενο, εικόνες και διαδρομές χρησιμοποιώντας διάφορες μεθόδους draw*. Σύμφωνα με το Android Developers Documentation (2025), το Canvas είναι το κύριο εργαλείο για προσαρμοσμένη σχεδίαση στο Android και χρησιμοποιείται μαζί με την κλάση Paint για τον καθορισμό στυλ και χρωμάτων. Κάθε κλήση onDraw() λαμβάνει ένα έτοιμο στιγμιότυπο Canvas που συνδέεται με το buffer bitmap της προβολής.
Κύρια σημεία
Canvas — είναι η κεντρική κλάση 2D γραφικών στο Android που παρέχει μεθόδους για σχεδίαση σε επίπεδο. Κάθε Canvas είναι συνδεδεμένο με ένα Bitmap — μια περιοχή μνήμης όπου γράφονται τα αποτελέσματα των λειτουργιών σχεδίασης. Όταν το View επανασχεδιάζεται, το σύστημα μεταβιβάζει στο onDraw() ένα Canvas που είναι ήδη συνδεδεμένο με το buffer αυτού του View.
Το Canvas λειτουργεί σε ένα σύστημα συντεταγμένων όπου (0,0) είναι η πάνω αριστερή γωνία, ο άξονας X πηγαίνει δεξιά, ο άξονας Y κάτω. Όλες οι λειτουργίες σχεδίασης χρησιμοποιούν την τρέχουσα κατάσταση του Canvas, συμπεριλαμβανομένου του πίνακα μετασχηματισμού, της περιοχής αποκοπής (clip) και του στυλ Paint. Σύμφωνα με το Android Documentation (2025), το Canvas API περιλαμβάνει πάνω από 30 μεθόδους draw* για διαφορετικούς τύπους γραφικών πρωτογόνων.
Από το Android 3.0 (API 11) το Canvas υποστηρίζει επιτάχυνση υλικού μέσω DisplayList. Στη λειτουργία επιτάχυνσης, οι λειτουργίες Canvas γράφονται στο DisplayList και στη συνέχεια εκτελούνται στην GPU. Αυτό δίνει σημαντική αύξηση απόδοσης, αλλά επιβάλλει περιορισμούς: ορισμένες λειτουργίες Canvas (clipPath με μη Path αποκοπή) δεν υποστηρίζονται στη λειτουργία επιτάχυνσης.
Το Canvas παρέχει ένα σύνολο μεθόδων για σχεδίαση βασικών πρωτογόνων. Κάθε μέθοδος δέχεται γεωμετρικές παραμέτρους και ένα αντικείμενο Paint που καθορίζει το οπτικό στυλ.
| Μέθοδος | Σκοπός | Παράδειγμα |
|---|---|---|
| drawRect() | Σχεδίαση ορθογωνίου | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Σχεδίαση κύκλου | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Σχεδίαση γραμμής | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Σχεδίαση κειμένου | canvas.drawText("Hello", 10f, 20f, paint) |
| drawBitmap() | Σχεδίαση εικόνας | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Σχεδίαση αυθαίρετης διαδρομής | canvas.drawPath(path, paint) |
| drawOval() | Σχεδίαση οβάλ | canvas.drawOval(rectF, paint) |
| drawArc() | Σχεδίαση τόξου ή τομέα | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Κάθε μέθοδος μπορεί να κληθεί σε οποιαδήποτε σειρά — το Canvas σχεδιάζει με τη σειρά κλήσεων, τοποθετώντας νέα στοιχεία πάνω από τα προηγούμενα. Για σύνθετες συνθέσεις χρησιμοποιείται η τεχνική πολυεπίπεδης σχεδίασης με αποθήκευση και επαναφορά της κατάστασης Canvas.
Paint — είναι η κλάση που καθορίζει πώς θα σχεδιάσει το Canvas. Χωρίς Paint, οι μέθοδοι Canvas είναι άχρηστες — ορίζει χρώμα, πάχος γραμμής, στυλ γεμίσματος, μέγεθος κειμένου, εφέ (σκιά, κλίση, θάμπωμα) και λειτουργία ανάμειξης.
Το Paint έχει βασικές ιδιότητες: color (χρώμα), style (FILL, STROKE ή FILL_AND_STROKE), strokeWidth (πάχος γραμμής), textSize (μέγεθος κειμένου), isAntiAlias (εξομάλυνση), shader (κλίση ή εικόνα). Η δημιουργία αντικειμένου Paint είναι μια δαπανηρή λειτουργία, επομένως πρέπει να δημιουργείται μία φορά και να επαναχρησιμοποιείται.
private val fillPaint = Paint().apply {
color = Color.parseColor("#FF6200EE")
style = Paint.Style.FILL
isAntiAlias = true
}
private val strokePaint = Paint().apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 4f
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(100f, 100f, 80f, fillPaint)
canvas.drawCircle(100f, 100f, 80f, strokePaint)
}
Σε αυτό το παράδειγμα χρησιμοποιούνται δύο Paint: ένα για το γέμισμα του κύκλου με μοβ χρώμα, το δεύτερο για το λευκό περίγραμμα. Ο διαχωρισμός σε fill και stroke είναι μια τυπική πρακτική που επιτρέπει την ευέλικτη διαχείριση της εμφάνισης χωρίς αναδημιουργία αντικειμένων.
Για γεμίσματα με κλίση, το Paint δέχεται ένα Shader — υποκλάσεις LinearGradient, RadialGradient, SweepGradient. Το Shader συνδέεται στο Paint μέσω της μεθόδου setShader() και καθορίζει πώς αλλάζει το χρώμα στην περιοχή σχεδίασης.
Το Canvas υποστηρίζει μητρικούς μετασχηματισμούς του συστήματος συντεταγμένων: translate() (μετατόπιση), rotate() (περιστροφή), scale() (κλίμακα) και skew() (λοξή). Οι μετασχηματισμοί συσσωρεύονται: κάθε επόμενη κλήση εφαρμόζεται πάνω από τις προηγούμενες.
Η διαχείριση της στοίβας μετασχηματισμών γίνεται μέσω save() και restore(). Η save() αποθηκεύει την τρέχουσα κατάσταση Canvas (πίνακα, clip) στη στοίβα, η restore() επαναφέρει την τελευταία αποθηκευμένη κατάσταση. Μεταξύ save() και restore() μπορεί να υπάρχουν ένθετοι μετασχηματισμοί.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Αποθήκευση αρχικής κατάστασης
canvas.translate(100f, 100f) // Μετατόπιση αρχής
// Πρώτο τετράγωνο
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Αποθήκευση μετά μετατόπιση
canvas.rotate(45f, 25f, 25f) // Περιστροφή γύρω από κέντρο τετραγώνου
// Περιστρεφόμενο τετράγωνο
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Επαναφορά μετά περιστροφή
canvas.restore() // Επαναφορά αρχικής κατάστασης
}
Χρησιμοποιείτε πάντα save() πριν από την έναρξη μετασχηματισμών και restore() μετά την ολοκλήρωσή τους. Οι μη ισορροπημένες save/restore οδηγούν σε συσσώρευση του πίνακα και λανθασμένη σχεδίαση των επόμενων στοιχείων.
Για σύνθετα γραφικά, το Canvas συνδυάζεται με Bitmap και Path. Το Bitmap χρησιμοποιείται για φόρτωση εικόνων και προσωρινή αποθήκευση αποδοθέντων καρέ. Το Path — για δημιουργία αυθαίρετων καμπύλων Bezier, πολυγώνων και σύνθετων περιγραμμάτων.
Τεχνική offscreen-απόδοσης: δημιουργείται ένα Bitmap, πάνω του σχεδιάζεται μια σύνθετη σκηνή μέσω ξεχωριστού Canvas, και στη συνέχεια το έτοιμο Bitmap εμφανίζεται στο onDraw() μέσω drawBitmap(). Αυτό επιτρέπει την ενημέρωση σύνθετων γραφικών χωρίς πλήρη επανασχεδίαση κάθε καρέ.
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null
fun buildOffscreenScene(width: Int, height: Int) {
offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
offscreenCanvas = Canvas(offscreenBitmap)
// Απόδοση σκηνής μία φορά
offscreenCanvas!!.drawColor(Color.WHITE)
drawGrid(offscreenCanvas!!)
drawComplexShape(offscreenCanvas!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}
Path χρησιμοποιείται για δημιουργία σύνθετων περιγραμμάτων: καμπύλες Bezier (quadTo, cubicTo), τόξα (arcTo), γραμμές (lineTo). Το Path μπορεί να είναι κλειστό (close()) για γεμισμένα σχήματα ή ανοιχτό για γραμμές. Οι κύριες μέθοδοι drawPath() συνδυάζουν Path και Paint για την τελική σχεδίαση.
Η απόδοση της σχεδίασης Canvas επηρεάζει άμεσα τα FPS της εφαρμογής. Κύρια προβλήματα: υπερβολικές δεσμεύσεις στο onDraw(), συχνές invalidate() και μη βέλτιστη χρήση της Επιτάχυνσης Υλικού.
Τα Paint, Path, Rect, RectF και άλλα αντικείμενα πρέπει να δημιουργούνται μία φορά στον αρχικοποιητή ή στη μέθοδο setup(). Κάθε δέσμευση στο onDraw() δημιουργεί φόρτο στον garbage collector, οδηγώντας σε μικρο-καθυστερήσεις. Εξαίρεση — static ή companion object για αμετάβλητα αντικείμενα.
Εάν μεγάλο μέρος του View είναι κρυμμένο εκτός ορατής περιοχής (π.χ. σε ScrollView), χρησιμοποιήστε canvas.clipRect() για περιορισμό της σχεδίασης. Η Επιτάχυνση Υλικού σε αυτή την περίπτωση βελτιστοποιεί περαιτέρω την αποκοπή αόρατων εικονοστοιχείων.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Αποκοπή σε ορατή περιοχή
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Απόδοση μεγάλης σκηνής
renderScene(canvas)
canvas.restore()
}
Χρησιμοποιήστε View.setLayerType() με LAYER_TYPE_HARDWARE για View με σύνθετα γραφικά Canvas που ενημερώνονται σπάνια. Για συχνά ενημερωμένες κινούμενες εικόνες, χρησιμοποιήστε LAYER_TYPE_NONE για να αποφύγετε τη δαπανηρή ενημέρωση του επιπέδου υλικού. Κάντε προφίλ μέσω GPU Profile Rendering και Android Studio Profiler.
Συχνές Ερωτήσεις
Ναι, μπορείτε να δημιουργήσετε Canvas(Bitmap) — έναν κατασκευαστή που δέχεται Bitmap. Αυτό χρησιμοποιείται για offscreen-απόδοση: σχεδιάζετε στο Bitmap και στη συνέχεια το εμφανίζετε μέσω drawBitmap(). Το Canvas μπορεί επίσης να δημιουργηθεί από Surface (για SurfaceView) ή μέσω Picture.
Canvas — είναι API για σχεδίαση σε View στο νήμα UI. SurfaceView — είναι ένα ξεχωριστό Surface που μπορεί να ενημερώνεται από το νήμα παρασκηνίου μέσω του δικού του Canvas (lockCanvas() / unlockCanvasAndPost()). Το SurfaceView είναι προτιμότερο για παιχνίδια και βίντεο.
Η πιο συχνή αιτία είναι οι μη ισορροπημένες save() και restore(). Εάν μετά τον μετασχηματισμό (translate, rotate) δεν κληθεί η restore(), το Canvas παραμένει στο τροποποιημένο σύστημα συντεταγμένων και τα επόμενα στοιχεία σχεδιάζονται με μετατόπιση. Ελέγξτε τα ζεύγη save()/restore().
Χρησιμοποιήστε Paint.setTextAlign(Paint.Align.CENTER) για οριζόντια στοίχιση και Paint.getTextBounds() ή Paint.descent() / ascent() για κάθετη. Κεντράρισμα: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).
Το Canvas από μόνο του δεν δημιουργεί κινούμενα σχέδια — απλώς σχεδιάζει την τρέχουσα κατάσταση. Η κίνηση επιτυγχάνεται μέσω διαδοχικών κλήσεων invalidate() (ή postInvalidateOnAnimation()), σχεδιάζοντας κάθε φορά ένα νέο καρέ στο onDraw(). Για ομαλές κινήσεις χρησιμοποιήστε ValueAnimator ή Choreographer για συγχρονισμό με Vsync.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης