Creating Android Custom Views with Kotlin and XML: A Detailed Guide

In Android development, custom views offer a powerful way to create reusable UI components tailored to your specific needs. By extending the View class, you can define custom drawing behavior, handle user interactions, and encapsulate complex UI logic. This blog post provides a detailed guide on creating a basic custom view in Kotlin with XML layout support for Android applications.

What is a Custom View?

A custom view is a UI component that you create by extending existing Android View classes or implementing your own from scratch. Custom views allow you to:

  • Create reusable components.
  • Implement custom drawing.
  • Encapsulate UI and interaction logic.
  • Improve code maintainability.

Why Use Custom Views?

  • Reusability: Encapsulate complex UI patterns into reusable components.
  • Customization: Implement unique drawing and interaction behaviors.
  • Abstraction: Hide complex UI logic behind a simple interface.
  • Performance: Optimize drawing and layout for specific use cases.

How to Create a Basic Custom View in Kotlin with XML Layout Support

Step 1: Create a New Kotlin Class

First, create a new Kotlin class that extends the View class. This class will contain the logic for your custom view.


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 : View {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.RED
        style = Paint.Style.FILL
    }

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

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

        val width = width.toFloat()
        val height = height.toFloat()

        canvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2, paint)
    }
}

Explanation:

  • Constructors: Define three constructors to support creating the view programmatically and from XML layouts.
  • Paint: Initializes a Paint object with basic styling for drawing.
  • onDraw(): Overrides the onDraw method to define custom drawing behavior. In this case, it draws a red circle in the center of the view.

Step 2: Handle XML Attributes (Optional)

To allow configuration of your custom view through XML attributes, you need to declare the attributes in a attrs.xml file and handle them in your view.

Create res/values/attrs.xml:




    
        
    

Modify the CustomView class to read these attributes:


import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import androidx.core.content.ContextCompat

class CustomView : View {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.RED
        style = Paint.Style.FILL
    }

    private var circleColor: Int = Color.RED

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        applyAttributes(attrs)
    }
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        applyAttributes(attrs)
    }

    private fun applyAttributes(attrs: AttributeSet?) {
        context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.CustomView,
            0,
            0
        ).apply {
            try {
                circleColor = getColor(R.styleable.CustomView_circleColor, Color.RED)
            } finally {
                recycle()
            }
        }
        paint.color = circleColor
    }


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

        val width = width.toFloat()
        val height = height.toFloat()

        canvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2, paint)
    }
}

Explanation:

  • Attribute Declaration: The attrs.xml file declares a custom attribute circleColor for the CustomView.
  • Attribute Retrieval: The applyAttributes function retrieves the value of circleColor from the XML attributes and applies it to the Paint object.

Step 3: Use the Custom View in XML Layout

You can now use your custom view in an XML layout file.

In your layout XML file (e.g., res/layout/activity_main.xml):





    


Note:

  • Make sure to use the fully qualified name of your custom view class in the XML.
  • Specify the circleColor attribute to customize the color of the circle.

Step 4: Integrate the Custom View in Your Activity

Finally, integrate the custom view into your activity by referencing it from your layout file.


import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Advanced Customization

Here are some additional techniques to enhance your custom views:

  • MeasureSpec: Properly handle MeasureSpec in the onMeasure method to control the size of the view.
  • Invalidation: Call invalidate() to trigger a redraw when the view’s state changes.
  • Animation: Incorporate animations to create dynamic and engaging user interfaces.

Conclusion

Creating custom views in Kotlin for Android applications allows you to build reusable, tailored UI components with specific behaviors and appearances. By handling XML attributes, you can configure your views directly from layout files, making them versatile and easy to use. Follow this guide to start building your own custom views and enhance your Android apps.