← Back to Tutorials Chapter 7

Event Handling

Event Listeners

Android uses event listeners to handle user interactions. The common pattern is to set a listener on a view.

// Lambda syntax (Kotlin)
val button: Button = findViewById(R.id.myButton)
button.setOnClickListener {
    Toast.makeText(this, "Button clicked!", Toast.LENGTH_SHORT).show()
}

// Anonymous inner class
button.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        // Handle click
    }
})

// Activity implements listener
class MainActivity : AppCompatActivity(), View.OnClickListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        findViewById<Button>(R.id.myButton).setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.myButton -> handleButtonClick()
        }
    }
}

Touch Events

// Set touch listener on a view
view.setOnTouchListener { v, event ->
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            println("Touch down at (${event.x}, ${event.y})")
            true
        }
        MotionEvent.ACTION_MOVE -> {
            println("Moving at (${event.x}, ${event.y})")
            true
        }
        MotionEvent.ACTION_UP -> {
            println("Touch up")
            true
        }
        else -> false
    }
}

// Custom View with touch handling
class DrawingView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    private val path = Path()
    private val paint = Paint().apply {
        color = Color.BLACK
        strokeWidth = 8f
        style = Paint.Style.STROKE
        strokeCap = Paint.Cap.ROUND
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> path.moveTo(event.x, event.y)
            MotionEvent.ACTION_MOVE -> path.lineTo(event.x, event.y)
            else -> return false
        }
        invalidate() // Redraw
        return true
    }

    override fun onDraw(canvas: Canvas) {
        canvas.drawPath(path, paint)
    }
}

Gesture Detection

class GestureActivity : AppCompatActivity() {
    private lateinit var gestureDetector: GestureDetectorCompat

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        gestureDetector = GestureDetectorCompat(this, GestureListener())
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
    }

    inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
        override fun onFling(
            e1: MotionEvent?, e2: MotionEvent?,
            velocityX: Float, velocityY: Float
        ): Boolean {
            val dx = e2?.x?.minus(e1?.x ?: 0f) ?: 0f
            if (Math.abs(dx) > 100 && Math.abs(velocityX) > 200) {
                if (dx > 0) {
                    Toast.makeText(this@GestureActivity, "Swiped Right", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this@GestureActivity, "Swiped Left", Toast.LENGTH_SHORT).show()
                }
                return true
            }
            return false
        }

        override fun onDoubleTap(e: MotionEvent?): Boolean {
            Toast.makeText(this@GestureActivity, "Double Tap", Toast.LENGTH_SHORT).show()
            return true
        }
    }
}

Keyboard and Focus

// Show keyboard
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)

// Hide keyboard
imm.hideSoftInputFromWindow(currentFocus?.windowToken, 0)

// Handle keyboard enter action
editText.setOnEditorActionListener { v, actionId, event ->
    if (actionId == EditorInfo.IME_ACTION_DONE) {
        handleSearch(editText.text.toString())
        true
    } else false
}

// Listen for focus changes
editText.setOnFocusChangeListener { v, hasFocus ->
    if (hasFocus) {
        // EditText gained focus
    } else {
        // EditText lost focus
    }
}

Drag & Drop

// Start drag
imageView.setOnLongClickListener { v ->
    val data = ClipData.newPlainText("image", "dragged_image")
    v.startDragAndDrop(data, View.DragShadowBuilder(v), null, 0)
    true
}

// Receive drop
targetView.setOnDragListener { v, event ->
    when (event.action) {
        DragEvent.ACTION_DRAG_STARTED -> {
            v.alpha = 0.5f
            true
        }
        DragEvent.ACTION_DROP -> {
            v.alpha = 1.0f
            // Handle drop
            Toast.makeText(context, "Item dropped!", Toast.LENGTH_SHORT).show()
            true
        }
        DragEvent.ACTION_DRAG_ENDED -> {
            v.alpha = 1.0f
            true
        }
        else -> true
    }
}
Exercise: Create a drawing app using custom View and touch events. The user should be able to draw with their finger, change the stroke color via a SeekBar, and clear the canvas with a button. Add swipe detection to show a "Clear?" confirmation dialog.