Working with Advanced UI
1. Using Custom Attributes
| Attribute | Type | Description | Example |
|---|---|---|---|
| app:customAttribute | String |
Custom attribute defined in XML layout | <TextView app:customAttribute="value" /> |
| app:customInt | Integer |
Custom integer attribute for configuration | <Button app:customInt="42" /> |
| app:customBoolean | Boolean |
Custom boolean attribute to toggle behavior | <Switch app:customBoolean="true" /> |
Example: Defining Custom Attributes in XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:customAttribute="Hello World"
app:customInt="100"
app:customBoolean="false" />
</LinearLayout>
Example: Reading Custom Attributes in Kotlin
class CustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
init {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView)
val customAttribute = typedArray.getString(R.styleable.CustomView_customAttribute)
val customInt = typedArray.getInt(R.styleable.CustomView_customInt, 0)
val customBoolean = typedArray.getBoolean(R.styleable.CustomView_customBoolean, false)
typedArray.recycle()
}
}
res/values/attrs.xml to be accessible in code.2. Implementing Custom Drawing
Example: Basic Custom View with Drawing
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 CustomDrawingView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val paint = Paint().apply {
color = Color.BLUE
style = Paint.Style.FILL
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(100f, 100f, 50f, paint)
}
}
| Property | Description | Values |
|---|---|---|
| Canvas | Used to draw graphics primitives | drawCircle(), drawRect(), drawLine() |
| Paint | Defines visual properties for drawing | color, style, strokeWidth |
| Color | Defines color for drawing operations | Color.BLUE, Color.RED |
super.onDraw() to ensure proper rendering.onDraw() to maintain UI responsiveness.3. Creating Shape Drawables
Shape drawables are XML files that define geometric shapes such as rectangles, ovals, lines, and arcs. They can be used as backgrounds or for custom UI elements.
| Property | Values | Description |
|---|---|---|
| android:shape | rectangle, oval, line, ring |
Defines the basic shape type |
| android:width | 1dp, 50dp |
Sets the width of the shape (for line and ring) |
| android:height | 1dp, 50dp |
Sets the height of the shape (for line and ring) |
| android:innerRadius | 10dp |
Inner radius for ring shape (optional) |
| android:thickness | 5dp |
Thickness of the ring (optional) |
Example: Rectangle Shape Drawable
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FF5722" />
<corners android:radius="8dp" />
</shape>
Example: Oval Shape Drawable
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<gradient
android:startColor="#00FF00"
android:endColor="#0000FF"
android:angle="45" />
</shape>
Example: Ring Shape Drawable
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:innerRadius="20dp"
android:thickness="5dp"
android:useLevel="false">
<solid android:color="#000000" />
</shape>
4. Using State List Drawables
State List Drawables are XML resources that define different drawables for different states of a view. They are commonly used to change the appearance of UI elements based on their state (e.g., enabled, pressed, focused).
| Property | Description | Values |
|---|---|---|
| android:drawable | Specifies the drawable to use for a given state | <drawable> |
| android:state_* | Defines the state condition (e.g., state_pressed, state_enabled) |
true or false |
Example: State List Drawable XML
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_pressed" android:state_pressed="true" />
<item android:drawable="@drawable/button_normal" android:state_enabled="true" />
<item android:drawable="@drawable/button_disabled" android:state_enabled="false" />
</selector>
Example: Applying State List Drawable to Button
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/button_states" />
5. Implementing Layer Drawables
A LayerDrawable is a drawable that manages an array of drawables to be drawn on top of each other. It's useful for creating complex UI elements like badges, overlays, or layered backgrounds.
| Property | Description | Example |
|---|---|---|
| drawable | Drawable resource or object to be layered | <item android:drawable="@drawable/overlay" /> |
| id | Identifier for referencing in code | <item android:id="@+id/overlay" /> |
| gravity | Gravity to align the drawable within the layer | android:gravity="center" |
Example: Layer Drawable XML
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/background" />
<item
android:drawable="@drawable/badge"
android:gravity="bottom|end"
android:mipMap="true" />
</layer-list>
Example: Using Layer Drawable in Code
val layerDrawable = ContextCompat.getDrawable(context, R.drawable.layered_drawable) as LayerDrawable
val badgeLayer = layerDrawable.findDrawableByLayerId(R.id.badge)
badgeLayer?.setAlpha(128)
AnimationDrawable or ObjectAnimator for dynamic UI effects.
6. Using Nine-Patch Images
Nine-patch images are a special type of PNG image that allows you to define stretchable areas and content insets. They are commonly used for UI elements like buttons, backgrounds, and other components that need to scale without losing their visual integrity.
| Feature | Description | Usage |
|---|---|---|
| Stretchable Areas | Defined by black pixels on the left and top edges of the image. | Indicate which parts of the image can be stretched to fit different sizes. |
| Content Insets | Defined by black pixels on the right and bottom edges of the image. | Specify where content should be placed inside the image to avoid stretching. |
Example: Nine-Patch Image Setup
Create a .9.png file in your project's res/drawable directory.
res/drawable/button_background.9.png
The image must have a black pixel border indicating stretchable areas and content insets.
.9.png extension is crucial for Android to recognize it as a nine-patch image.
Key Points
- Nine-patch images are used to scale UI elements without distortion.
- They must have a
.9.pngextension to be recognized by Android. - Black pixels on the borders define stretchable areas and content insets.
7. Creating Ripple Effects
| Property | Values | Description |
|---|---|---|
| android:background | ?android:attr/selectableItemBackground |
Applies a ripple effect to the view when touched |
| android:background | ?android:attr/selectableItemBackgroundBorderless |
Applies a ripple effect that extends beyond the view bounds |
Example: Ripple Effect on Button
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tap Me"
android:background="?android:attr/selectableItemBackground" />
Example: Ripple with Custom Color
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true" />
8. Implementing Clipping
Clipping in Android UI allows you to define a custom shape or region that limits the visible area of a view. This is useful for creating visually appealing layouts and custom UI elements.
| Property | Description | Values |
|---|---|---|
| android:clipChildren | Controls whether children are clipped to the bounds of their parent view. | true or false |
| android:clipToPadding | Controls whether the view clips its content to its padding. | true or false |
Example: Using clipChildren
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/image"
android:layout_marginStart="50dp" />
</LinearLayout>
Example: Using clipToPadding
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Content that extends beyond padding" />
</LinearLayout>
</ScrollView>
9. Using Elevation and Shadows
| Property | Values | Description |
|---|---|---|
| android:elevation | 0dp to 24dp |
Sets the z-position of a view in the UI hierarchy. Higher values appear above lower ones. |
| android:translationZ | 0dp to 24dp |
Adjusts the z-position of a view during animations or interactions. |
| android:outlineProvider | bounds, paddedBounds, none |
Determines how the view's shadow is calculated. bounds uses view bounds, paddedBounds includes padding. |
Example: Setting Elevation on a Button
<Button
android:id="@+id/elevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="8dp"
android:translationZ="4dp"
android:text="Elevated Button" />
Example: Using Shadow with CardView
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
app:cardElevation="12dp"
app:cardCornerRadius="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Card with Elevation" />
</androidx.cardview.widget.CardView>
CardView to simulate elevation effects.
Elevation and Shadows Summary
- Elevation creates a 3D-like depth effect.
- TranslationZ allows dynamic elevation changes during animations.
- CardView is a convenient way to apply elevation on older Android versions.
10. Working with WindowInsets
| Property | Description | Values |
|---|---|---|
| WindowInsets.Type.statusBars() | Represents the status bar area | Int |
| WindowInsets.Type.navigationBars() | Represents the navigation bar area | Int |
| WindowInsets.Type.ime() | Represents the on-screen input method area | Int |
| WindowInsets.Type.systemGestures() | Represents system gesture areas | Int |
| WindowInsets.Type.displayCutout() | Represents display cutouts (notch, camera hole) | Int |
Example: Requesting Insets in a View
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val view = findViewById<View>(R.id.main_view)
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
// Handle insets here
insets
}
}
}
Example: Using WindowInsetsCompat
val insets = ViewCompat.getRootWindowInsets(view)
insets?.let {
val systemGesturesInsets = it.getInsets(WindowInsetsCompat.Type.systemGestures())
val imeInsets = it.getInsets(WindowInsetsCompat.Type.ime())
}
WindowInsets is part of the AndroidX library and provides a more consistent way to handle insets across different Android versions.
11. Implementing Edge-to-Edge Design
Example: Enabling Edge-to-Edge in Android
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// Enable edge-to-edge design
val windowInsetsController = ViewCompat.getWindowInsetsController(window.decorView)
windowInsetsController?.hide(WindowInsetsCompat.Type.systemBars())
}
}
}
| Property | Description | Notes |
|---|---|---|
| WindowInsetsController | Controls how system UI insets are handled | Available from API level 30+ |
| systemBars() | Represents system bars (status bar, navigation bar) | Used to hide or show these UI elements |
| setDecorFitsSystemWindows() | Controls whether the window fits system windows | Set to false for edge-to-edge design |