Я пытаюсь создать свой собственный подкласс 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()
}
}
Любая помощь будет оценена.