【CustomView】数字解锁键盘(LockKeypad)- 用星号图标替换文案显示

自定义数字解锁功能,如下所示:

20201019172539451    >>>>>>>>>>>>     

#### 接着上篇的 数字解锁键盘(LockKeypad)

1.仔细观察星号,这里密码显示的星号(*)是设计师提供设计icon。当然,如果有不需要的可以把对应代码注掉就行;

星号(icon)替换代码:(这里的代码在自定义view里都有,我只是单独贴出来)

input.addTextChangedListener {
            if (it.isNullOrEmpty()) {
                clear_text.hide()
                button_delete.invisible()
                button_enter.isEnabled = false
                password_display.text = ""
            } else {
                clear_text.show()
                button_delete.show()
                button_enter.isEnabled = true
                
                // Add password_display TextView: used here to display asterisks
                password_display.text = it.toDisplayByInput(context)
            }
        }


// 循环替换每个数字
fun Editable.toDisplayByInput(context: Context): CharSequence {
    val length = this.length
    if(length == 0) return ""
    val sb = SpannableStringBuilder()
    for (i in 0 until length) {
        sb.append(displayWithIcon(context))
    }
    return sb
}

// 用星号(Icon)来替换每个数字
private fun displayWithIcon(context: Context): Spannable {
    val sb = SpannableStringBuilder()
    sb.append("   Icon   ")
    val index = sb.indexOf("Icon")
    sb.setSpan(
        ImageSpan(context, R.drawable.ic_password_hidden_display, ImageSpan.ALIGN_CENTER),
        index,
        index + "Icon".length,
        Spanned.SPAN_INCLUSIVE_EXCLUSIVE
    )
    return sb
}

 

1.CustomView >>> LockKeypad:(代码实现)


class LockKeypad @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = -1
) : ConstraintLayout(context, attrs, defStyleAttr) {

    var onSuccessListener: ((String) -> Unit)? = null

    //Shaking margin value in the specified time
    private val shakeTime: Array<Float> by lazy {
        arrayOf(0F, 0.2F, 0.4F, 0.6F, 0.8F, 1.0F)
    }
    private val shakeMargin: Array<Float> by lazy {
        arrayOf(30F, -30F, 15F, -15F, 8F, 0F)
    }

    init {
        context.getLayoutInflater().inflate(R.layout.view_lock_keypad, this, true)

        password_display.show()
        input.invisible()

        button_one.setOnClickListener { appendNumber("1") }
        button_two.setOnClickListener { appendNumber("2") }
        button_three.setOnClickListener { appendNumber("3") }
        button_four.setOnClickListener { appendNumber("4") }
        button_five.setOnClickListener { appendNumber("5") }
        button_six.setOnClickListener { appendNumber("6") }
        button_seven.setOnClickListener { appendNumber("7") }
        button_eight.setOnClickListener { appendNumber("8") }
        button_nine.setOnClickListener { appendNumber("9") }
        button_zero.setOnClickListener { appendNumber("0") }

        var isLongClick = false
        val runnable = object : Runnable {
            override fun run() {
                isLongClick = true
                    
                input.removeLast()

                handler.postDelayed(
                    this,
                    50L
                )
            }
        }

        button_delete.setOnTouchListener { _, event ->
            hideError()
            if (visibility == View.INVISIBLE) {
                false
            }
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    handler.postDelayed(
                        runnable,
                        android.view.ViewConfiguration.getLongPressTimeout().toLong()
                    )
                }

                MotionEvent.ACTION_UP -> {
                    handler.removeCallbacks(runnable)
                    if (!isLongClick) {    
                        input.removeLast()
                    }
                    isLongClick = false
                }
            }
            true
        }

        button_enter.setOnClickListener {
            onSuccessListener?.let { it1 -> it1(input.text.toString()) }
        }

        clear_text.setOnClickListener {
            input.text.clear()
            hideError()
        }

        input.addTextChangedListener {
            if (it.isNullOrEmpty()) {
                clear_text.hide()
                button_delete.invisible()
                button_enter.isEnabled = false
                password_display.text = ""
            } else {
                clear_text.show()
                button_delete.show()
                button_enter.isEnabled = true
                // Add password_display TextView: used here to display asterisks
                password_display.text = it.toDisplayByInput(context)
            }
        }
    }

    fun showError() {
        error_label.show()
        shakeKeyframe(password_display, 400L).start()
    }

    fun hideError() {
        error_label.hide()
    }

    private fun appendNumber(num: String) {
        hideError()
        input.text.append(num)
        input.playSoundEffect(SoundEffectConstants.CLICK) // 为此视图播放声音效果;该框架将为某些内置动作(例如单击)播放声音效果
    }

    /**
     * Shake left and right within a specified time
     * @param view
     * @param duration
     * @return
     */
    private fun shakeKeyframe(view: View, duration: Long): ObjectAnimator {
        val pvhTranslateX = PropertyValuesHolder.ofKeyframe(
            View.TRANSLATION_X,
            Keyframe.ofFloat(shakeTime[0], shakeMargin[0]),
            Keyframe.ofFloat(shakeTime[1], shakeMargin[1]),
            Keyframe.ofFloat(shakeTime[2], shakeMargin[2]),
            Keyframe.ofFloat(shakeTime[3], shakeMargin[3]),
            Keyframe.ofFloat(shakeTime[4], shakeMargin[4]),
            Keyframe.ofFloat(shakeTime[5], shakeMargin[5])
        )
        return ObjectAnimator.ofPropertyValuesHolder(view, pvhTranslateX).setDuration(duration)
    }
}

Layout:view_lock_keypad(自定义view对应的布局文件)

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    tools:ignore="HardcodedText">

    <TextView
        android:id="@+id/error_label"
        style="@style/errorRed"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/dp_10"
        android:text="@string/invalid_pin"
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@id/input"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/input"
        style="@style/LockKeyPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minWidth="@dimen/dp_320"
        android:lines="1"
        android:layout_marginBottom="@dimen/dp_16"
        android:autofillHints="password"
        app:layout_constraintBottom_toTopOf="@id/button_one"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />

    <TextView
        android:id="@+id/password_display"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minWidth="@dimen/dp_320"
        android:maxWidth="@dimen/dp_1260"
        android:lines="1"
        android:ellipsize="marquee"
        android:gravity="center"
        android:visibility="visible"
        app:layout_constraintEnd_toEndOf="@id/input"
        app:layout_constraintStart_toStartOf="@id/input"
        app:layout_constraintTop_toTopOf="@id/input"
        app:layout_constraintBottom_toBottomOf="@id/input"/>

    <ImageView
        android:id="@+id/clear_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_lock_key_close"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@id/input"
        app:layout_constraintStart_toEndOf="@+id/password_display"
        app:layout_constraintTop_toTopOf="@id/input" />

    <TextView
        android:id="@+id/button_one"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:layout_marginBottom="@dimen/dp_50"
        android:text="1"
        app:layout_constraintBottom_toTopOf="@id/button_four"
        app:layout_constraintEnd_toStartOf="@id/button_two"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/input" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_one"
        app:layout_constraintEnd_toStartOf="@id/button_one"
        app:layout_constraintStart_toEndOf="@id/button_one" />

    <TextView
        android:id="@+id/button_two"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:layout_marginStart="@dimen/dp_80"
        android:layout_marginEnd="@dimen/dp_80"
        android:text="2"
        app:layout_constraintBottom_toBottomOf="@id/button_one"
        app:layout_constraintEnd_toStartOf="@id/button_three"
        app:layout_constraintStart_toEndOf="@id/button_one"
        app:layout_constraintTop_toTopOf="@id/button_one" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_two"
        app:layout_constraintEnd_toStartOf="@id/button_two"
        app:layout_constraintStart_toEndOf="@id/button_two" />

    <TextView
        android:id="@+id/button_three"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:text="3"
        app:layout_constraintBottom_toBottomOf="@id/button_one"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/button_two"
        app:layout_constraintTop_toTopOf="@id/button_one" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_three"
        app:layout_constraintEnd_toStartOf="@id/button_three"
        app:layout_constraintStart_toEndOf="@id/button_three" />

    <TextView
        android:id="@+id/button_four"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:layout_marginBottom="@dimen/dp_50"
        android:text="4"
        app:layout_constraintBottom_toTopOf="@id/button_seven"
        app:layout_constraintEnd_toStartOf="@id/button_five"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button_one" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_four"
        app:layout_constraintEnd_toStartOf="@id/button_four"
        app:layout_constraintStart_toEndOf="@id/button_four" />

    <TextView
        android:id="@+id/button_five"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:layout_marginStart="@dimen/dp_80"
        android:layout_marginEnd="@dimen/dp_80"
        android:text="5"
        app:layout_constraintBottom_toBottomOf="@id/button_four"
        app:layout_constraintEnd_toStartOf="@id/button_six"
        app:layout_constraintStart_toEndOf="@id/button_four"
        app:layout_constraintTop_toTopOf="@id/button_four" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_five"
        app:layout_constraintEnd_toStartOf="@id/button_five"
        app:layout_constraintStart_toEndOf="@id/button_five" />

    <TextView
        android:id="@+id/button_six"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:text="6"
        app:layout_constraintBottom_toBottomOf="@id/button_four"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/button_five"
        app:layout_constraintTop_toTopOf="@id/button_four" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_six"
        app:layout_constraintEnd_toStartOf="@id/button_six"
        app:layout_constraintStart_toEndOf="@id/button_six" />

    <TextView
        android:id="@+id/button_seven"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:layout_marginBottom="@dimen/dp_50"
        android:text="7"
        app:layout_constraintBottom_toTopOf="@id/button_delete"
        app:layout_constraintEnd_toStartOf="@id/button_eight"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button_four" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_seven"
        app:layout_constraintEnd_toStartOf="@id/button_seven"
        app:layout_constraintStart_toEndOf="@id/button_seven" />

    <TextView
        android:id="@+id/button_eight"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:layout_marginStart="@dimen/dp_80"
        android:layout_marginEnd="@dimen/dp_80"
        android:text="8"
        app:layout_constraintBottom_toBottomOf="@id/button_seven"
        app:layout_constraintEnd_toStartOf="@id/button_nine"
        app:layout_constraintStart_toEndOf="@id/button_seven"
        app:layout_constraintTop_toTopOf="@id/button_seven" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_eight"
        app:layout_constraintEnd_toStartOf="@id/button_eight"
        app:layout_constraintStart_toEndOf="@id/button_eight" />

    <TextView
        android:id="@+id/button_nine"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:text="9"
        app:layout_constraintBottom_toBottomOf="@id/button_seven"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/button_eight"
        app:layout_constraintTop_toTopOf="@id/button_seven" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_nine"
        app:layout_constraintEnd_toStartOf="@id/button_nine"
        app:layout_constraintStart_toEndOf="@id/button_nine" />


    <ImageView
        android:id="@+id/button_delete"
        style="@style/LockFab"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:scaleType="center"
        android:src="@drawable/ic_lock_key_delete"
        android:visibility="invisible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/button_zero"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button_seven" />

    <TextView
        android:id="@+id/button_zero"
        style="@style/LockCircleButton"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:layout_marginStart="@dimen/dp_80"
        android:layout_marginEnd="@dimen/dp_80"
        android:text="0"
        app:layout_constraintBottom_toBottomOf="@id/button_delete"
        app:layout_constraintEnd_toStartOf="@id/button_enter"
        app:layout_constraintStart_toEndOf="@id/button_delete"
        app:layout_constraintTop_toTopOf="@id/button_delete" />

    <View
        android:layout_width="@dimen/lock_key_line_width"
        android:layout_height="@dimen/lock_key_line_height"
        android:background="@color/lock_key_bottom_line"
        app:layout_constraintBottom_toBottomOf="@id/button_zero"
        app:layout_constraintEnd_toStartOf="@id/button_zero"
        app:layout_constraintStart_toEndOf="@id/button_zero" />

    <ImageView
        android:id="@+id/button_enter"
        style="@style/LockFab"
        android:layout_width="@dimen/fab_icon_size"
        android:layout_height="@dimen/fab_icon_size"
        android:scaleType="center"
        android:src="@drawable/ic_lock_key_check_selector"
        app:layout_constraintBottom_toBottomOf="@id/button_delete"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/button_zero"
        app:layout_constraintTop_toTopOf="@id/button_delete"
        app:maxImageSize="@dimen/fab_size_36" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

2. XML使用示例:

<com.your.app.view.LockKeypad
        android:id="@+id/lock_keypad"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

 

3. Activity使用示例:

lock_keypad.onSuccessListener = { pin ->
            lifecycleScope.launch {
                try {
                    val user = userViewModel.getUserByPin(pin.toInt())
                    if (user != null) { //verify Success
                        lock_keypad.hideError()
                        sp.userId = user.userId
                        sp.userName = user.userName
                        lock_keypad.input.setText("")

                        //TODO 验证通过执行跳转页面
                        // findNavigate()
                    } else {
                        //TODO 验证不通过执行
                        lock_keypad.showError()
                    }
                } catch (e: Exception) {
                    lock_keypad.showError()
                    Logger.e(TAG, "Error getting user from the db", e)
                }
            }
        }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值