Closed UniqHu closed 9 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
)
}
大神,记得你有个安卓动态表单库,我记得写得特别好收藏了,现在打开404了,还能分享不