Kotlin学习 02

发布时间 2023-05-25 14:37:04作者: Liu Siyuan

在前一次只能选择省份简称的基础上,增加了输入车牌号码的交互

这次主要是自己动手写代码实现:

1、动态生成7位的车牌输入框EditText

2、省份是选择的,车牌号是手动输入的

3、点击清除实现了最后一位的删除,并自动将焦点移动到前面框中

自己实现一些功能之后,确实发现kotlin能剩下很多代码,可读性也稍微变差了  -_-||

 

package com.example.mylogindemomvp.ui.carnumber

import android.content.Context
import android.graphics.Color
import android.text.Editable
import android.text.InputFilter
import android.text.InputFilter.LengthFilter
import android.text.TextWatcher
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.KeyEvent
import android.widget.*
import androidx.core.content.ContextCompat
import com.example.mylogindemomvp.R
import com.example.mylogindemomvp.util.AppUtils


/**
 * 自定义车牌号选择器
 */
class LicensePlateView: LinearLayout {

    companion object {
        val mLicensePlateList = arrayListOf(
            "京", "津", "渝", "沪", "冀", "晋", "辽", "吉", "黑", "苏",
            "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "琼",
            "川", "贵", "云", "陕", "甘", "青", "蒙", "桂", "宁", "新",
            "藏", "使", "领", "学", "港", "澳",
        )
        // 拍照内容字符(禁用了O和I)
        val mLicenseNumberList = arrayListOf(
            "A", "B", "C", "D", "E", "F", "G",
            "H", "J", "K", "L", "M", "N", "P",
            "Q", "R", "S", "T", "U", "V", "W",
            "X", "Y", "Z", "0", "1", "2", "3",
            "4", "5", "6", "7", "8", "9"
        )
    }

    private var mSpacing = 10f          // 间隔
    private var mMarginLeftRight = 10f  // 左边距
    private var mMarginBottom = 0f      // 下边距
    private var mMarginTop = 60f        // 上边距

    private var mLength = 10            // 长度
    private var mRectHeight = 88f       // 每个格子高度
    private var mRectMarginTop = 16f    // 距离上边高度

    private var mBackGroundColor = Color.TRANSPARENT  // 背景颜色
    private var mBackGroundImageRes:Int ?= null // 背景图片资源
    private var mRectBackGround = R.drawable.view_shape_stroke_8548d2_radius_2      // 格子背景
    private var mRectSelectBackGround = R.drawable.view_shape_stroke_8548d2_radius_2// 格子选中背景

    private var editTextBackground = R.drawable.background_corner_trans_blue

    private var mCompleteText = "完成"//完成文字内容
    private var mIsShowComplete = true     //是否显示完成按钮

    private var mClearText = "清除"//完成文字内容
    private var mIsShowClear = true        //是否显示清除按钮

    private var mCompleteTextColor = Color.BLACK//完成按钮文字颜色

    private var mCompleteTextSize = 18f//完成文字大小

    private var mCompleteMarginTop = 20f//完成距离上边

    private var mCompleteMarginBottom = 20f//完成距离上边

    private var mCompleteMarginRight = 20f//完成距离右边

    private var mRectTextSize = 16f//格子的文字大小

    private var mRectTextColor = ContextCompat.getColor(context, R.color.text_333333)//格子文字的默认颜色

    private var mRectSelectTextColor = ContextCompat.getColor(context, R.color.text_8548D2) // 格子文字的选中颜色

    private var mNumProhibit = true // 默认禁止选择
    private var mNumProhibitColor = ContextCompat.getColor(context, R.color.text_999999)

    private var mTempTextViewList = ArrayList<TextView>() // 存储临时View
    private var mTextClickEffect = true // 是否触发点击效果

    private var mOldTextView:TextView = TextView(context)

    // 鄂A66666
    val max_length = 7
    var editTextList = ArrayList<EditText>(max_length); // 常用车牌号一般是7位

    constructor(context: Context): super(context) {
        initData(context)
    }

    constructor(context: Context, attrs: AttributeSet?): super(context, attrs) {
        context.obtainStyledAttributes(attrs, R.styleable.LicensePlateView)
            .apply {
                // 整理的背景颜色
                mBackGroundColor = getColor(R.styleable.LicensePlateView_lp_background, Color.parseColor("#F5F5F5"))
                mBackGroundImageRes = getResourceId(R.styleable.LicensePlateView_lp_background_img, -1)
                // 每个格子的默认背景
                mRectBackGround = getResourceId(R.styleable.LicensePlateView_lp_rect_background, mRectBackGround)

                // 每个格子的选中背景
                mRectSelectBackGround = getResourceId(R.styleable.LicensePlateView_lp_rect_background, mRectSelectBackGround)

                // 格子的文字大小
                mRectTextSize = getDimension(R.styleable.LicensePlateView_lp_rect_text_size, mRectTextSize)
                // 格子的文字颜色
                mRectTextColor = getColor(R.styleable.LicensePlateView_lp_rect_text_color, mRectTextColor)
                //格子的选中文字颜色
                mRectSelectTextColor = getColor(R.styleable.LicensePlateView_lp_rect_select_text_color, mRectSelectTextColor)
                //每个格子的边距
                mSpacing = getDimension(R.styleable.LicensePlateView_lp_rect_spacing, mSpacing)
                //每个格子的高度
                mRectHeight = getDimension(R.styleable.LicensePlateView_lp_rect_height, mRectHeight)
                // 格子距离上边的距离
                mRectMarginTop = getDimension(R.styleable.LicensePlateView_lp_rect_margin_top, mRectMarginTop)
                // 视图距离左右的距离
                mMarginLeftRight = getDimension(R.styleable.LicensePlateView_lp_margin_left_right, mMarginLeftRight)
                //视图距离上边的距离
                mMarginTop =
                    getDimension(R.styleable.LicensePlateView_lp_margin_top, mMarginTop)
                //视图距离下边的距离
                mMarginBottom =
                    getDimension(R.styleable.LicensePlateView_lp_margin_bottom, mMarginBottom)
                //视图距离左右的距离
                mMarginLeftRight = getDimension(R.styleable.LicensePlateView_lp_margin_left_right, mMarginLeftRight)
                //是否显示完成按钮
                mIsShowComplete = getBoolean(R.styleable.LicensePlateView_lp_is_show_complete, true)
                //完成按钮文字颜色
                mCompleteTextColor =
                    getColor(R.styleable.LicensePlateView_lp_complete_text_color, Color.parseColor("#087EFD"))
                //完成按钮文字大小
                mCompleteTextSize =
                    getDimension(
                        R.styleable.LicensePlateView_lp_complete_text_size,
                        mCompleteTextSize
                    )
                //完成按钮文字内容
                getString(R.styleable.LicensePlateView_lp_complete_text)?.let {
                    mCompleteText = it
                }
                //完成按钮距离上边
                mCompleteMarginTop =
                    getDimension(
                        R.styleable.LicensePlateView_lp_complete_margin_top,
                        mCompleteMarginTop
                    )
                //完成按钮距离下边
                mCompleteMarginBottom =
                    getDimension(
                        R.styleable.LicensePlateView_lp_complete_margin_bottom,
                        mCompleteMarginBottom
                    )
                //完成按钮距离上边
                mCompleteMarginRight =
                    getDimension(
                        R.styleable.LicensePlateView_lp_complete_margin_right,
                        mCompleteMarginRight
                    )
                //是否触发点击效果
                mTextClickEffect =
                    getBoolean(
                        R.styleable.LicensePlateView_lp_text_click_effect,
                        mTextClickEffect
                    )
                this.recycle()
            }

        initData(context)
    }

    /**
     * 初始化数据
     */
    private fun initData(context: Context) {
        // 设置背景颜色
        setBackgroundColor(mBackGroundColor)
        if(mBackGroundImageRes != -1) {
            mBackGroundImageRes?.let { setBackgroundResource(it) }
        }

        orientation = VERTICAL
        // 设置距离底部
        setPadding(0, mMarginTop.toInt(), 0, mMarginBottom.toInt())

        // 显示文本框 鄂 A 6 D 5 3 2
        var topLayout = LinearLayout(context)
        var index = 0
        while(index < max_length) {
            var editText = XZEditView(context).apply {
                tag = index
                setBackgroundResource(editTextBackground)
                setTextColor(Color.BLACK)
                setTextSize(TypedValue.COMPLEX_UNIT_SP, mCompleteTextSize)
                gravity = Gravity.CENTER
                maxLines = 1
                filters = arrayOf<InputFilter>(LengthFilter(1))    // 限定长度
//                inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS // 限定输入类型
                weightSum = 1.0f
                editTextList.add(this)
                if(index == 0) {
                    isEnabled = false
//                } else if(index == 1) {
//                    inputType = InputType.TYPE_TEXT_FLAG_CAP_WORDS or InputType.TYPE_CLASS_NUMBER
                }

                // 添加输入监听,实现输入之后自动跳转下一个
                val localIndex = index
                addTextChangedListener(object : TextWatcher {
                    override fun afterTextChanged(s: Editable?) {
                        if(inputCheck(localIndex, s.toString())) {
                            focusToNext(localIndex)
                        }
                    } //输入后的监听}
                    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                        println("beforeTextChanged")

                    }//输入后的监听}
                    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                        println("onTextChanged")
                    }//输入文字产生变化的监听}
                })
            }

            // 调用Java 工具方法
            var dp2 = AppUtils().dp2px(context, 2f)
            var editTextParams = LayoutParams(LayoutParams.MATCH_PARENT, AppUtils().dp2px(context, 42f), 1.0f)
            editTextParams.leftMargin = dp2
            editTextParams.rightMargin = dp2
            topLayout.addView(editText, editTextParams)

            index ++
        }
        addView(topLayout)

        val actionLayout = LinearLayout(context)
        actionLayout.apply {
            orientation = HORIZONTAL
            gravity = Gravity.RIGHT
        }
        // “完成” 按钮是否显示?
        if(mIsShowComplete) {
            // 添加完成的View视图
            val textView = TextView(context)
            textView.apply {
                setOnClickListener() {
                    mKeyboardCompleted?.invoke()
                }
                gravity = Gravity.END
                text = mCompleteText
                setTextColor(mCompleteTextColor)
                setTextSize(TypedValue.COMPLEX_UNIT_SP, mCompleteTextSize)
            }

            actionLayout.addView(textView)
            val submitParams = textView.layoutParams as LayoutParams
            submitParams.apply {
                width = LayoutParams.WRAP_CONTENT
                topMargin = mCompleteMarginTop.toInt()
                bottomMargin = (mCompleteMarginBottom - mRectMarginTop).toInt()
                leftMargin = mCompleteMarginRight.toInt()
                rightMargin = mCompleteMarginRight.toInt()
                textView.layoutParams = this
            }
        }
        // “清除” 按钮是否显示?
        if(mIsShowClear) {
            // 添加完成的View视图
            val textView = TextView(context)
            textView.apply {
                setOnClickListener {
                    backspace()
                }
                gravity = Gravity.END
                text = mClearText
                setTextColor(mCompleteTextColor)
                setTextSize(TypedValue.COMPLEX_UNIT_SP, mCompleteTextSize)
            }

            actionLayout.addView(textView)
            val clearParams = textView.layoutParams as LayoutParams
            clearParams.apply {
                width = LayoutParams.WRAP_CONTENT
                topMargin = mCompleteMarginTop.toInt()
                bottomMargin = (mCompleteMarginBottom - mRectMarginTop).toInt()
                rightMargin = mCompleteMarginRight.toInt()
                textView.layoutParams = this
            }
        }
        // 操作栏添加到UI中
        addView(actionLayout, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))

        // 每行对应的省份简称
        var layout: LinearLayout? = null
        mLicensePlateList.forEachIndexed{ index, s ->
            if(index % mLength == 0) {
                // 重新创建,并添加View
                layout = createLinearLayout()
                layout?.weightSum = 1f
                addView(layout)
                val params = layout?.layoutParams as LayoutParams
                params.apply {
                    topMargin = mRectMarginTop.toInt()
                    height = mRectHeight.toInt()
                    leftMargin = mMarginLeftRight.toInt()
                    rightMargin = mMarginLeftRight.toInt() - mSpacing.toInt()
                    layout?.layoutParams = this
                }
            }

            //创建文字视图
            val textView = TextView(context).apply {
                text = s
                //设置文字的属性
                textSize = mRectTextSize // setTextSize(TypedValue.COMPLEX_UNIT_SP, mRectTextSize)
                //最后五个是否禁止
                if (mNumProhibit && index > (mLicensePlateList.size - 6)) {
                    setTextColor(mNumProhibitColor)
                    mTempTextViewList.add(this)
                } else {
                    setTextColor(mRectTextColor)
                }

                setBackgroundResource(mRectBackGround)
                gravity = Gravity.CENTER
                setOnClickListener {
                    if (mNumProhibit && index > (mLicensePlateList.size - 6)) {
                        return@setOnClickListener
                    }
                    // 点击的省份填充到第1个文本框
                    editTextList?.get(0).setText(text)
                    //每个格子的点击事件
                    changeTextViewState(this)
                }
            }
            addRectView(textView, layout, 0.1f)
        }

        //追加最后一个删除按钮View,动态计算宽度
        addEndView(layout)
    }

    private fun backspace() {
        val focusEditText = editTextList.filter {
            it.hasFocus()
        }

        var focusIndexInt: Int = if(focusEditText?.size > 0) {
            val focusIndex = focusEditText?.get(0).tag
            focusIndex as Int
        } else {
            max_length - 1
        }
        editTextList?.get(focusIndexInt).setText("")
        if(focusIndexInt > 1) {
            editTextList?.get(focusIndexInt - 1).requestFocus()
        }
    }

    /**
     * 要实现键盘上的退格功能
     */
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if(event?.keyCode == KeyEvent.KEYCODE_BACK) {
            val focusEditText = editTextList.filter {
                it.hasFocus()
            }
            if(focusEditText?.size > 0) {
                val focusIndex = focusEditText?.get(0).tag
                focusEditText?.get(0).setText("")
                if(focusIndex as Int > 1) { // 这里涉及到强制类型转换 as
                    editTextList?.get(focusIndex - 1).requestFocus()
                }
            }
        }
        return super.onKeyDown(keyCode, event)
    }

    /**
     * 输入校验
     */
    private fun inputCheck(index: Int, text: String): Boolean {
        if(index == 0) {
            return mLicensePlateList.contains(text)     // 输入省份
        } else {
            return mLicenseNumberList.contains(text)    // 输入字母和数字
        }
    }

    private fun focusToNext(index: Int) {
        editTextList?.get(index).clearFocus()
        if(index < max_length - 1) {
            editTextList?.get(index + 1).requestFocus()
        }
    }

    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:追加最后一个View
     */
    private fun addEndView(layout: LinearLayout?) {
        val endViewLayout = LinearLayout(context)
        endViewLayout.gravity = Gravity.END
        //删除按钮
        val endView = RelativeLayout(context)
        //添加删除按钮
        val deleteImage = ImageView(context)
        deleteImage.setImageResource(R.mipmap.icon_left)
        endView.addView(deleteImage)

        val imageParams = deleteImage.layoutParams as RelativeLayout.LayoutParams
        imageParams.addRule(RelativeLayout.CENTER_IN_PARENT)
        deleteImage.layoutParams = imageParams
        endView.setOnClickListener {
            //删除
            mKeyboardDelete?.invoke()
            invalidate()
        }
        endView.setBackgroundResource(mRectBackGround)
        endViewLayout.addView(endView)
        val params = endView.layoutParams as LayoutParams
        params.width = (getScreenWidth() / mLength) * 2 - mMarginLeftRight.toInt()
        params.height = LayoutParams.MATCH_PARENT

        endView.layoutParams = params

        layout?.addView(endViewLayout)
        val endParams = endViewLayout.layoutParams as LayoutParams
        endParams.apply {
            width = (mSpacing * 3).toInt()
            height = LayoutParams.MATCH_PARENT
            weight = 0.4f
            rightMargin = mSpacing.toInt()
            endViewLayout.layoutParams = this
        }
    }

    private fun addRectView(view: TextView, layout: LinearLayout?, w: Float) {
        layout?.addView(view)
        val textParams = view.layoutParams as LayoutParams
        textParams.apply {
            weight = w
            width = 0
            height = LayoutParams.MATCH_PARENT
            //每行的最后一个
            rightMargin = mSpacing.toInt()
            view.layoutParams = this
        }

    }

    private fun changeTextViewState(textView: TextView) {
        if (mTextClickEffect) {
            //点击设置成效果
            textView.setSelectTextStyle()
            textView.postDelayed({
                textView.setUnSelectTextStyle()
            }, 300)
        } else {
            //记录上一个
            mOldTextView?.setUnSelectTextStyle()
            textView.setSelectTextStyle()
            mOldTextView = textView
        }
        println("点击选择:" + textView.text)
        //每次点击后进行赋值
        mKeyboardContent?.invoke(textView.text.toString())
    }

    private fun TextView.setSelectTextStyle() {
        setBackgroundResource(mRectSelectBackGround)
        setTextColor(mRectSelectTextColor)
    }

    private fun TextView.setUnSelectTextStyle() {
        setBackgroundResource(mRectBackGround)
        setTextColor(mRectTextColor)
    }

    private fun createLinearLayout(): LinearLayout? {
        val layout = LinearLayout(context)
        layout.orientation = HORIZONTAL
        return layout
    }

    private var mKeyboardContent: ((content: String) -> Unit?)? = null
    fun keyboardContent(block: (String) -> Unit) {
        mKeyboardContent = block
    }

    /**
     * 键盘“完成”按键
     */
    private var mKeyboardCompleted: (() -> Unit)? = null
    fun keyboardCompleted(block: () -> Unit) {
        mKeyboardCompleted = block
    }

    /**
     * 键盘“删除”按键
     */
    private var mKeyboardDelete: (() -> Unit?)? = null
    fun keyboardDelete(block: () -> Unit) {
        mKeyboardDelete = block
    }

    fun openProhibit(isOpen: Boolean) {
        //禁止解开
        mNumProhibit = isOpen
        mTempTextViewList.forEach {
            if (isOpen) {
                it.setTextColor(mRectTextColor)
            } else {
                it.setTextColor(mNumProhibitColor)
            }
        }
    }

    /**
     * 获取屏幕的宽
     */
    private fun getScreenWidth(): Int {
        return resources.displayMetrics.widthPixels
    }
}