package com.example.customlayout
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.graphics.Color
import android.view.Gravity
class CustomCircleLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : ViewGroup(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var maxChildWidth = 0
var maxChildHeight = 0
// Measure children
for (i in 0 until childCount) {
val child = getChildAt(i)
measureChild(child, widthMeasureSpec, heightMeasureSpec)
if (child.measuredWidth > maxChildWidth) maxChildWidth = child.measuredWidth
if (child.measuredHeight > maxChildHeight) maxChildHeight = child.measuredHeight
}
val desiredWidth = maxChildWidth * 4
val desiredHeight = maxChildHeight * 4
val width = resolveSize(desiredWidth, widthMeasureSpec)
val height = resolveSize(desiredHeight, heightMeasureSpec)
setMeasuredDimension(width, height)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val centerX = (r - l) / 2
val centerY = (b - t) / 2
val radius = (Math.min(centerX, centerY) * 0.7).toInt()
val childCount = childCount
if (childCount == 0) return
// Layout center child (assumed last child)
val centerChild = getChildAt(childCount - 1)
val centerLeft = centerX - centerChild.measuredWidth / 2
val centerTop = centerY - centerChild.measuredHeight / 2
centerChild.layout(
centerLeft,
centerTop,
centerLeft + centerChild.measuredWidth,
centerTop + centerChild.measuredHeight
)
// Layout other children in circle
val angleStep = 2 * Math.PI / (childCount - 1)
for (i in 0 until childCount - 1) {
val child = getChildAt(i)
val angle = i * angleStep - Math.PI / 2 // start at top
val childCenterX = (centerX + radius * Math.cos(angle)).toInt()
val childCenterY = (centerY + radius * Math.sin(angle)).toInt()
val left = childCenterX - child.measuredWidth / 2
val top = childCenterY - child.measuredHeight / 2
child.layout(
left,
top,
left + child.measuredWidth,
top + child.measuredHeight
)
}
}
}
class CustomLayoutScreen : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val customLayout = CustomCircleLayout(this)
// Add 5 child TextViews arranged in a circle
for (i in 1..5) {
val tv = TextView(this).apply {
text = "Item $i"
setBackgroundColor(Color.parseColor("#FFBB86FC"))
setTextColor(Color.WHITE)
gravity = Gravity.CENTER
setPadding(20, 20, 20, 20)
}
customLayout.addView(tv)
}
// Add center TextView
val centerView = TextView(this).apply {
text = "Center View"
setBackgroundColor(Color.parseColor("#FF6200EE"))
setTextColor(Color.WHITE)
gravity = Gravity.CENTER
setPadding(30, 30, 30, 30)
}
customLayout.addView(centerView)
setContentView(customLayout)
}
}We created a custom ViewGroup named CustomCircleLayout that arranges its children in a circle.
In onMeasure, we measure all children and decide the size of the layout based on the largest child size multiplied to give enough space for the circle.
In onLayout, we calculate the center point and radius. We place the last child in the center, and the other children evenly spaced around the circle using simple trigonometry.
In the activity, we add 5 TextViews as circle items and one center TextView labeled "Center View". Each child has background color and padding for visibility.
This example shows how to create a custom layout by overriding measurement and layout behavior in Android Kotlin.