In Android development, creating custom views allows you to build reusable UI components tailored to your application’s specific needs. While it’s common to draw custom views programmatically using the Canvas API, leveraging XML for defining these views can simplify the process and improve maintainability. In this post, we’ll explore how to draw custom views using XML and Canvas in Android, focusing on defining shapes, paths, and attributes.
What are Custom Views?
Custom views are UI components that you create to extend or modify existing Android View classes. This allows you to implement unique designs, animations, or interactive elements that are not available through the standard Android widgets.
Why Draw Custom Views Using XML and Canvas?
- Separation of Concerns: Define the structure and attributes of your view in XML and handle the drawing logic in your custom view class.
- Improved Readability: XML provides a clear, declarative way to define view attributes.
- Reusability: Easily reuse custom views in different layouts.
- Maintainability: Simplifies modifications and updates to the view’s structure and appearance.
How to Draw Custom Views Using XML Canvas
To create custom views with XML and Canvas, follow these steps:
Step 1: Create a Custom View Class
First, create a custom view class that extends View and override the necessary methods such as the constructor and onDraw().
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.BLUE
style = Paint.Style.FILL
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw a rectangle
canvas.drawRect(100f, 100f, 500f, 500f, paint)
}
}
In this example:
- The
CustomViewclass extendsViewand takes aContextandAttributeSetin its constructor. - The
Paintobject is created to define the color and style of the drawing. - The
onDraw()method is overridden to draw a blue rectangle on the canvas.
Step 2: Define Attributes in attrs.xml
Create a file named attrs.xml in the res/values directory to define custom attributes for your view.
<resources>
<declare-styleable name="CustomView">
<attr name="rectColor" format="color"/>
</declare-styleable>
</resources>
This defines an attribute rectColor that allows you to specify the color of the rectangle in XML.
Step 3: Update Custom View Class to Use Attributes
Modify your custom view class to read and use the custom attributes defined in attrs.xml.
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
}
private var rectColor: Int = Color.BLUE
init {
attrs?.let {
val typedArray = context.obtainStyledAttributes(it, R.styleable.CustomView)
rectColor = typedArray.getColor(R.styleable.CustomView_rectColor, Color.BLUE)
paint.color = rectColor
typedArray.recycle()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw a rectangle
canvas.drawRect(100f, 100f, 500f, 500f, paint)
}
}
In this updated code:
- Inside the constructor, we obtain the styled attributes using
context.obtainStyledAttributes(). - The
rectColorattribute is read usingtypedArray.getColor(). - The color of the
Paintobject is set based on the value ofrectColor. - The
typedArrayis recycled to free resources.
Step 4: Use Custom View in XML Layout
Now, use your custom view in your layout XML file and specify the rectColor attribute.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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.example.myapp.CustomView
android:id="@+id/customView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rectColor="@color/red" />
</androidx.constraintlayout.widget.ConstraintLayout>
In this XML:
- The
CustomViewis declared using its fully qualified name. - The
rectColorattribute is set to@color/red, which should be defined in yourcolors.xml.
Step 5: Drawing Shapes, Paths, and Text
Inside the onDraw() method, you can use the Canvas object to draw various shapes, paths, and text.
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.View
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
color = Color.GREEN
}
private val path = Path().apply {
moveTo(100f, 700f)
lineTo(300f, 900f)
lineTo(500f, 700f)
close()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw a circle
canvas.drawCircle(200f, 200f, 150f, paint)
// Draw a rectangle
canvas.drawRect(400f, 100f, 600f, 300f, paint)
// Draw a path
canvas.drawPath(path, paint)
// Draw text
paint.color = Color.WHITE
paint.textSize = 48f
canvas.drawText("Hello, Custom View!", 100f, 600f, paint)
}
}
In this extended example:
- A circle is drawn using
canvas.drawCircle(). - A rectangle is drawn using
canvas.drawRect(). - A path (triangle) is drawn using
canvas.drawPath(). - Text is drawn using
canvas.drawText().
Code Samples
Basic Example
Here’s a full, basic example that draws a customizable rectangle:
// CustomView.kt
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
}
private var rectColor: Int = Color.BLUE
init {
attrs?.let {
val typedArray = context.obtainStyledAttributes(it, R.styleable.CustomView)
rectColor = typedArray.getColor(R.styleable.CustomView_rectColor, Color.BLUE)
paint.color = rectColor
typedArray.recycle()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawRect(100f, 100f, 500f, 500f, paint)
}
}
<!-- res/values/attrs.xml -->
<resources>
<declare-styleable name="CustomView">
<attr name="rectColor" format="color"/>
</declare-styleable>
</resources>
<!-- layout XML -->
<com.example.myapp.CustomView
android:id="@+id/customView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rectColor="@color/red" />
Advanced Example
This advanced example combines shapes, paths, text, and attributes for greater customization:
// CustomView.kt
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.View
class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
}
private var rectColor: Int = Color.BLUE
private var textColor: Int = Color.WHITE
init {
attrs?.let {
val typedArray = context.obtainStyledAttributes(it, R.styleable.CustomView)
rectColor = typedArray.getColor(R.styleable.CustomView_rectColor, Color.BLUE)
textColor = typedArray.getColor(R.styleable.CustomView_textColor, Color.WHITE)
paint.color = rectColor
typedArray.recycle()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Draw a rectangle
canvas.drawRect(100f, 100f, 500f, 500f, paint)
// Draw text
paint.color = textColor
paint.textSize = 48f
canvas.drawText("Custom View", 150f, 300f, paint)
}
}
<!-- res/values/attrs.xml -->
<resources>
<declare-styleable name="CustomView">
<attr name="rectColor" format="color"/>
<attr name="textColor" format="color"/>
</declare-styleable>
</resources>
<!-- layout XML -->
<com.example.myapp.CustomView
android:id="@+id/customView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rectColor="@color/green"
app:textColor="@color/black" />
Conclusion
Drawing custom views using XML Canvas offers a flexible and organized approach to Android UI development. By defining attributes in XML and implementing drawing logic in your custom view class, you can create reusable and maintainable UI components. Whether you’re drawing basic shapes, complex paths, or text, combining XML with Canvas provides the power and flexibility needed to build rich and engaging user interfaces.
