Custom layouts let you arrange app elements exactly how you want. They help make your app look unique and fit your design needs.
Custom layouts in Android Kotlin
class MyCustomLayout(context: Context, attrs: AttributeSet?) : ViewGroup(context, attrs) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // Measure children and set measured dimensions val widthSize = MeasureSpec.getSize(widthMeasureSpec) val heightSize = MeasureSpec.getSize(heightMeasureSpec) setMeasuredDimension(widthSize, heightSize) } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { // Position children inside this layout } }
You must override onMeasure to tell Android how big your layout and its children should be.
You must override onLayout to place each child view inside your layout.
class SimpleCustomLayout(context: Context) : ViewGroup(context) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val count = childCount var maxWidth = 0 var totalHeight = 0 for (i in 0 until count) { val child = getChildAt(i) measureChild(child, widthMeasureSpec, heightMeasureSpec) maxWidth = maxOf(maxWidth, child.measuredWidth) totalHeight += child.measuredHeight } val width = resolveSize(maxWidth, widthMeasureSpec) val height = resolveSize(totalHeight, heightMeasureSpec) setMeasuredDimension(width, height) } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { var currentTop = 0 for (i in 0 until childCount) { val child = getChildAt(i) child.layout(0, currentTop, child.measuredWidth, currentTop + child.measuredHeight) currentTop += child.measuredHeight } } }
class CenterCustomLayout(context: Context) : ViewGroup(context) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val child = getChildAt(0) measureChild(child, widthMeasureSpec, heightMeasureSpec) setMeasuredDimension( MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec) ) } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { val child = getChildAt(0) val left = (width - child.measuredWidth) / 2 val top = (height - child.measuredHeight) / 2 child.layout(left, top, left + child.measuredWidth, top + child.measuredHeight) } }
This custom layout stacks buttons vertically. Each button appears below the previous one. It measures all children to find the widest width and total height, then arranges them top to bottom.
class VerticalStackLayout(context: Context) : ViewGroup(context) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { var maxWidth = 0 var totalHeight = 0 for (i in 0 until childCount) { val child = getChildAt(i) measureChild(child, widthMeasureSpec, heightMeasureSpec) maxWidth = maxOf(maxWidth, child.measuredWidth) totalHeight += child.measuredHeight } val width = resolveSize(maxWidth, widthMeasureSpec) val height = resolveSize(totalHeight, heightMeasureSpec) setMeasuredDimension(width, height) } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { var currentTop = 0 for (i in 0 until childCount) { val child = getChildAt(i) child.layout(0, currentTop, child.measuredWidth, currentTop + child.measuredHeight) currentTop += child.measuredHeight } } } // Usage in an Activity or Fragment: // val layout = VerticalStackLayout(context) // layout.addView(Button(context).apply { setText("Button 1") }) // layout.addView(Button(context).apply { setText("Button 2") }) // setContentView(layout)
Always call measureChild or measureChildWithMargins inside onMeasure to measure children properly.
Use setMeasuredDimension to tell Android your layout's size after measuring.
In onLayout, use child.layout(left, top, right, bottom) to position each child view.
Custom layouts let you control exactly how child views are measured and placed.
You must override onMeasure and onLayout methods.
Custom layouts help create unique and flexible UI designs beyond standard layouts.
Practice
Solution
Step 1: Understand the role of custom layouts
Custom layouts allow developers to define how child views are measured and positioned beyond standard layouts.Step 2: Compare with other options
Options A, B, and C describe unrelated features like animations, automatic UI generation, or system UI replacement.Final Answer:
To control exactly how child views are measured and placed -> Option AQuick Check:
Custom layouts = precise child view control [OK]
- Thinking custom layouts generate UI automatically
- Confusing custom layouts with animations
- Believing custom layouts replace system UI
Solution
Step 1: Identify the method for measuring size
TheonMeasuremethod is used to determine the size requirements of the layout and its children.Step 2: Differentiate from other methods
onLayoutpositions children,onDrawhandles drawing, andonCreateis unrelated to layout measurement.Final Answer:
onMeasure -> Option BQuick Check:
Measure size = onMeasure [OK]
- Confusing onLayout with onMeasure
- Using onDraw for size calculation
- Thinking onCreate handles layout measurement
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val child = getChildAt(0)
child.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(50, MeasureSpec.EXACTLY))
setMeasuredDimension(200, 100)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val child = getChildAt(0)
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
}Solution
Step 1: Analyze onMeasure child measurement
The child is measured with width 100 pixels exactly and height 50 pixels exactly.Step 2: Analyze onLayout child placement
The child is laid out from (0,0) to (child.measuredWidth, child.measuredHeight), so width is 100 pixels.Final Answer:
100 pixels -> Option DQuick Check:
Child width = measured width = 100 [OK]
- Using parent width instead of child's measured width
- Confusing layout size with measure specs
- Assuming child's width equals parent's width
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
for (i in 0 until childCount) {
val child = getChildAt(i)
child.layout(0, 0, 0, 0)
}
}Solution
Step 1: Examine child.layout parameters
Each child is positioned with left=0, top=0, right=0, bottom=0, which means zero width and height.Step 2: Understand effect on visibility
Zero size means children are not visible on screen.Final Answer:
Child views are laid out with zero width and height -> Option AQuick Check:
Zero size layout hides children [OK]
- Assuming loop skips children
- Thinking super.onLayout is mandatory
- Ignoring layout size parameters
onLayout for this?Solution
Step 1: Understand layout requirements
Two children side by side means splitting parent's width equally, full height each.Step 2: Apply correct coordinates in onLayout
First child from left=0 to right=width/2, second from left=width/2 to right=width, both top=0 bottom=height.Final Answer:
Layout first child from (0,0) to (width/2, height), second from (width/2,0) to (width, height) -> Option CQuick Check:
Side by side = split width in onLayout [OK]
- Overlapping children by using full width for both
- Placing second child outside visible bounds
- Stacking children vertically instead of horizontally
Ready to prove it?
Pass the test and this pattern moves unknown
