Android低代码开发 - InputMenuPanelItem详解

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

InputMenuPanelItem源码

kotlin 复制代码
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
    }
}

属性解析

marginTop:上边距

title和titleSpan:标题和标题边距

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

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

content:输入内容,同android:text

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

使用场景

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

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

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

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

使用说明书

是否需要指定menuName

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

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

kotlin 复制代码
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(), "")
        }
    }
}

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

获取到InputMenuPanelItem里面的EditText

kotlin 复制代码
getCacheChildView()

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

kotlin 复制代码
companion object {
    @JvmField
    val ID_EDIT_TEXT_INPUT: Int = R.id.et_menu_panel_input
}
kotlin 复制代码
getCacheViewFromItem()

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

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

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

android:maxLength代码指定

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

kotlin 复制代码
etInput.filters = arrayOf(InputFilter.LengthFilter(10))
android:digits代码指定

如设置只能输入数字。

kotlin 复制代码
etInput.keyListener = DigitsKeyListener.getInstance("0123456789")
组合使用

只能输入2位的正整数。

kotlin 复制代码
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")

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

定制输入框界面

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

kotlin 复制代码
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)
}

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

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

总结

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

相关推荐
大白要努力!36 分钟前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟1 小时前
Android音频采集
android·音视频
小白也想学C3 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程3 小时前
初级数据结构——树
android·java·数据结构
闲暇部落5 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX7 小时前
Android 分区相关介绍
android
大白要努力!8 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee8 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood8 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-11 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记