当前位置:   article > 正文

Android低代码开发 - InputMenuPanelItem详解

Android低代码开发 - InputMenuPanelItem详解

我们知道MenuPanel是一个菜单面板容器,它里面可以放各式各样的菜单和菜单组。今天我们就来详细讲解输入菜单这个东西。

InputMenuPanelItem源码

package dora.widget.panel.menu

import android.content.Context
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.LinearLayout
import dora.widget.panel.R
import dora.widget.panel.MenuPanelItem
import dora.widget.panel.MenuPanelItemRoot
import dora.widget.panel.MenuPanelItemRoot.Companion.DEFAULT_MARGIN_TOP
import dora.widget.panel.MenuPanelItemRoot.Companion.DEFAULT_TITLE_SPAN

class InputMenuPanelItem
    @JvmOverloads constructor(
    override var marginTop: Int = DEFAULT_MARGIN_TOP,
    override var title: String? = "",
    private var titleSpan: MenuPanelItemRoot.Span = MenuPanelItemRoot.Span(DEFAULT_TITLE_SPAN),
    override val menuName: String? = MenuPanelItem.generateMenuName("InputMenuPanelItem"),
    private val hint: String?  = "",
    private val content: String? = "",
    private val watcher: ContentWatcher? = null
) : MenuPanelItem {

    constructor(title: String, titleSpan: MenuPanelItemRoot.Span, hint: String,
                content: String, watcher: ContentWatcher? = null) : this(DEFAULT_MARGIN_TOP,
        title, titleSpan, MenuPanelItem.generateMenuName("InputMenuPanelItem"),
        hint, content, watcher)

    constructor(title: String, titleSpan: MenuPanelItemRoot.Span, hint: String,
                content: String) : this(title, titleSpan, hint, content, null)

    constructor(hint: String,
                content: String, watcher: ContentWatcher? = null) : this(DEFAULT_MARGIN_TOP,
        "", MenuPanelItemRoot.Span(DEFAULT_TITLE_SPAN), MenuPanelItem.generateMenuName("InputMenuPanelItem"),
        hint, content, watcher)

    constructor(hint: String,
                content: String) : this(hint, content, null)

    override fun initData(menuView: View) {
        val lp = LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )
        lp.topMargin = marginTop
        menuView.layoutParams = lp
        val editText = menuView.findViewById<EditText>(ID_EDIT_TEXT_INPUT)
        editText.hint = hint
        if (!TextUtils.isEmpty(content)) {
            editText.setText(content)
            editText.setSelection(content!!.length)
        }
        if (watcher != null) {
            editText.addTextChangedListener(object : TextWatcher {
                override fun beforeTextChanged(
                    s: CharSequence,
                    start: Int,
                    count: Int,
                    after: Int
                ) {
                }

                override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                    watcher.onContentChanged(this@InputMenuPanelItem, s.toString())
                }

                override fun afterTextChanged(s: Editable) {}
            })
        }
    }

    override fun hasTitle(): Boolean {
        return title != null && title != ""
    }

    override fun getTitleSpan(): MenuPanelItemRoot.Span {
        return titleSpan
    }

    override fun setTitleSpan(titleSpan: MenuPanelItemRoot.Span) {
        this.titleSpan = titleSpan
    }

    override val layoutId: Int
        get() = R.layout.layout_menu_panel_input

    override fun inflateView(context: Context): View {
        return LayoutInflater.from(context).inflate(layoutId, null)
    }

    interface ContentWatcher {
        fun onContentChanged(item: InputMenuPanelItem, content: String)
    }

    companion object {
        @JvmField
        val ID_EDIT_TEXT_INPUT: Int = R.id.et_menu_panel_input
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104

属性解析

marginTop:上边距

title和titleSpan:标题和标题边距

menuName:菜单名称,在同一个MenuPanel保证唯一性

hint:输入提示信息,同android:hint

content:输入内容,同android:text

watcher:输入观察者,用来监听输入的字符

使用场景

  • 场景1,6个属性都传入,完全自定义
  • 场景2,只指定hint和content,其他用默认
  • 场景3,在场景2的基础上增加一个输入字符监听器
  • 场景4,指定标题、标题边距、hint和content
  • 场景5,在场景4的基础上再增加一个输入字符监听器

IMAGE 2024-06-13 16:24:44.jpg

在这幅图中,就用到了几次InputMenuPanelItem。position分别为1、3、4和5。

InputMenuPanelItem相较于直接使用EditText的优势

  1. 可以给输入框指定一个标题提示信息,而不同于hint,用户可以一直看到。同时它可以和hint一起使用,让hint显示校验规则等其他信息。
  2. 可以快捷的添加到MenuPanel,而不用考虑布局,写一大堆的属性,仅指定几个关键属性。

使用说明书

是否需要指定menuName

不需要。我们知道,menuName作为MenuPanelItem的核心属性,需要保证其不重复,这样方便于我们找到它,并做相应的处理,如设置菜单的点击事件。但是在InputMenuPanelItem中,它的情况比较特殊,通常我们是不需要处理输入框的点击事件的。

所以框架在改进的过程中,最大限度的提供包容性。


companion object {

    /**
     * 虽然用它生成了默认的menuName,但是不建议你使用,最好使用业务的命名覆盖它。
     */
    fun generateMenuName(prefix: String? = null) : String {
        val uuid = UUID.randomUUID().toString()
        return if (!TextUtils.isEmpty(prefix)) {
            prefix + "-" + uuid.replace("-".toRegex(), "")
        } else {
            uuid.replace("-".toRegex(), "")
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注释虽然这么写,但是它不是针对InputMenuPanelItem的。我们建议你就直接使用框架生成的menuName,所以构造函数都没有让你指定menuName,当然你也可以指定它。在InputMenuPanelItem中,默认的menuName它是一个“InputMenuPanelItem-”再加上随机字符串。

获取到InputMenuPanelItem里面的EditText

getCacheChildView()
  • 1

推荐使用它,通过position和view的id去拿到。id有一个常量,InputMenuPanelItem.ID_EDIT_TEXT_INPUT,可以直接使用,避免你导R包冲突,要写一长串的包名。

companion object {
    @JvmField
    val ID_EDIT_TEXT_INPUT: Int = R.id.et_menu_panel_input
}
  • 1
  • 2
  • 3
  • 4
getCacheViewFromItem()
  • 1

也可以使用它,需要你传入一个MenuPanelItem的属性先拿到根布局,再通过findViewById()进行查找。

设置最大长度限制和输入的字符限制

拿到了EditText我们要给它设置限制条件就方便了。

android:maxLength代码指定

如设置最多输入10个字符。

etInput.filters = arrayOf(InputFilter.LengthFilter(10))
  • 1
android:digits代码指定

如设置只能输入数字。

etInput.keyListener = DigitsKeyListener.getInstance("0123456789")
  • 1
组合使用

只能输入2位的正整数。

etInput.filters = arrayOf(InputFilter.LengthFilter(2), object : InputFilter {

    override fun filter(
        source: CharSequence?,
        start: Int,
        end: Int,
        dest: Spanned?,
        dstart: Int,
        dend: Int
    ): CharSequence? {
        try {
            val input = Integer.parseInt(dest.toString() + source.toString())
            if (input <= 0) {
                return ""; // 输入的数字小于或等于0,返回空字符串
            }
        } catch (e: NumberFormatException) {
            return ""; // 输入的不是数字,返回空字符串
        }
        return null
    }
})
etInput.keyListener = DigitsKeyListener.getInstance("0123456789")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这个案例中,你不设置DigitsKeyListener也是可以保证只输入两位的正整数的,但是有个问题就是,输入法默认不给你显示数字输入键盘,需要手动切换。

定制输入框界面

如设置多行输入,自定义光标。

etInput.layoutParams = LinearLayout.LayoutParams(
    ViewGroup.LayoutParams.MATCH_PARENT,
    DensityUtils.dp2px(300f)
)
etInput.gravity = Gravity.TOP
etInput.isSingleLine = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    etInput.textCursorDrawable = resources.getDrawable(R.drawable.edit_text_bg)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

背景也可以定制,当然我们一般不这样做,有悖于框架的UI风格,哈哈。

MenuPanel的后期改进规划

  • 提供更多的MenuPanelItem
  • 可通过menuName获取到MenuPanelItem,当然这个API对于InputMenuPanelItem来说并没有什么用
  • 提供更贴心的构造函数,让使用者在用的时候可以少传参

总结

不管怎么说,输入菜单InputMenuPanelItem都是一个举足轻重的核心菜单项,对于它的优化,可不能马虎。MenuPanel遵循低代码设计理念,目的是为了让大家写代码就像拼积木一样,把能想到的尽可能多的考虑到,写代码就跟玩一样。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/736923
推荐阅读
相关标签
  

闽ICP备14008679号