Skip to main content

Building Traditional UIs

1. Creating Layouts

Layouts in Android define the structure and arrangement of UI elements. They are created using XML files in the res/layout directory.

Layout Type Description Use Case
LinearLayout Arranges views in a single direction (horizontal or vertical). Simple alignment of elements in one dimension.
RelativeLayout Positions views relative to each other or the parent. Flexible positioning without nested containers.
ConstraintLayout Allows complex layouts with constraints between views. Modern, efficient layout for complex UIs.
FrameLayout Stacks views on top of each other. Simple overlays or containers with one child.
GridLayout Arranges views in a grid of rows and columns. Uniform arrangement like a table or grid of items.

Example: LinearLayout with Vertical Orientation

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, World!" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />

</LinearLayout>

Example: ConstraintLayout with Basic Constraints

<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">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

2. Using Common Views

View Type Description Key Properties Usage Example
TextView Displays text on the screen text, textSize, textColor
<TextView
    android:text="Hello World"
    android:textSize="16sp"
    android:textColor="#000000" />
Button Clickable element to trigger actions text, onClick
<Button
    android:text="Click Me"
    android:onClick="onButtonClick" />
ImageView Displays images from resources or URLs src, scaleType
<ImageView
    android:src="@drawable/my_image"
    android:scaleType="centerCrop" />
EditText Allows user input of text hint, inputType, text
<EditText
    android:hint="Enter your name"
    android:inputType="text"
    android:text="" />
CheckBox Boolean toggle for user selections text, isChecked
<CheckBox
    android:text="Agree to terms"
    android:checked="false" />
Switch Toggle switch for binary options text, isChecked
<Switch
    android:text="Enable notifications"
    android:checked="true" />

Example: Layout with Common Views

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:text="Welcome"
        android:textSize="24sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <EditText
        android:hint="Enter your email"
        android:inputType="textEmailAddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:text="Submit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

3. Setting View Properties

Property Values Description
android:layout_width match_parent, wrap_content, 0dp Defines the width of the view
android:layout_height match_parent, wrap_content, 0dp Defines the height of the view
android:background @color/colorPrimary, #FF0000, ?attr/colorPrimary Background color or drawable resource
android:text "Hello World" Text content for TextViews
android:textSize 16sp, 20dp Font size in sp or dp units
android:textColor @color/white, #FFFFFF Color of the text content
android:visibility visible, gone, invisible Controls visibility of the view
android:padding 16dp Padding inside the view in dp units
android:margin 8dp Margin outside the view in dp units
android:gravity center, start, end Controls content alignment within the view

Example: Setting View Properties in XML

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello, Android!"
    android:textSize="18sp"
    android:textColor="#FFFFFF"
    android:background="@color/colorPrimary"
    android:padding="16dp"
    android:gravity="center" />

Example: Setting View Properties Programmatically

val textView = findViewById<TextView>(R.id.textView)
textView.text = "Hello, Kotlin!"
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
textView.setTextColor(Color.WHITE)
textView.setBackgroundColor(ContextCompat.getColor(this, R.color.colorPrimary))
textView.setPadding(16, 16, 16, 16)

4. Handling Click Events

Method Description Usage
setOnClickListener Registers a click listener for a view button.setOnClickListener { /* code */ }
onClickListener Property to set a click listener directly button.onClickListener = { /* code */ }

Example: Basic Click Handler

val button = findViewById<Button>(R.id.my_button)
button.setOnClickListener {
    Toast.makeText(this, "Button clicked!", Toast.LENGTH_SHORT).show()
}

Example: Handling Different Views

val button1 = findViewById<Button>(R.id.button1)
val button2 = findViewById<Button>(R.id.button2)

button1.setOnClickListener {
    // Handle button 1 click
}

button2.setOnClickListener {
    // Handle button 2 click
}
Note: In Android, setOnClickListener is the standard way to handle click events on UI components like buttons, TextViews, etc.

5. Using ViewBinding

ViewBinding is a feature in Android that allows you to more safely and efficiently access views in your layout files. It replaces the traditional findViewById approach, providing compile-time safety and reducing boilerplate code.

Feature Description Notes
ViewBinding Generates a binding class for each layout file Binding class name is derived from the layout file name (e.g., activity_main.xml → ActivityMainBinding)
Compile-time Safety Prevents runtime NullPointerException when accessing views View binding ensures that the view is present in the layout before accessing it
Performance Improves performance by avoiding findViewById calls No need to call findViewById at runtime, reducing overhead

Example: Enabling ViewBinding in a Module

android {
    buildFeatures {
        viewBinding = true
    }
}

Example: Using ViewBinding in an Activity

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
    }
}

Example: Accessing Views with ViewBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.textView.text = "Hello, ViewBinding!"
        binding.button.setOnClickListener {
            binding.textView.text = "Button clicked!"
        }
    }
}
Note: ViewBinding is part of the AndroidX library and requires Android Gradle Plugin version 3.6 or higher.

6. Using Data Binding

Data Binding is a powerful Android Jetpack library that allows you to bind UI components in your layout directly to data sources in your app using a declarative syntax. It reduces boilerplate code and improves performance by minimizing the need for manual view updates.

Feature Description Usage
Binding Expression Allows you to write expressions in XML that are evaluated at runtime. <TextView android:text="@{user.name}" />
View Binding Alternative to Data Binding that provides type-safe access to views. val binding = ActivityMainBinding.inflate(layoutInflater)
Observable Fields Used to notify UI when data changes. val name = ObservableField<String>()

Example: Basic Data Binding Setup

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.user = User("John", "Doe")
    }
}

Example: Layout with Data Binding

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.example.User" />
    </data>

    <TextView
        android:text="@{user.firstName}"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</layout>
Note: Data Binding requires enabling it in your build.gradle file.
Warning: Ensure that all variables used in binding expressions are properly initialized to avoid runtime errors.

7. Using LayoutInflater

Property Description Usage
LayoutInflater.from(context) Creates a LayoutInflater instance from the provided context val layoutInflater = LayoutInflater.from(context)
inflate(resourceId, parent, attachToRoot) Inflates a layout resource into a view hierarchy val view = layoutInflater.inflate(R.layout.item_layout, parent, false)

Example: Inflating a Layout

class CustomAdapter(
    private val context: Context,
    private val items: List<String>
) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = LayoutInflater.from(context)
        val view = layoutInflater.inflate(R.layout.list_item, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(items[position])
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: String) {
            itemView.findViewById<TextView>(R.id.textView).text = item
        }
    }
}
Note: The attachToRoot parameter should typically be false when inflating views for use in a RecyclerView or ListView to prevent unnecessary view hierarchy modifications.

8. Setting View Dimensions

Property Values Description
layout_width match_parent, wrap_content, 0dp Defines the width of the view
layout_height match_parent, wrap_content, 0dp Defines the height of the view

Example: Setting Fixed Dimensions

<TextView
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:text="Fixed Size" />

Example: Using Match Parent

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Full Width Button" />
Note: 0dp is used with ConstraintLayout to indicate a dimension should be calculated based on constraints.

9. Applying Styles and Themes

Property Values Description
android:theme Theme.AppCompat.Light Applies a predefined theme to the activity or application
android:style ?android:attr/buttonStyle Applies a style to a specific view element
app:theme ?attr/colorPrimary Applies a custom theme attribute from the style

Example: Applying a Custom Theme

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CustomTheme" parent="Theme.AppCompat.Light">
        <item name="colorPrimary">#FF5722</item>
        <item name="colorAccent">#FF4081</item>
    </style>
</resources>

Example: Using Theme Attributes in Layout

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Styled Button"
    style="?android:attr/buttonStyle"
    android:theme="@style/CustomTheme" />
Note: Theme attributes can be overridden in layout files using android:theme or app:theme.
Warning: Using android:theme on a view will apply the theme to that view and all its children.

Theme Application Summary

  • Application-wide themes are defined in res/values/themes.xml
  • View-specific themes can be applied using android:theme
  • Theme attributes can be accessed using ?attr/attributeName

10. Creating Custom Views

Custom views in Android allow developers to create reusable UI components with custom drawing and interaction logic. They extend the base View or ViewGroup classes.

Class Description Usage
View Base class for all UI components. Extend to create custom drawing logic.
ViewGroup Base class for containers that hold other views. Extend to create custom layouts or containers.

Example: Custom Circle 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 CircleView @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)
        val radius = minOf(width, height) / 2f
        canvas.drawCircle(
            width / 2f,
            height / 2f,
            radius - 10f,
            paint
        )
    }
}
Note: Custom views must override onDraw to draw their content and handle layout measurements using onMeasure.

Key Concepts

  • onDraw: Called to draw the view's content.
  • onMeasure: Determines the size of the view.
  • onTouchEvent: Handles touch input for interactivity.

11. Implementing ViewGroups

A ViewGroup is a special type of View that contains other View or ViewGroup objects, forming a tree structure that defines the UI layout. Implementing custom ViewGroups allows for complex and flexible UI arrangements that aren't possible with standard layouts.

Property Description Usage
childCount Number of child views in this group. val count = viewGroup.childCount
children Iterable of all child views in this group. for (child in viewGroup.children) { ... }
layoutParams Parameters that define how the view group is laid out. val params = ViewGroup.LayoutParams.MATCH_PARENT

Example: Custom ViewGroup with Layout Logic

class CustomViewGroup(context: Context, attrs: AttributeSet?) : ViewGroup(context, attrs) {

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        var y = 0
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            child.layout(0, y, child.measuredWidth, y + child.measuredHeight)
            y += child.measuredHeight
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var maxHeight = 0
        var totalWidth = 0

        for (i in 0 until childCount) {
            val child = getChildAt(i)
            measureChild(child, widthMeasureSpec, heightMeasureSpec)
            maxHeight = max(maxHeight, child.measuredHeight)
            totalWidth = max(totalWidth, child.measuredWidth)
        }

        setMeasuredDimension(totalWidth, maxHeight)
    }
}
Note: When implementing a custom ViewGroup, you must override onMeasure() and onLayout() to define how children are measured and positioned.

Key Concepts

  • onMeasure(): Determines the size of the view group and its children.
  • onLayout(): Positions the child views within the view group.
  • Custom ViewGroups provide flexibility for complex UIs not achievable with built-in layouts.

Was this article helpful?