Kotlin for Android

Build modern Android apps with Kotlin's powerful features

šŸ“± What is Kotlin for Android?

Kotlin is Google's preferred language for Android development. It offers modern syntax, null safety, and seamless Java interoperability, making Android app development faster, safer, and more enjoyable than ever before.


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            Toast.makeText(this, "Hello Kotlin!", Toast.LENGTH_SHORT).show()
        }
    }
}
                                    

Android Development Features

šŸ›”ļø

Null Safety

Eliminate null pointer exceptions

val name: String? = null
name?.let { println(it) }
šŸ”—

View Binding

Type-safe access to views

binding.textView.text = "Hello"
binding.button.setOnClickListener { }
⚔

Coroutines

Asynchronous programming for UI

lifecycleScope.launch {
    val data = fetchData()
    updateUI(data)
}
šŸ—ļø

Data Classes

Perfect for model objects

data class User(
    val id: Int,
    val name: String
)

šŸ”¹ Basic Activity with Kotlin

Create a simple Android activity using Kotlin:

import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    
    private lateinit var welcomeText: TextView
    private lateinit var clickButton: Button
    private var clickCount = 0
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // Initialize views
        welcomeText = findViewById(R.id.welcomeText)
        clickButton = findViewById(R.id.clickButton)
        
        // Set initial text
        welcomeText.text = "Welcome to Kotlin Android!"
        
        // Set click listener
        clickButton.setOnClickListener {
            handleButtonClick()
        }
    }
    
    private fun handleButtonClick() {
        clickCount++
        val message = when {
            clickCount == 1 -> "First click!"
            clickCount < 5 -> "Click #$clickCount"
            clickCount == 5 -> "You've clicked 5 times!"
            else -> "Click count: $clickCount"
        }
        
        welcomeText.text = message
        Toast.makeText(this, "Button clicked $clickCount times", Toast.LENGTH_SHORT).show()
    }
}

Features Demonstrated:

• lateinit for delayed initialization

• String templates with $variable

• when expression for conditional logic

• Lambda expressions for click listeners

• Null-safe findViewById calls

šŸ”¹ Data Classes for Models

Use data classes to represent your app's data:

// User model
data class User(
    val id: Int,
    val name: String,
    val email: String,
    val isActive: Boolean = true
) {
    fun getDisplayName(): String = name.uppercase()
}

// Product model with validation
data class Product(
    val id: Int,
    val name: String,
    val price: Double,
    val category: String
) {
    val isExpensive: Boolean
        get() = price > 100.0
    
    fun getFormattedPrice(): String = "$%.2f".format(price)
}

// Usage in Activity or Fragment
class ProductActivity : AppCompatActivity() {
    
    private val products = listOf(
        Product(1, "Laptop", 999.99, "Electronics"),
        Product(2, "Coffee Mug", 12.50, "Kitchen"),
        Product(3, "Smartphone", 699.99, "Electronics")
    )
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_product)
        
        displayProducts()
    }
    
    private fun displayProducts() {
        val expensiveProducts = products.filter { it.isExpensive }
        val electronicProducts = products.filter { it.category == "Electronics" }
        
        println("Expensive products:")
        expensiveProducts.forEach { product ->
            println("${product.name}: ${product.getFormattedPrice()}")
        }
        
        println("Electronic products: ${electronicProducts.size}")
    }
}

Output:

Expensive products:

Laptop: $999.99

Smartphone: $699.99

Electronic products: 2

šŸ”¹ View Binding Example

Use View Binding for type-safe view access:

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.myapp.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityMainBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Initialize view binding
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupViews()
    }
    
    private fun setupViews() {
        // Type-safe access to views
        binding.titleText.text = "Welcome to Kotlin!"
        binding.subtitleText.text = "Building amazing Android apps"
        
        // Set click listeners
        binding.primaryButton.setOnClickListener {
            handlePrimaryAction()
        }
        
        binding.secondaryButton.setOnClickListener {
            handleSecondaryAction()
        }
        
        // Update UI based on data
        val user = User(1, "John Doe", "[email protected]")
        updateUserInfo(user)
    }
    
    private fun handlePrimaryAction() {
        binding.statusText.text = "Primary action clicked!"
        binding.primaryButton.isEnabled = false
        
        // Re-enable after delay (in real app, use coroutines)
        binding.primaryButton.postDelayed({
            binding.primaryButton.isEnabled = true
            binding.statusText.text = "Ready for action"
        }, 2000)
    }
    
    private fun handleSecondaryAction() {
        val currentText = binding.titleText.text.toString()
        binding.titleText.text = if (currentText.contains("!")) {
            currentText.replace("!", "")
        } else {
            "$currentText!"
        }
    }
    
    private fun updateUserInfo(user: User) {
        binding.userNameText.text = "Hello, ${user.name}"
        binding.userEmailText.text = user.email
        binding.userStatusIcon.setImageResource(
            if (user.isActive) R.drawable.ic_active else R.drawable.ic_inactive
        )
    }
}

View Binding Benefits:

  • Type Safety: No more ClassCastException
  • Null Safety: Views are guaranteed to exist
  • Performance: No findViewById() calls needed
  • Code Completion: IDE can suggest view names

šŸ”¹ Coroutines in Android

Handle asynchronous operations with coroutines:

import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*

class UserProfileActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityUserProfileBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityUserProfileBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        loadUserProfile()
    }
    
    private fun loadUserProfile() {
        // Show loading state
        binding.progressBar.visibility = View.VISIBLE
        binding.contentLayout.visibility = View.GONE
        
        // Launch coroutine in lifecycle scope
        lifecycleScope.launch {
            try {
                // Simulate network calls
                val user = fetchUserData()
                val posts = fetchUserPosts(user.id)
                val followers = fetchFollowers(user.id)
                
                // Update UI on main thread
                displayUserProfile(user, posts, followers)
                
            } catch (e: Exception) {
                showError("Failed to load profile: ${e.message}")
            } finally {
                binding.progressBar.visibility = View.GONE
            }
        }
    }
    
    private suspend fun fetchUserData(): User = withContext(Dispatchers.IO) {
        delay(1000) // Simulate network delay
        User(1, "Alice Johnson", "[email protected]", true)
    }
    
    private suspend fun fetchUserPosts(userId: Int): List = withContext(Dispatchers.IO) {
        delay(800)
        listOf("My first post!", "Learning Kotlin", "Android development is fun")
    }
    
    private suspend fun fetchFollowers(userId: Int): Int = withContext(Dispatchers.IO) {
        delay(600)
        1250 // Follower count
    }
    
    private fun displayUserProfile(user: User, posts: List, followerCount: Int) {
        binding.contentLayout.visibility = View.VISIBLE
        
        binding.userName.text = user.name
        binding.userEmail.text = user.email
        binding.followerCount.text = "$followerCount followers"
        binding.postCount.text = "${posts.size} posts"
        
        // Display recent posts
        val recentPosts = posts.take(3).joinToString("\n• ", "• ")
        binding.recentPosts.text = recentPosts
    }
    
    private fun showError(message: String) {
        binding.contentLayout.visibility = View.GONE
        binding.errorText.text = message
        binding.errorText.visibility = View.VISIBLE
    }
}

Coroutines Benefits:

• Non-blocking UI operations

• Automatic lifecycle management

• Easy error handling with try-catch

• Parallel execution with async/await

• Context switching (Main, IO, Default)

šŸ”¹ Extension Functions for Android

Create useful extensions for Android development:

import android.content.Context
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment

// Context extensions
fun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}

fun Context.showLongToast(message: String) {
    showToast(message, Toast.LENGTH_LONG)
}

// View extensions
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

fun View.invisible() {
    visibility = View.INVISIBLE
}

// String extensions for validation
fun String.isValidEmail(): Boolean {
    return android.util.Patterns.EMAIL_ADDRESS.matcher(this).matches()
}

fun String.isValidPhone(): Boolean {
    return android.util.Patterns.PHONE.matcher(this).matches()
}

// Usage in Activity
class LoginActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityLoginBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityLoginBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupLoginForm()
    }
    
    private fun setupLoginForm() {
        binding.loginButton.setOnClickListener {
            val email = binding.emailInput.text.toString()
            val password = binding.passwordInput.text.toString()
            
            if (validateInput(email, password)) {
                performLogin(email, password)
            }
        }
    }
    
    private fun validateInput(email: String, password: String): Boolean {
        return when {
            email.isEmpty() -> {
                showToast("Please enter email")
                false
            }
            !email.isValidEmail() -> {
                showToast("Please enter valid email")
                false
            }
            password.length < 6 -> {
                showLongToast("Password must be at least 6 characters")
                false
            }
            else -> true
        }
    }
    
    private fun performLogin(email: String, password: String) {
        // Show loading
        binding.progressBar.show()
        binding.loginButton.hide()
        
        lifecycleScope.launch {
            try {
                delay(2000) // Simulate login
                showToast("Login successful!")
                // Navigate to main activity
            } catch (e: Exception) {
                showToast("Login failed: ${e.message}")
            } finally {
                binding.progressBar.hide()
                binding.loginButton.show()
            }
        }
    }
}

Extension Benefits:

• Cleaner, more readable code

• Reusable utility functions

• Better code organization

• Enhanced existing Android classes

• Improved developer productivity

🧠 Test Your Knowledge

What is Google's preferred language for Android development?