Kotlin 基于DialogFragment封装的通用Dial

2021-01-02  本文已影响0人  爱上这阳光

一、背景

Dialog是项目中最常用的功能之一,为了减少在xml中重复定义底部两个按钮,所以在参考Android基于DialogFragment封装一个通用的Dialog的基础上使用Kotlin重写了BaseDialog。

二、相关源码:
BaseDialog.kt

import android.app.Dialog
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.TextUtils
import android.view.*
import androidx.annotation.LayoutRes
import androidx.annotation.StyleRes
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import kotlinx.android.synthetic.main.dialog_base.*

class BaseDialog() : DialogFragment() {

    companion object {
        const val BASE_DIALOG_TAG = "BASE_DIALOG"
        private var mFragment: FragmentManager? = null
        private var mBuildMethod: ((Dialog?, View?) -> Unit)? = null
        private var mDismissMethod: (() -> Unit)? = null
        private var mNegativeButtonMethod: ((Dialog?, View?) -> Unit)? = null
        private var mPositiveButtonMethod: ((Dialog?, View?) -> Unit)? = null
    }

    private var mLayoutRes: Int = 0
    private var mWidth: Int = 0
    private var mHeight: Int = 0
    private var mAnimRes: Int = 0
    private var mTitle: String? = null
    private var mContent: String? = null
    private var mMessage: String? = null
    private var mNegativeToDismiss = true
    private var mCanceledOnTouchOutside = false
    private var mBuildMethodAdded = false
    private var mDismissMethodAdded = false
    private var mNegativeButtonMethodAdded = false
    private var mPositiveButtonMethodAdded = false

    private val mButtonHandler =
        View.OnClickListener { view ->
            if (view === btn_dialog_cancel) {
                if (mNegativeToDismiss) dismiss()
                if (mNegativeButtonMethodAdded) {
                    mNegativeButtonMethod?.run {
                        invoke(dialog, getBaseView())
                    }
                }
            } else if (view === btn_dialog_ok) {
                if (mPositiveButtonMethodAdded) {
                    mPositiveButtonMethod?.run {
                        invoke(dialog, getBaseView())
                    }
                }
            }
        }

    init {
        arguments = Bundle()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.run {
            mLayoutRes = getInt("mLayoutRes")
            mWidth = getInt("mWidth")
            mHeight = getInt("mHeight")
            mTitle = getString("mTitle")
            mContent = getString("mContent")
            mMessage = getString("mMessage")
            mNegativeToDismiss = getBoolean("mNegativeToDismiss", true)
            mCanceledOnTouchOutside = getBoolean("mCanceledOnTouchOutside")
            mBuildMethodAdded = getBoolean("mBuildMethodAdded")
            mDismissMethodAdded = getBoolean("mDismissMethodAdded")
            mNegativeButtonMethodAdded = getBoolean("mNegativeButtonMethodAdded")
            mPositiveButtonMethodAdded = getBoolean("mPositiveButtonMethodAdded")
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.dialog_base, container, false)
        if (mLayoutRes > 0) {
            val contentView = inflater.inflate(mLayoutRes, null, false)
            (view.findViewById(R.id.ll_dialog_custom) as ViewGroup).addView(contentView, 0)
        }
        isCancelable = true
        dialog?.run {
            setCanceledOnTouchOutside(mCanceledOnTouchOutside)
        }
        if (mBuildMethodAdded) {
            mBuildMethod?.run {
                invoke(dialog, view)
            }
        }
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val showTitle = !TextUtils.isEmpty(mTitle)
        val showContent = !TextUtils.isEmpty(mContent)
        val showMessage = !TextUtils.isEmpty(mMessage)
        if (showTitle) {
            tv_dialog_title.text = mTitle
            tv_dialog_title_only.text = mTitle
        }
        if (showContent) {
            tv_dialog_content.text = mContent
            tv_dialog_title.visibility = if (showTitle) View.VISIBLE else View.GONE
        } else {
            tv_dialog_title_only.visibility = if (showTitle) View.VISIBLE else View.GONE
        }
        if (showMessage) tv_dialog_message.text = mMessage
        tv_dialog_content.visibility = if (showContent) View.VISIBLE else View.GONE
        tv_dialog_message.visibility = if (showMessage) View.VISIBLE else View.GONE

        btn_dialog_cancel.setOnClickListener(mButtonHandler)
        btn_dialog_ok.setOnClickListener(mButtonHandler)
    }

    override fun onStart() {
        super.onStart()
        val dialog = dialog ?: return
        val window = dialog.window ?: return
        val params = window.attributes
        // 设置背景色透明
        window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
        // 设置Dialog动画效果
        if (mAnimRes > 0) window.setWindowAnimations(mAnimRes)
        // 设置Dialog的宽度
        if (mWidth > 0) params.width = mWidth
        // 设置Dialog的高度
        if (mHeight > 0) params.height = mHeight
        // 设置屏幕透明度 0.0f~1.0f(完全透明~完全不透明)
        params.dimAmount = 0.4f
        params.gravity = Gravity.CENTER
        window.attributes = params
    }

    override fun dismiss() {
        if (fragmentManager == null) return
        super.dismissAllowingStateLoss()
    }

    override fun onDestroy() {
        if (mDismissMethodAdded) {
            mDismissMethod?.run {
                invoke()
            }
        }
        super.onDestroy()
    }

    private fun getBaseView(): View? {
        return view
    }

    fun setFragmentManager(fragment: FragmentManager): BaseDialog {
        mFragment = fragment
        return this
    }

    fun setDialogView(@LayoutRes layoutRes: Int): BaseDialog {
        mLayoutRes = layoutRes
        arguments?.putInt("mLayoutRes", mLayoutRes)
        return this
    }

    fun setWindowSize(width: Int, height: Int): BaseDialog {
        setWidth(width)
        setHeight(height)
        return this
    }

    fun setWidth(width: Int): BaseDialog {
        mWidth = width
        arguments?.putInt("mWidth", mWidth)
        return this
    }

    fun setHeight(height: Int): BaseDialog {
        mHeight = height
        arguments?.putInt("mHeight", mHeight)
        return this
    }

    fun setAnimStyle(@StyleRes animStyle: Int): BaseDialog {
        mAnimRes = animStyle
        arguments?.putInt("mAnimRes", mAnimRes)
        return this
    }

    fun setTitle(title: String): BaseDialog {
        mTitle = title
        arguments?.putString("mTitle", mTitle)
        return this
    }

    fun setContent(content: String): BaseDialog {
        mContent = content
        arguments?.putString("mContent", mContent)
        return this
    }

    fun setMessage(message: String): BaseDialog {
        mMessage = message
        arguments?.putString("mMessage", mMessage)
        return this
    }

    fun setNegativeToDismiss(negativeToDismiss: Boolean): BaseDialog {
        mNegativeToDismiss = negativeToDismiss
        arguments?.putBoolean("mNegativeToDismiss", mNegativeToDismiss)
        return this
    }

    fun setCanceledOnTouchOutside(canceledOnTouchOutside: Boolean): BaseDialog {
        mCanceledOnTouchOutside = canceledOnTouchOutside
        arguments?.putBoolean("mCanceledOnTouchOutside", mCanceledOnTouchOutside)
        return this
    }

    fun setBuildMethod(buildMethod: (Dialog?, View?) -> Unit): BaseDialog {
        mBuildMethod = buildMethod
        arguments?.putBoolean("mBuildMethodAdded", true)
        return this
    }

    fun setDismissMethod(dismissMethod: () -> Unit): BaseDialog {
        mDismissMethod = dismissMethod
        arguments?.putBoolean("mDismissMethodAdded", true)
        return this
    }

    fun setNegativeButtonMethod(negativeButtonMethod: (Dialog?, View?) -> Unit): BaseDialog {
        mNegativeButtonMethod = negativeButtonMethod
        arguments?.putBoolean("mNegativeButtonMethodAdded", true)
        return this
    }

    fun setPositiveButtonMethod(positiveButtonMethod: (Dialog?, View?) -> Unit): BaseDialog {
        mPositiveButtonMethod = positiveButtonMethod
        arguments?.putBoolean("mPositiveButtonMethodAdded", true)
        return this
    }

    fun show() {
        mFragment?.let {
            val ft = it.beginTransaction()
            try {
                val cls: Class<*> = DialogFragment::class.java
                val mDismissed = cls.getDeclaredField("mDismissed")
                mDismissed.isAccessible = true
                mDismissed[this] = false
                val mShownByMe = cls.getDeclaredField("mShownByMe")
                mShownByMe.isAccessible = true
                mShownByMe[this] = true
            } catch (e: Exception) {
                super.show(ft, BASE_DIALOG_TAG)
                return
            }
            val prev = it.findFragmentByTag(BASE_DIALOG_TAG)
            if (prev != null) {
                ft.remove(prev)
            }
            ft.add(this, BASE_DIALOG_TAG)
            ft.commitAllowingStateLoss()
        }
    }

}

dialog_base.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:background="@drawable/shape_bg_dialog_base"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingHorizontal="15dp">

        <TextView
            android:id="@+id/tv_dialog_title_only"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:gravity="center"
            android:lineSpacingExtra="6dp"
            android:text="订单提交成功"
            android:textColor="#333"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/tv_dialog_title"
            android:layout_width="match_parent"
            android:layout_height="52dp"
            android:gravity="center"
            android:lineSpacingExtra="6dp"
            android:text="订单提交成功"
            android:textColor="#333"
            android:textSize="18sp"
            android:visibility="gone" />

        <TextView
            android:id="@+id/tv_dialog_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:lineSpacingExtra="6dp"
            android:text="我们将在30分钟处理,稍后通知您订单结果!感谢您选择"
            android:textColor="#666"
            android:textSize="16sp"
            android:visibility="gone" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_dialog_custom"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_dialog_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15dp"
            android:gravity="center"
            android:lineSpacingExtra="6dp"
            android:minHeight="56dp"
            android:text="订单提交成功"
            android:textColor="#292d33"
            android:textSize="17sp"
            android:visibility="gone" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginTop="15dp"
        android:background="@color/dividerColor" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="52dp">

        <Button
            android:id="@+id/btn_dialog_cancel"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/selector_bg_dialog_base_btn_cancel"
            android:gravity="center"
            android:stateListAnimator="@null"
            android:text="@android:string/cancel"
            android:textColor="#999"
            android:textSize="16sp" />

        <View
            android:layout_width="0.5dp"
            android:layout_height="match_parent"
            android:background="@color/dividerColor" />

        <Button
            android:id="@+id/btn_dialog_ok"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/selector_bg_dialog_base_btn_ok"
            android:gravity="center"
            android:stateListAnimator="@null"
            android:text="@android:string/ok"
            android:textColor="#393f50"
            android:textSize="16sp" />
    </LinearLayout>

</LinearLayout>

shape_bg_dialog_base.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/white" />
    <corners android:radius="8dp" />
</shape>

selector_bg_dialog_base_btn_ok.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="false">
        <shape>
            <solid android:color="#FFF"></solid>
            <corners android:bottomRightRadius="8dp" />
        </shape>
    </item>
    <item android:state_pressed="true">
    <shape>
        <solid android:color="#F0F0F0" />
        <corners android:bottomRightRadius="8dp" />
    </shape>
    </item>
</selector>

selector_bg_dialog_base_btn_cancel.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="false">
        <shape>
            <solid android:color="#FFF"></solid>
            <corners android:bottomLeftRadius="8dp" />
        </shape>
    </item>
    <item android:state_pressed="true">
        <shape>
            <solid android:color="#F0F0F0" />
            <corners android:bottomLeftRadius="8dp" />
        </shape>
    </item>
</selector>

三、使用文档

1、使用内置样式:

BaseDialog()
    .setFragmentManager(supportFragmentManager)
    .setMessage("确定删除?")
    .setCanceledOnTouchOutside(true) // 点击dialog外部关闭dialog
    .setDismissMethod { // Dialog消失回调
        ...
    }
    .setPositiveButtonMethod { dialog, view -> // 点击底部右侧按钮回调
        dialog?.dismiss()
        ...
    }
    .show()

2、加入自定义样式:

BaseDialog()
    .setFragmentManager(supportFragmentManager)
    .setTitle("添加xx")
    .setDialogView(R.layout.dialog_xx)
    .setWindowSize(800, 600)
    .setBuildMethod { _, view -> // DialogFragment onCreateView方法时回调
        view?.run {
            findViewById<EditText>(R.id.xx).setText("")
            ...
        }
    }
    .setNegativeToDismiss(false) // 取消点击底部左侧按钮自动关闭dialog
    .setNegativeButtonMethod { dialog, view -> // 点击底部左侧按钮回调
        ...
        dialog?.dismiss()
    }
    .show()
上一篇下一篇

猜你喜欢

热点阅读