android - 自定义视图具有隐藏实际美学的背景
问题描述
我正在开发一个应用程序屏幕,用户必须输入他们将通过 SMS 接收的 OTP 代码。我想要的外观如下(请注意,屏幕的其余部分看起来像预期的那样并且没有问题,所以我只会显示什么不起作用):
如您所见,此设计具有单独的圆角矩形,其中包含来自 OTP 代码的单个字符。对于我的问题,所有颜色和字体都不重要(因为它们可以在资源 xml 文件中进行修改。
然而,我当前的代码并没有像我预期的那样呈现出来,如下所示:
这是与视图关联的 kotlin 代码以及关联的attrs.xml
和dimens.xml
文件:
OtpTextField.kt
package com.example.myapp.widget
import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.PaintDrawable
import android.text.InputFilter
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import com.example.myapp.Event
import com.example.myapp.util.extension.dismissKeyboard
import com.example.myapp.R
import com.example.myapp.databinding.ViewOtpEdittextBinding
class OtpTextField(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
private var binding: ViewOtpEdittextBinding =
ViewOtpEdittextBinding.inflate(LayoutInflater.from(context), this)
var length: Int = -1
set(value) {
if (field != value) {
field = value
applyLength()
}
}
var otp: String
get() = binding.otpInput.text?.toString().orEmpty()
set(value) {
binding.otpInput.setText(value)
}
var cellWidth: Int = -1
var cellHeight: Int = -1
var cellSpacing: Int = -1
var cellCornerRadius: Int = -1
var cellBackgroundColor: Int = -1
private var otpDisplayTexts: List<TextView> = emptyList()
private var _otpEntered = MediatorLiveData<Event<Unit>>()
val otpEntered: LiveData<Event<Unit>> = _otpEntered
init {
applyUserAttributes(context, attrs)
bindEditTextToDisplay()
}
private fun applyUserAttributes(context: Context, attrs: AttributeSet?) {
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.OtpTextField, 0, 0)
length =
typedArray.getInt(R.styleable.OtpTextField_otp_length, DEFAULT_OTP_LENGTH)
cellWidth = typedArray.getDimension(
R.styleable.OtpTextField_cell_width,
resources.getDimension(R.dimen.otp_cell_width)
).toInt()
cellHeight = typedArray.getDimension(
R.styleable.OtpTextField_cell_height,
resources.getDimension(R.dimen.otp_cell_height)
).toInt()
cellSpacing = typedArray.getDimension(
R.styleable.OtpTextField_cell_spacing,
resources.getDimension(R.dimen.otp_cell_spacing)
).toInt()
cellCornerRadius = typedArray.getDimension(
R.styleable.OtpTextField_cell_corner_radius,
resources.getDimension(R.dimen.otp_cell_corner_radius)
).toInt()
val defaultBackgroundColor = resources.getColor(R.color.secondary, context.theme)
cellBackgroundColor = typedArray.getColor(
R.styleable.OtpTextField_cell_background_color,
Color.argb(
@Suppress("MagicNumber")
26,
Color.red(defaultBackgroundColor),
Color.green(defaultBackgroundColor),
Color.blue(defaultBackgroundColor)
)
)
typedArray.recycle()
}
private fun applyLength() {
binding.otpInput.filters = arrayOf(InputFilter.LengthFilter(length))
val linearRoot = binding.otpDisplayedContentLinear
linearRoot.removeAllViews()
otpDisplayTexts = List(length) {
applyCellAttributes(linearRoot.context)
.also {
linearRoot.addView(it)
}
}
}
private fun applyCellAttributes(context: Context): TextView {
return TextView(context).apply {
layoutParams = LinearLayout.LayoutParams(cellWidth, cellHeight, 1.0f).apply {
setMargins(cellSpacing / 2, 0, cellSpacing / 2, 0)
}
background = PaintDrawable(cellBackgroundColor).apply {
setCornerRadius(cellCornerRadius.toFloat())
}
textSize = resources.getDimension(R.dimen.text_size_h5)
setTypeface(typeface, Typeface.BOLD)
setTextColor(resources.getColor(R.color.text_color_primary, context.theme))
includeFontPadding = false
gravity = Gravity.CENTER
}
}
private fun bindEditTextToDisplay() {
binding.otpInput.addTextChangedListener(afterTextChanged = { newText ->
otpDisplayTexts.forEach { it.text = "" }
newText?.toList()?.forEachIndexed { index, char ->
otpDisplayTexts.getOrNull(index)?.text = "$char"
}
if (newText?.length == length) {
binding.root.dismissKeyboard()
_otpEntered.value = Event(Unit)
}
})
}
companion object {
private const val DEFAULT_OTP_LENGTH = 6
}
}
view_otp_edittext.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="android.widget.FrameLayout">
<!--
This EditText is hidden from the user as the content is displayed somewhere else
Only use it to manage keyboard management, focus etc ...
-->
<com.example.myapp.widget.NonSelectableEditText
android:id="@+id/otp_input"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:textColor="@color/transparent" />
<LinearLayout
android:id="@+id/otp_displayed_content_linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
</LinearLayout>
</merge>
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="OtpTextField">
<attr name="otp_length" format="integer" />
<attr name="cell_width" format="dimension" />
<attr name="cell_height" format="dimension" />
<attr name="cell_corner_radius" format="dimension" />
<attr name="cell_spacing" format="dimension" />
<attr name="cell_background_color" format="reference|color"/>
</declare-styleable>
</resources>
尺寸.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- OTP Text Field default dimensions -->
<dimen name="otp_cell_width">56dp</dimen>
<dimen name="otp_cell_height">60dp</dimen>
<dimen name="otp_cell_corner_radius">12dp</dimen>
<dimen name="otp_cell_spacing">11dp</dimen>
</resources>
如您所见,该OtpTextField.kt
文件具有我想要的外观的正确声明和定义,但白色背景以某种方式存在,我不确定为什么。任何帮助将不胜感激!
解决方案
推荐阅读
- javascript - 如何根据另一个选择中的所选选项更改选择中的选项
- bash - 在bash中连接CSV文件只保留一次标题
- c# - c# 在转发器控件中使用 Calendar.SelectedDate,但月份给了我“00”
- authentication - VueJS / Vuex App - 验证用于身份验证的 JWT 令牌
- keyboard - 奇怪的键绑定(Ubuntu 16.04 VM、xfce、Spice 控制台)
- sql - 按列将值递增到行 - SQL Server
- javascript - 捕获量角器测试用例的通过/失败以进行自定义报告
- c# - 填充算法无法按预期工作 - 它会在像素之间产生间隙
- laravel - axios 响应数据在 laravel 中总是设置为空字符串
- c# - Lottie.Forms - 从 EmbeddedResources 加载