Невозможно выделить текст в пользовательском подклассе EditText - PullRequest
0 голосов
/ 03 июля 2019

Я пытаюсь создать свой собственный подкласс EditText, но я столкнулся с проблемами, из-за которых я потерял много функциональных возможностей, которые имел оригинальный EditText, и я не уверен, почему. EditText не реагирует на длительные нажатия, и курсор не может быть перемещен назад без использования возврата. Клавиатура также не открывалась при выборе EditText, но я смог найти обходной путь для этой проблемы с помощью InputMethodManager.

Вот код для моего класса EditText:

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.widget.EditText
import android.animation.ObjectAnimator
import android.text.*
import android.util.Log
import androidx.core.content.ContextCompat
import android.view.KeyEvent
import com.changeroom.android.R
import android.util.TypedValue
import android.view.inputmethod.InputMethodManager
import android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT
import kotlin.math.roundToInt


class FloatingHintEditText : EditText {

    //controls whether or not hints are displayed in all caps
    private var hintCaps = false

    //how much space to leave at bottom of edittext
    private val bottomSpacing = resources.getDimensionPixelSize(R.dimen.inner_components_spacing)

    //controls the color of error message text
    private var errorColor = ContextCompat.getColor(context, R.color.errorRed)

    //controls the size of the error message
    private val bottomTextSize = 0.75f

    //stores the text to go underneath
    var bottomText: CharSequence? = null
        private set

    //controls the style of the floating hint
    private val hintPaint = Paint().apply {
        this.color = (this@FloatingHintEditText).currentHintTextColor
        this.textSize = (this@FloatingHintEditText).textSize
        this.typeface = (this@FloatingHintEditText).typeface
        this.strokeWidth = 2f
    }

    //controls the style of the bottom text
    private val bottomTextPaint = TextPaint().apply {
        this.color = errorColor
        this.textSize = (this@FloatingHintEditText).textSize * 0.75f
        this.typeface = (this@FloatingHintEditText).typeface
    }

    //stores whether the text is a password field
    private var isPassword =
        inputType and InputType.TYPE_NUMBER_VARIATION_PASSWORD == InputType.TYPE_NUMBER_VARIATION_PASSWORD
                || inputType and InputType.TYPE_TEXT_VARIATION_PASSWORD == InputType.TYPE_TEXT_VARIATION_PASSWORD
                || inputType and InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD


    //stores values for animating
    private val errorAnimDuration = 100L
    private val focusAnimDuration = 100L
    var floatingLabelFraction = 1f
    var hintSize = 1f
    var mainColorController = currentHintTextColor
    var errorHeight = 1f
    private val errorFocusAnimator = ObjectAnimator.ofFloat(this, "errorHeight", 1f, 0f).apply {
        duration = errorAnimDuration
    }
    private val labelFocusAnimator = ObjectAnimator.ofFloat(this, "floatingLabelFraction", 1f, 0f).apply {
        duration = focusAnimDuration
    }
    private val labelSizeAnimator = ObjectAnimator.ofFloat(this, "hintSize", 1f, 0.75f).apply {
        duration = focusAnimDuration
    }

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        initializeAttrs(attrs)
    }
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initializeAttrs(attrs)
    }

    private fun initializeAttrs(attrs: AttributeSet) {
        context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.FloatingHintEditText,
            0, 0).apply {

            try {
                errorColor = getColor(
                    R.styleable.FloatingHintEditText_errorColor, ContextCompat.getColor(context,
                        R.color.errorRed
                    ))
                highlightColor = getColor(
                    R.styleable.FloatingHintEditText_highlightColor, ContextCompat.getColor(context,
                        R.color.colorAccent
                    ))
                hintCaps = getBoolean(
                    R.styleable.FloatingHintEditText_hintCaps, false
                )
            } finally {
                bottomTextPaint.color = errorColor
                recycle()
            }
        }
    }

    init {
        setPadding(paddingLeft, paddingTop, paddingRight, getPixel(8) + paddingBottom)

        setOnFocusChangeListener { _, _ ->
            val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
            imm!!.showSoftInput(this, SHOW_IMPLICIT)
            if (text.isNotEmpty() || bottomText != null) {
                //do nothing
            }
            else if (isFocused) {
                labelSizeAnimator.start()
                labelFocusAnimator.start()
            } else {
                labelSizeAnimator.reverse()
                labelFocusAnimator.reverse()
            }

            if (isFocused && bottomText == null) {
                mainColorController = highlightColor
            }
            else if (bottomText == null) {
                mainColorController = currentHintTextColor
            }
        }
    }

    override fun onDraw(canvas: Canvas?) {

        val startX = scrollX + paddingStart
        val endX = width + scrollX - paddingEnd

        //draw the bottom text (if there is any)
        if (bottomText != null) {
            bottomTextPaint.textSize = textSize * bottomTextSize
            bottomTextPaint.color = mainColorController

            //calculate the position of the bottom message
            val bottomTextStartX = if (layoutDirection == LAYOUT_DIRECTION_RTL) endX else startX
            val bottomTextStartY = height.toFloat() - errorHeight * textSize * bottomTextSize / 3 - textSize * bottomTextSize / 3

            //draw the bottom message
            canvas?.drawText(bottomText.toString(), bottomTextStartX.toFloat(), bottomTextStartY, bottomTextPaint)
        }

        //set hint/underline colour
        hintPaint.color = mainColorController

        //draw the underline
        val underlineY = height.toFloat() - textSize * bottomTextSize - bottomSpacing
        canvas?.drawLine(startX.toFloat(), underlineY, endX.toFloat(), underlineY, hintPaint)

        hintPaint.textSize = textSize * hintSize

        val hintText = if (hintCaps) hint?.toString()?.toUpperCase() ?: "" else hint?.toString() ?: ""

        // calculate the position of the floating hint
        val floatingLabelStartX: Float = if (layoutDirection == LAYOUT_DIRECTION_RTL) endX.toFloat() else startX.toFloat()
        val floatingLabelStartY = (height / 2) * floatingLabelFraction + (textSize * hintSize) * (1 - floatingLabelFraction) + scrollY

        //draw the hint
        canvas?.drawText(hintText, floatingLabelStartX, floatingLabelStartY, hintPaint)

        // makes EditText not draw hint
        val tempHint = hint
        hint = null
        super.onDraw(canvas)
        hint = tempHint
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val width = measuredWidth
        val height = measuredHeight + textSize * 2.2
        setMeasuredDimension(width, height.toInt())
    }

    fun setBottomText(msg: CharSequence?, isError: Boolean) {
        bottomText = msg
        if (isError) {
            if (!isFocused && text?.isEmpty() == true && bottomText != null) {
                labelSizeAnimator.start()
                labelFocusAnimator.start()
            } else if (text?.isEmpty() == true && !isFocused) {
                labelSizeAnimator.reverse()
                labelFocusAnimator.reverse()
            }
            if (bottomText != null) {
                val errorColorAnimator = ObjectAnimator.ofArgb(
                    this, "mainColorController",
                    mainColorController, errorColor
                ).apply {
                    duration = errorAnimDuration
                }
                errorColorAnimator.start()
                errorFocusAnimator.start()
            } else {
                val reverseErrorColourAnimator = ObjectAnimator.ofArgb(
                    this, "mainColorController",
                    if (isFocused) highlightColor else currentHintTextColor
                ).apply {
                    duration = errorAnimDuration
                }
                reverseErrorColourAnimator.start()
            }
        }
        else {
            mainColorController = currentHintTextColor
        }
    }


    override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
        if (event.keyCode == KeyEvent.KEYCODE_BACK){
            this.clearFocus()
        }
        return super.onKeyPreIme(keyCode, event)
    }

    private fun getPixel(dp: Int): Int {
        val r = context.resources
        val px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), r.displayMetrics)
        return px.roundToInt()
    }
}

Любая помощь будет оценена.

1 Ответ

0 голосов
/ 04 июля 2019

Оказывается, проблема заключалась в том, что я устанавливал подсказку в null перед вызовом super.onDraw (), а затем сбрасывал ее на прежнее значение.В итоге я просто создал отдельное поле для хранения плавающей метки, и это исправило его для меня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...