(Kotlin) Android使用DialogX实现iOS风格底部弹窗(带Toggle开关)

本文将详细介绍如何使用DialogX库实现一个iOS风格的底部弹窗,包含图标文本Toggle 开关的列表项。
实现步骤
1. 添加依赖

build.gradle文件中添加:

复制代码
implementation 'com.github.kongzue.DialogX:DialogX:0.0.49.beta14'
implementation 'com.github.kongzue.DialogX:DialogXIOSStyle:0.0.49.beta14'
implementation 'androidx.recyclerview:recyclerview:1.3.2'

2. 创建布局文件

底部弹窗主布局 (dialog_bottom_toggle.xml)

复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="@drawable/bg_bottom_dialog_round_top">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:maxHeight="400dp"/>

    <Button
        android:id="@+id/btn_cancel"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:text="取消"
        android:background="?attr/selectableItemBackground"/>
</LinearLayout>

列表项布局 (item_toggle.xml)

复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    android:orientation="horizontal"
    android:paddingStart="24dp"
    android:paddingEnd="16dp"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginEnd="16dp"/>

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="16sp"/>

    <androidx.appcompat.widget.SwitchCompat
        android:id="@+id/switch_toggle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

3. 实现工具类

复制代码
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.SwitchCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.kongzue.dialogx.dialogs.CustomDialog
import com.kongzue.dialogx.interfaces.OnBindView
import com.kongzue.dialogx.style.IOSStyle
import smartconnection.com.smartconnect.R

object BottomToggleDialog {

    data class ToggleItem(
        val iconRes: Int,
        val title: String,
        var isChecked: Boolean = false,
        val onToggleChanged: ((Boolean) -> Unit)? = null
    )

    /**
     * 使用CustomDialog的可靠实现
     */
    fun show(
        context: Context,
        title: String? = null,
        items: List<ToggleItem>
    ) {
        CustomDialog.build()
            .setCustomView(object : OnBindView<CustomDialog>(R.layout.dialog_bottom_toggle) {
                override fun onBind(dialog: CustomDialog?, view: View?) {
                    view?.apply {
                        // 绑定视图
                        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
                        val tvTitle = findViewById<TextView>(R.id.tv_title)
                        val btnCancel = findViewById<Button>(R.id.btn_cancel)

                        // 设置标题
                        title?.let { tvTitle.text = it } ?: run { tvTitle.visibility = View.GONE }

                        // 配置列表
                        recyclerView.layoutManager = LinearLayoutManager(context)
                        recyclerView.adapter = ToggleAdapter(items).apply {
                            onItemClick = { position, isChecked ->
                                items[position].isChecked = isChecked
                                items[position].onToggleChanged?.invoke(isChecked)
                            }
                        }

                        // 取消按钮
                        btnCancel.setOnClickListener { dialog?.dismiss() }
                    }
                }
            })
            .setStyle(IOSStyle.style())
            .setAlign(CustomDialog.ALIGN.BOTTOM)
            .setCancelable(true)
            .show()
    }

    private class ToggleAdapter(private val items: List<ToggleItem>) :
        RecyclerView.Adapter<ToggleAdapter.ViewHolder>() {

        var onItemClick: ((Int, Boolean) -> Unit)? = null

        class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            val ivIcon: ImageView = view.findViewById(R.id.iv_icon)
            val tvTitle: TextView = view.findViewById(R.id.tv_title)
            val switch: SwitchCompat = view.findViewById(R.id.switch_toggle)
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_toggle, parent, false)
            return ViewHolder(view)
        }

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            val item = items[position]
            holder.ivIcon.setImageResource(item.iconRes)
            holder.tvTitle.text = item.title
            holder.switch.isChecked = item.isChecked

            holder.switch.setOnCheckedChangeListener { _, isChecked ->
                onItemClick?.invoke(position, isChecked)
            }

            holder.itemView.setOnClickListener {
                holder.switch.toggle()
            }
        }

        override fun getItemCount() = items.size
    }
}

使用示例

复制代码
// 准备数据
val items = listOf(
    BottomToggleDialog.ToggleItem(
        iconRes = R.drawable.ic_notification,
        title = "消息通知",
        isChecked = true
    ) { isChecked ->
        Toast.makeText(this, "通知状态: $isChecked", Toast.LENGTH_SHORT).show()
    },
    BottomToggleDialog.ToggleItem(
        iconRes = R.drawable.ic_dark_mode,
        title = "暗黑模式",
        isChecked = false
    ) { isChecked ->
        Toast.makeText(this, "暗黑模式: $isChecked", Toast.LENGTH_SHORT).show()
    }
)

// 显示弹窗
BottomToggleDialog.show(
    context = this,
    title = "功能设置",
    items = items
)

关键点说明
1.底部显示控制:

setAlignBottom() 确保内容对齐底部

自定义动画实现滑动效果

顶部圆角背景优化视觉效果

2.数据绑定:

使用RecyclerView实现高效列表

通过回调处理Toggle状态变化

相关推荐
程序员江同学40 分钟前
Kotlin 技术月报 | 2025 年 4 月
android·kotlin
SlowFeather1 小时前
Unity 使用 ADB 实时查看手机运行性能
android·unity·adb·性能优化·profiler
Android 小码峰啊1 小时前
Android Compose 框架矢量图标深入剖析(七)
android
Chrome深度玩家5 小时前
谷歌翻译安卓版拍照翻译精准度与语音识别评测【轻松交流】
android·人工智能·语音识别
长点点6 小时前
从架构角度了解安卓APP(1):安卓核心组件的设计逻辑与演进
android·架构·app
用户71887350336806 小时前
Android适配最新SplashScreen方案
android·android jetpack
用户3031057186856 小时前
Kotlin协程的介绍
kotlin
EQ-雪梨蛋花汤6 小时前
【Part 2安卓原生360°VR播放器开发实战】第二节|基于等距圆柱投影方式实现全景视频渲染
android·音视频·vr
Railshiqian7 小时前
Framework.jar里的类无法通过Class.forName反射某个类的问题排查
android·反射·framework.jar