raedev / androidx-swift

Android 常用组件库
Apache License 2.0
1 stars 0 forks source link

记得有个表单库的呀 #1

Closed UniqHu closed 10 months ago

UniqHu commented 10 months ago

大神,记得你有个安卓动态表单库,我记得写得特别好收藏了,现在打开404了,还能分享不

raedev commented 10 months ago

这个表单库已经删除不再开源了,但你可以从maven库中继续依赖使用。

buildscript {
    repositories {
       maven { url 'https://maven.raeblog.com/repository/public/' }
    }
}
dependencies {
    // 表单库必须组件
    implementation 'com.github.raedev:android-forms:1.0.1'
    // (可选)若使用动态表单加载可引用generator库
    implementation 'com.github.raedev:android-forms-generator:1.0.1'
}

示例代码


class MainActivity : AppCompatActivity() {

    private val adapter = FormGroupAdapter()
    lateinit var render: DataBindingFormRender

    private val formGroup: FormGroup
        get() = adapter.formGroup

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<View>(R.id.btn_anim).setOnClickListener {
            FormSelectDialogFragment().apply {
                provider = SimpleDataProvider(
                    listOf(
                        FormSelectItem("渐入动画", "1"),
                        FormSelectItem("放大动画", "2"),
                        FormSelectItem("上侧滑入", "3"),
                        FormSelectItem("下侧滑入", "4"),
                        FormSelectItem("左侧滑入", "5"),
                        FormSelectItem("右侧滑入", "6"),
                    )
                )
                listener = object : FormSelectChangedListener {
                    override fun onFormSelectChanged(item: FormSelectItem) {
                        adapter.itemAnimator = when (item.value) {
                            "1" -> AlphaInAnimation()
                            "2" -> ScaleInAnimation()
                            "3" -> SlideInAnimation(Gravity.TOP)
                            "4" -> SlideInAnimation(Gravity.BOTTOM)
                            "5" -> SlideInAnimation(Gravity.START)
                            "6" -> SlideInAnimation(Gravity.END)
                            else -> null
                        }
                        formGroup.refresh()
                    }
                }
            }.show(supportFragmentManager, "animSelect")
        }
        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
        // Item 动画,默认不开启动画。
//        adapter.itemAnimator = AlphaInAnimation()
        adapter.itemAnimator = ScaleInAnimation()
//        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = FormLayoutManager(this)
        recyclerView.adapter = adapter
        recyclerView.addItemDecoration(
            FormItemDecoration(
                this,
                Color.TRANSPARENT,
//                Color.BLACK,
//                ContextCompat.getColor(this, com.raedev.forms.R.color.form_divider_color),
                0.5f,
                40
            )
        )
        // 初始化表单渲染器
        this.render = DemoFormRender(this, adapter, supportFragmentManager)

        initViewListener()
        photoDemo()
        // 初始化示例
        dataBindingDemo()
        editTextDemo()
        radioDemo()
        dateDemo()
        selectDemo()
        parentAndChildrenDemo()
        otherDemo()

        // 自定义的表单
        formGroup.addItem(CustomFormItem())
    }

    private fun initViewListener() {
        // 按钮事件监听
        findViewById<Button>(R.id.btn_entity).setOnClickListener {
            val text = "实体值:${render.toJson()}"
            Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
            Log.d("rae", text)
        }
        findViewById<Button>(R.id.btn_form).setOnClickListener {
            val text = "表单值:${formGroup.toMap()}"
            Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
            Log.d("rae", text)
        }
        findViewById<Button>(R.id.btn_viewonly).setOnClickListener {
            formGroup.viewonly = !formGroup.viewonly
            it as Button
            it.text = if (formGroup.viewonly) "可编辑" else "只读"
        }
        findViewById<Button>(R.id.btn_validate).setOnClickListener {
            val result = formGroup.validateForm()
            if (result.successfully()) {
                Toast.makeText(this, "校验成功", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }
            AlertDialog.Builder(this)
                .setTitle("表单校验失败")
                .setMessage(result.message)
                .setPositiveButton("确定", null)
                .show()
//            Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
//            Log.e("rae", text)
        }
        var randomPosition = 0
        findViewById<Button>(R.id.btn_nav).setOnClickListener {
            val items = mutableListOf<FormItem>()
            formGroup.forEach {
                if (it.formType == FormType.Group.ordinal) {
                    items.add(it)
                }
            }
            randomPosition = (randomPosition + 1) % items.size
            val item = items[randomPosition]
            Toast.makeText(this, "滚动到:${item.label}", Toast.LENGTH_SHORT).show()
            render.smoothScrollTo(item)
        }
        findViewById<Button>(R.id.btn_collapse).setOnClickListener {
            it as Button
            if (it.text == "折叠组") {
                formGroup.collapseAll()
                it.text = "展开组"
            } else {
                formGroup.expandAll()
                it.text = "折叠组"
            }
        }
        findViewById<Button>(R.id.btn_form_state).setOnClickListener {
            if (it.tag == 1) {
                render.showEmpty()
                it.tag = 0
            } else {
                render.showLoading()
                it.tag = 1
            }
        }
    }

    /**
     * 文本编辑框示例
     */
    private fun editTextDemo() {
        val etName = "ET"
        render.addGroup("输入框示例") {
            addText("单纯文本", etName.random(), "我是文本框")
            addEditText("普通文本", etName.random(), "hello")
            addEditText("只读文本", etName.random(), "word").apply { viewonly = true }
            addMultiEditText("多行文本", etName.random(), "多行文本")
            addNumberEditText("输入纯数字", etName.random()).apply { unit = "平方米" }
            addNumberEditText("输入面积(自动保留2位)", etName.random())
            addNumberEditText("手机号码(自动11位)", etName.random(), required = true)
            addNumberEditText("限制长度", etName.random()).apply {
                this.hint = "最大长度为6"
                this.maxLength = 6
                this.unit = "位"
            }
            addNumberEditText("表单过滤器", etName.random()).apply {
                this.hint = "输入666拦截"
                this.filter = object : FormValueFilter {
                    override fun filter(item: FormItem, value: String?): String? {
                        if (value == "666") {
                            Toast.makeText(this@MainActivity, "触发拦截:$value", Toast.LENGTH_SHORT)
                                .show()
                            return null
                        }
                        return value
                    }
                }
            }
        }
    }

    /**
     * 拍照框示例
     */
    private fun photoDemo() {
        // 照片回调处理, 在添加照片表单项之前设置。
        render.photoListener = object : DefaultFormPhotoListener(this) {
            override fun onItemPhotoDeleteClick(
                formItem: PhotoFormItem,
                item: FormPhotoEntity,
                emit: (Boolean) -> Unit
            ) {
                // 模拟删除动作
                lifecycleScope.launchWhenStarted {
                    // 模拟延时处理
                    delay(1000)
                    // 随机结果
                    val success: Boolean = Random.nextInt(10) % 2 == 1
                    emit(success)
                    Toast.makeText(
                        applicationContext,
                        "${if (success) "删除成功" else "删除失败"}:${formItem.label}的${item.title}",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }
        render.addGroup("照片示例") {
            val photos = mutableListOf<FormPhotoEntity>(
                FormPhotoEntity(
                    "示例照片1",
                    "https://mmbiz.qpic.cn/mmbiz/W2LicmlfP6YRk42nk2BGvDfKUunZaynVIngcgBAfRx906HJBia8UBpWlrsrTMSxGxAywnjgz6iaS1HibNIOEeN7ibtw/640"
                ),
                FormPhotoEntity(
                    "示例照片2",
                    "https://mmbiz.qpic.cn/mmbiz/W2LicmlfP6YRk42nk2BGvDfKUunZaynVIFzpdwC0Smf6IBLqpoCpM0iaFkMv3iaiblB9VIQwxDVMrE2lprjZtIsK8g/640"
                ),
                FormPhotoEntity(
                    "示例照片3",
                    "https://mmbiz.qpic.cn/mmbiz/W2LicmlfP6YRk42nk2BGvDfKUunZaynVIsb227KvakJL2589n7eNZBbBg8ia0BuXVDo5dQuguSOjfwticyTicVB9AQ/640"
                ),
                FormPhotoEntity(
                    "示例照片4",
                    "https://mmbiz.qpic.cn/mmbiz/W2LicmlfP6YRk42nk2BGvDfKUunZaynVIA0zRIuUichzyKhTxianH8pOGr1ujdaDDfrQUfZakL5LxOeiaHxQnvN5kQ/640"
                ),
                FormPhotoEntity(
                    "示例照片5",
                    "https://mmbiz.qpic.cn/mmbiz/gsRxk9yxfOV6XJF6Qfl204kW0Vy4Eic9fic1OGPMBMMqkufwTQ0f2A1ianegtNxZrOwLIsThb0gibTKhPu9XjnF6tQ/640"
                )
            )
            addPhoto(
                "活动照片(限制数量3到9张)",
                "hdzp",
                photos.subList(0, 3),
                min = 3,
                max = 9,
                deletable = true
            )
            addPhoto("花絮照片(不限制数量)", "hxzp", deletable = true)
            addPhoto("出图照片(不提供删除)", "ctzp", photos, max = 9)
        }
    }

    /**
     * 单选框示例
     */
    private fun radioDemo() {
        render.addGroup("单选框示例") {
            addRadioGroup("单选框1", "radio1", null, false)
            addRadioGroup("单选框2", "radio2", true.toString(), false).apply {
                this.hint = "默认值为TRUE"
                this.setCheckBoxLabel("真", "假")
            }
        }
    }

    /**
     * 日期示例
     */
    private fun dateDemo() {
        render.addGroupTitle("日期示例")
        render.addDate("普通日期", randomName())
        render.addDate("初始化日期", randomName(), "2022-10-01")
        render.addDate("不同样式的日期", randomName())
        render.addDate("指定日期(20220901~20221001)", randomName()).apply {
            this.startDate = "2022-09-01"
            this.endDate = "2022-10-01"
        }
    }

    /**
     * 选择框示例
     */
    private fun selectDemo() {
        // 初始化数据提供
        val data = mutableListOf<FormSelectItem>().apply {
            for (i in 0 until 20) {
                val item = FormSelectItem("演示项$i", "parent$i", order = -1)
                this.add(item)
                if (i < 2) {
                    // 添加子项
                    for (k in 0 until 5) {
                        item.addChild(FormSelectItem("演示项${i}-子项$k", "node$i-$k", order = k))
                    }
                }
            }

            val item = FormSelectItem("多级演示项", "parent").apply {
                addChild(FormSelectItem("二级选项1", "level2-1"))
                addChild(FormSelectItem("二级选项2", "level2-2"))
                addChild(FormSelectItem("二级选项3", "level2-3").apply {
                    addChild(FormSelectItem("三级选项1", "level3-1"))
                    addChild(
                        FormSelectItem(
                            "三级选项222222222222333333333333333444444444",
                            "level3-2"
                        ).apply {
                            addChild(FormSelectItem("四级选项1", "level4-1"))
                            addChild(FormSelectItem("四级选项2", "level4-2"))
                            addChild(FormSelectItem("四级选项3", "level4-3"))
                            addChild(FormSelectItem("四级选项3", "level4-4"))
                        })
                    addChild(FormSelectItem("三级选项3", "level3-3"))
                    addChild(FormSelectItem("三级选项4", "level3-4"))
                })
            }
            this.add(item)
        }

        render.addGroupTitle("选择框示例")
        render.addSelect(
            SimpleDataProvider(data),
            "简单的选择框",
            randomName(),
            null,
            dialogTitle = "我是自定义标题"
        )
        render.addSelect(SimpleDataProvider(data), "自动选择演示项5", randomName(), "parent5")
        render.addSelect(
            SimpleDataProvider(data),
            "自动选择二级",
            randomName(),
            "node1-2"
        )
        render.addSelect(
            SimpleDataProvider(data).apply {
                pathSeparator = "-"
                enableCheckParent = true
            },
            "自动选择三级",
            randomName(),
            "level3-1"
        )
    }

    /**
     * 父子表单示例关联示例
     */
    private fun parentAndChildrenDemo() {
        render.addGroup("表单关联示例") {
            val rootItem = render.addCheckBox("表单关联", "parent1", false.toString(), true).apply {
                hint = "打开试试看吧"
                setCheckBoxLabel("打开", "关闭")
            }
            rootItem.setFormChangedListener { item, _, _ ->
                // 移除所有子项目
                item.removeAllChildren()
                if (!(item as CheckBoxFormItem).checked) return@setFormChangedListener
                // 二级表单
                render.addEditTextChild(item, "1-1表单", "parent-node1", required = true)
                render.addEditTextChild(item, "1-2表单", "parent-node2")
                render.addEditTextChild(item, "1-3表单", "parent-node3")
                // 三级表单
                render.addRadioGroupChild(item, "继续打开关联", "parent-node4", null).apply {
                    this.setFormChangedListener { subItem, value, _ ->
                        // 再添加子表单
                        subItem.removeAllChildren()
                        if ("true" == value) {
                            render.addEditTextChild(
                                subItem,
                                "1-4-1表单",
                                "parent-node4-node1",
                                null
                            )
                        }
                    }
                }
            }
        }
    }

    /**
     * 数据绑定示例
     */
    private fun dataBindingDemo() {
        render.addGroup("数据绑定示例") {
            val entity = DemoEntity().apply {
                age = 18
                sex = 1
                sfbd = 1
                birthday = "2022-09-01"
                workCity = "BJ"
                lastWorkCity = "GZ"
                testSelection = ""
            }
            val data = mutableListOf<FormSelectItem>().apply {
                add(FormSelectItem("北京", "WORK.BJ"))
                add(FormSelectItem("上海", "WORK.SH"))
                add(FormSelectItem("广州", "WORK.GZ"))
                add(FormSelectItem("深圳", "WORK.SZ"))
                add(FormSelectItem("成都", "WORK.CD"))
                add(FormSelectItem("测试选项1", "TEST.01"))
                add(FormSelectItem("测试选项2", "TEST.02"))
                add(FormSelectItem("测试选项3", "TEST.03"))
            }
            // 字典表
            render.provider = SimpleDataProvider(data)
            render.bind(entity)
            render.render()
        }

    }

    private fun otherDemo() {
        render.addGroupTitle("其他示例")
        for (i in 0 until 10) {
            render.addEditText("测试$i", "test$i")
            if (i == 2 || i == 6 || i == 9) render.addPhoto(
                "测试照片$i",
                "test-photo-$i",
                max = i,
                deletable = true
            )
        }
    }

    /**
     * 随机生成表单名
     */
    private fun randomName(prefix: String? = ""): String {
        return prefix + "-" + UUID.randomUUID().toString()
    }

    private fun String.random(): String {
        return randomName(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        render.unbind()
    }
}

/**
 * 动态表单示例
 * @author RAE
 * @date 2023/05/10
 * @copyright Copyright (c) https://github.com/raedev All rights reserved.
 */
class DynamicFormActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic)
        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
        val adapter = FormGroupAdapter()
        adapter.itemAnimator = AlphaInAnimation()
        recyclerView.layoutManager = FormLayoutManager(this)
        recyclerView.addItemDecoration(
            FormItemDecoration(this, bottom = 0)
        )
        recyclerView.adapter = adapter
        // 数据源绑定
        val binder = HookFormValueBinder(DemoInfo())
        // 表单渲染器
        val render = FormGeneratorRender(this, adapter, binder, supportFragmentManager)
        // 照片选择
        render.photoListener = DefaultFormPhotoListener(this)
        // 读取JSON 配置
        val stream = this.assets.open("demo.json")
        val out = ByteArrayOutputStream()
        val buffer = ByteArray(1024)
        var length = 0
        while (true) {
            length = stream.read(buffer)
            if (length == -1) break
            out.write(buffer, 0, length)
        }
        val json = out.toString().also { out.close() }.also { stream.close() }

        // 交给表单生成工厂处理
        lifecycle.coroutineScope.launch {
            FormGeneratorFactory(FormJson.from(json)).generate(render)
        }

        findViewById<Button>(R.id.btn_validate).setOnClickListener {
            val result = render.formGroup.validateForm()
            if (result.successfully()) {
                Toast.makeText(this, "校验成功", Toast.LENGTH_SHORT).show()
            } else {
                AlertDialog.Builder(this).setTitle("校验失败").setMessage(result.message)
                    .setPositiveButton("确定", null).show()
            }
        }
        findViewById<Button>(R.id.btn_value).setOnClickListener {
            val map = render.formGroup.toMap()
            val json = GsonBuilder().setPrettyPrinting()
                .create().toJson(map)
            AlertDialog.Builder(this).setTitle("表单值").setMessage(json)
                .setPositiveButton("确定", null).show()
            Log.d("rae", "表单值为:\n$json")
        }

    }

    private class DemoInfo(
        var username: String = "rae",
        var age: Int = 18,
        var workPlace: String? = null
    )
}