Skip to main content

Handling User Input

1. Working with EditText

Property Description Values
android:text Initial text content "Hello World"
android:hint Placeholder text when empty "Enter text here"
android:inputType Keyboard type and behavior "text", "number", "email"
android:maxLength Maximum number of characters 100
android:enabled Whether the view is interactive true, false

Example: Setting up EditText in XML

<EditText
    android:id="@+id/editText"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Initial text"
    android:hint="Enter your name"
    android:inputType="text"
    android:maxLength="50"/>

Example: Reading input from EditText in Kotlin

val editText = findViewById<EditText>(R.id.editText)
val userInput = editText.text.toString()

Example: Setting text programmatically

val editText = findViewById<EditText>(R.id.editText)
editText.setText("New text")
Note: Use editText.text.toString() to get the user input as a string.
Warning: Never use editText.text directly in logic, as it returns an Editable object.

2. Implementing TextWatcher

Method Description Usage
afterTextChanged Called after the text has been changed
override fun afterTextChanged(s: Editable?) {
    // Handle text change
}
beforeTextChanged Called before the text is changed
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    // Prepare for text change
}
onTextChanged Called when the text is changed
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    // Handle text change in real-time
}

Example: TextWatcher Implementation

class MyTextWatcher : TextWatcher {
    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        // Prepare for text change
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        // Handle text change in real-time
    }

    override fun afterTextChanged(s: Editable?) {
        // Handle text change after it's applied
    }
}

val editText = findViewById<EditText>(R.id.editText)
editText.addTextChangedListener(MyTextWatcher())
Note: TextWatcher is commonly used for real-time validation or auto-saving.

3. Using Input Validation

Example: Basic Input Validation

fun validateEmail(email: String): Boolean {
    val emailRegex = Regex("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\\.[A-Za-z]{2,})$")
    return emailRegex.matches(email)
}

fun validatePhone(phone: String): Boolean {
    val phoneRegex = Regex("^\\+?[1-9]\\d{1,14}$")
    return phoneRegex.matches(phone)
}

fun validateAge(age: String): Boolean {
    return age.toIntOrNull() != null && age.toInt() in 1..120
}
Validation Type Method Description Example
Email Regex Checks email format using regex pattern validateEmail("user@example.com")
Phone Number Regex Validates international phone number format validatePhone("+1234567890")
Age toIntOrNull() Checks if input is numeric and within valid range validateAge("25")
Note: Always validate input on both client and server side for security.
Warning: Never trust user input without validation. Always sanitize and escape.

Input Validation Best Practices

  • Validate all user inputs before processing
  • Use regex patterns for structured data like emails and phone numbers
  • Implement server-side validation as a backup to client-side checks
  • Define clear error messages for invalid inputs
  • Use built-in validation libraries where available (e.g., AndroidX Validation)

4. Handling Soft Keyboard

Property Description Values
imeOptions Controls the action button on the soft keyboard actionDone, actionGo, actionNext, actionSearch, actionSend
inputType Defines the type of input expected (e.g., text, number) text, number, phone, email, textMultiLine

Example: Setting Keyboard Options

val editText = findViewById<EditText>(R.id.editText)
editText.imeOptions = EditorInfo.IME_ACTION_DONE
editText.inputType = InputType.TYPE_CLASS_TEXT
Note: Use imeOptions to customize the action button's label and behavior.
Warning: Ensure inputType matches the expected input to provide appropriate keyboard layout.

5. Working with CheckBox and RadioButton

Property Description Values
isChecked Indicates whether the checkbox/radio button is checked Boolean
text Text displayed next to the checkbox/radio button String
onCheckedChange Callback when the state changes (Boolean) -> Unit

Example: CheckBox Usage

val checkBox = CheckBox(context)
checkBox.text = "Accept Terms"
checkBox.isChecked = false
checkBox.setOnCheckedChangeListener { _, isChecked ->
    if (isChecked) {
        // Handle checked state
    } else {
        // Handle unchecked state
    }
}

Example: RadioButton Usage

val radioButton = RadioButton(context)
radioButton.text = "Option 1"
radioButton.isChecked = false
radioButton.setOnCheckedChangeListener { _, isChecked ->
    if (isChecked) {
        // Handle selection
    }
}
Note: For grouping RadioButtons, use RadioGroup to ensure only one is selected at a time.

CheckBox vs RadioButton

  • CheckBox: Allows multiple selections
  • RadioButton: Allows only one selection from a group

6. Using Spinner

Property Description Values
android:entries Defines the array of strings to be used as spinner items @array/...
android:prompt Text to display when the spinner is empty @string/...
android:spinnerMode Mode of the spinner (dialog or dropdown) dialog, dropdown

Example: Basic Spinner Setup

<Spinner
    android:id="@+id/spinner"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:entries="@array/choices"
    android:prompt="@string/spinner_prompt" />

Example: Spinner with ArrayAdapter

val choices = arrayOf("Option 1", "Option 2", "Option 3")
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, choices)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
Note: The spinnerMode attribute affects how the spinner appears on different devices.

7. Implementing Date and Time Pickers

Component Description Usage
DatePickerDialog Dialog for selecting a date Use to allow users to pick a date from a calendar view
TimePickerDialog Dialog for selecting a time Use to allow users to pick a time (hours and minutes)

Example: Using DatePickerDialog

val datePicker = DatePickerDialog(this, { _, year, month, dayOfMonth ->
    // Handle date selection
    val selectedDate = "$year-${month + 1}-$dayOfMonth"
}, 2024, 5, 15)

datePicker.show()

Example: Using TimePickerDialog

val timePicker = TimePickerDialog(this, { _, hourOfDay, minute ->
    // Handle time selection
    val selectedTime = "$hourOfDay:$minute"
}, 14, 30, true)

timePicker.show()
Note: TimePickerDialog supports both 12-hour and 24-hour formats. Set the last parameter to true for 24-hour format.

Date and Time Picker Features

  • NEW Support for custom date formats
  • Easy integration with Calendar objects
  • Localization support for different date/time formats

8. Working with SeekBar and Slider

Property Description Values
max Maximum value the slider can reach Int
progress Current position of the slider Int
thumb Custom drawable for the slider thumb Drawable
onProgressChangeListener Callback when progress changes OnSeekBarChangeListener

Example: Basic SeekBar Usage

val seekBar = findViewById<SeekBar>(R.id.seekBar)
seekBar.max = 100
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
        Log.d("SeekBar", "Progress: $progress")
    }

    override fun onStartTrackingTouch(seekBar: SeekBar?) {}

    override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})

Example: Custom Slider with Thumb Drawable

val slider = findViewById<Slider>(R.id.slider)
slider.valueFrom = 0f
slider.valueTo = 100f
slider.stepSize = 1f
slider.thumbDrawable = ContextCompat.getDrawable(this, R.drawable.custom_thumb)
Note: Slider is part of AndroidX Material Components and requires the dependency com.google.android.material:material.
Warning: Ensure that max is set before setting progress to avoid unexpected behavior.

9. Handling Touch Events

Event Type Description Usage
MotionEvent.ACTION_DOWN First finger touches the screen Start of gesture recognition
MotionEvent.ACTION_MOVE Finger moves on the screen Track movement during gesture
MotionEvent.ACTION_UP Last finger lifts off the screen End of gesture, trigger action
MotionEvent.ACTION_CANCEL Gesture is cancelled (e.g., system interrupt) Reset or cancel ongoing touch handling

Example: Basic Touch Event Handling

class MainActivity : AppCompatActivity() {
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                // Handle touch start
                Log.d("Touch", "Down at (${event.x}, ${event.y})")
            }
            MotionEvent.ACTION_MOVE -> {
                // Handle touch movement
                Log.d("Touch", "Moving to (${event.x}, ${event.y})")
            }
            MotionEvent.ACTION_UP -> {
                // Handle touch end
                Log.d("Touch", "Up at (${event.x}, ${event.y})")
            }
        }
        return true
    }
}
Note: Returning true from onTouchEvent indicates that the event was consumed and should not be passed to other views.

Example: Detecting Swipe Gesture

class SwipeDetector(private val listener: OnSwipeListener) : View.OnTouchListener {
    private var startX = 0f
    private var startY = 0f

    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                startX = event.x
                startY = event.y
            }
            MotionEvent.ACTION_UP -> {
                val endX = event.x
                val endY = event.y
                val deltaX = endX - startX
                val deltaY = endY - startY

                if (abs(deltaX) > abs(deltaY)) {
                    if (deltaX > 50) listener.onSwipeRight()
                    else if (deltaX < -50) listener.onSwipeLeft()
                } else {
                    if (deltaY > 50) listener.onSwipeDown()
                    else if (deltaY < -50) listener.onSwipeUp()
                }
            }
        }
        return true
    }

    interface OnSwipeListener {
        fun onSwipeLeft()
        fun onSwipeRight()
        fun onSwipeUp()
        fun onSwipeDown()
    }
}

Touch Event Lifecycle

  • ACTION_DOWN: Initial touch point detected
  • ACTION_MOVE: Finger moved on screen
  • ACTION_UP: Last finger lifted off screen
  • ACTION_CANCEL: Gesture interrupted or cancelled

10. Implementing Gesture Detection

Gesture Detector Class Description Usage
Single Tap GestureDetector.SimpleOnGestureListener Detects single tap events
val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
    override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
        // Handle single tap
        return true
    }
})
Double Tap GestureDetector.SimpleOnGestureListener Detects double tap events
override fun onDoubleTap(e: MotionEvent): Boolean {
    // Handle double tap
    return true
}
Scroll GestureDetector.SimpleOnGestureListener Detects scroll (fling) gestures
override fun onScroll(
    e1: MotionEvent,
    e2: MotionEvent,
    distanceX: Float,
    distanceY: Float
): Boolean {
    // Handle scroll
    return true
}
Fling GestureDetector.SimpleOnGestureListener Detects fling gestures (quick swipe)
override fun onFling(
    e1: MotionEvent,
    e2: MotionEvent,
    velocityX: Float,
    velocityY: Float
): Boolean {
    // Handle fling
    return true
}

Example: Detecting Swipe Gesture

class SwipeGestureDetector : GestureDetector.SimpleOnGestureListener() {
    private val SWIPE_THRESHOLD = 100
    private val SWIPE_VELOCITY_THRESHOLD = 100

    override fun onFling(
        e1: MotionEvent,
        e2: MotionEvent,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
        val diffX = e2.x - e1.x
        val diffY = e2.y - e1.y

        if (abs(diffX) > SWIPE_THRESHOLD && abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
            if (diffX > 0) {
                // Swipe right
            } else {
                // Swipe left
            }
            return true
        }
        return false
    }
}
Note: Use onFling for swipe detection and onScroll for continuous scroll gestures.

11. Using ScaleGestureDetector

Property Description Notes
ScaleGestureDetector Class for detecting scale gestures (pinch-to-zoom) on touch screens. Part of Android's gesture detection system.
onTouchEvent(MotionEvent) Method to pass touch events to the detector. Must be called from the view's onTouchEvent method.
isInProgress() Checks if a scale gesture is currently in progress. Useful for determining whether to process the event further.

Example: Basic ScaleGestureDetector Usage

class ZoomableImageView : ImageView() {
    private val scaleDetector = ScaleGestureDetector(context, ScaleListener())

    override fun onTouchEvent(event: MotionEvent): Boolean {
        scaleDetector.onTouchEvent(event)
        return true
    }

    private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            val scale = detector.scaleFactor
            // Apply zoom transformation to the view
            scaleX *= scale
            scaleY *= scale
            return true
        }
    }
}
Note: The ScaleGestureDetector works best with multi-touch input and requires handling of the MotionEvent in a custom view.

Was this article helpful?