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 |
|
| Button | Clickable element to trigger actions | text, onClick |
|
| ImageView | Displays images from resources or URLs | src, scaleType |
|
| EditText | Allows user input of text | hint, inputType, text |
|
| CheckBox | Boolean toggle for user selections | text, isChecked |
|
| Switch | Toggle switch for binary options | text, isChecked |
|
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
}
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: 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!"
}
}
}
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>
build.gradle file.
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
}
}
}
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" />
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" />
android:theme or app:theme.
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
)
}
}
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)
}
}
ViewGroup, you must override onMeasure() and onLayout() to define how children are measured and positioned.