Saturday, August 29, 2020

android kotlin - EditText change cursor color programmatically

MainActivity.kt

package com.example.jetpack

import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.os.Bundle
import android.util.TypedValue
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_main.*
import java.lang.reflect.Field


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // change edit text cursor color programmatically
        editText.setCursorColor(applicationContext, Color.RED)
        editText2.setCursorColor(applicationContext, Color.GREEN)
    }
}


// extension function to set/change edit text cursor color programmatically
fun EditText.setCursorColor(context: Context, color: Int){
    val editText = this

    val shapeDrawable = GradientDrawable().apply {
        shape = GradientDrawable.RECTANGLE
        setSize(2.dpToPixels(context), 0)
        setColor(color)
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        textCursorDrawable = shapeDrawable
    }else{
        try {
            // get the cursor resource id
            TextView::class.java.getDeclaredField("mCursorDrawableRes").apply {
                isAccessible = true
                val drawableResId: Int = getInt(editText)

                // get the editor
                val editorField: Field = TextView::class.java
                    .getDeclaredField("mEditor")
                editorField.isAccessible = true
                val editor: Any = editorField.get(editText)

                // get the drawable and set a color filter
                val drawable: Drawable? = ContextCompat
                    .getDrawable(editText.context, drawableResId)
                drawable?.setColorFilter(color, PorterDuff.Mode.SRC_IN)

                // set the drawables
                editor.javaClass.getDeclaredField("mCursorDrawable").apply {
                    isAccessible = true
                    set(editor, arrayOf(drawable,drawable))
                }
            }
        } catch (e: Exception) {
            // log exception here
        }
    }
}


// extension function to convert dp to equivalent pixels
fun Int.dpToPixels(context: Context):Int = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics
).toInt()
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/editText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:inputType="text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.12" />

    <EditText
        android:id="@+id/editText2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="24dp"
        android:layout_marginEnd="8dp"
        android:inputType="text"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText" />

</androidx.constraintlayout.widget.ConstraintLayout>