簡單封裝一個易拓展的Dialog

語言: CN / TW / HK

本文正在參加「金石計劃 . 瓜分6萬現金大獎」

Dialog,每個專案中多多少少都會用到,肯定也會有自己的一套封裝邏輯,無論如何封裝,都是奔著簡單複用的思想,有的是深層次的封裝,也就是把相關的UI效果直接封裝好,暴露可以修改的屬性和方法,讓呼叫者根據實際業務,呼叫修改即可,當然也有簡單的封裝,只封裝基本的功能,其UI和實際的動作,交給呼叫者,兩種封裝方式,各有利弊,前者呼叫者不用自己建立UI和實現相關動作,只需要簡單的呼叫即可,但是不易於擴充套件,效果比較侷限,想要拓展其他的效果,就不得不自己動手實現;後者擴充套件性強,因為只提供基本的呼叫方式,也就是說,你想要什麼效果都行,畢竟是所有的UI和動作都是你自己來實現,優點是它,其缺點也是它。

前者的封裝司空見慣,大多數的公司也都是採取的這樣的封裝,畢竟呼叫者實現起來也是很方便,這裡就不詳細說了,具體我們談一下後者的封裝,後者的封裝雖然呼叫者需要自己來實現,但是擴充套件性是很強的。

今天的內容大致如下:

1、效果及程式碼具體呼叫。

2、如何封裝一個Dialog。

3、開源地址。

4、總結及注意事項。

一、效果及程式碼具體呼叫

通過Kotlin的擴充套件函式,引數以類做為擴充套件,封裝之後,呼叫非常的便捷,只需要傳遞你要的檢視即可,我們先看下具體的案例,程式碼如下:

kotlin showVipDialog { addLayout(R.layout.layout_dialog_custom)//傳遞dialog檢視 set { //Dialog操作,獲取View及繫結資料 } }

通過以上的程式碼,我們就實現了一個Dialog的彈出,addLayout方法傳遞檢視,set擴充套件函式進行獲取View和繫結資料,這樣的一個簡單的封裝,我們就實現了Dialog的擴充套件操作,針對不同的Dialog樣式,傳遞不同的xml檢視即可。

1、快速使用

為了方便大家使用,目前已經上傳到了遠端maven,大家可以進行依賴使用,或者下載原始碼依賴也可以。

根專案build.gradle

allprojects { repositories { …… maven { url "https://gitee.com/AbnerAndroid/almighty/raw/master" } } }

在需要的Module下引入依賴

dependencies { …… implementation "com.vip:dialog:1.0.0" }

2、程式碼案例

原始碼下載之後,執行專案,就可以看到給大家提供的相關Demo,當然了,由於做到了可擴充套件,大家想實現什麼樣的效果都是可以的,畢竟檢視都是自己傳遞的。

由於所有的案例都是呼叫開頭的程式碼,就不一一列舉了,簡單的列舉幾個。

普通的提示框

普通的提示框,可以按照下面的程式碼邏輯進行呼叫。

kotlin showVipDialog { addLayout(R.layout.layout_dialog_custom)//新增彈出的檢視 set {//邏輯處理,獲取view,繫結資料 setDialogCancelable(false)//點選空白不消失 val btnConfirm = findView<TextView>(R.id.dialog_button_confirm)//獲取View btnConfirm.setOnClickListener { toast("確定") dismiss() } } }

方法一覽

| 方法名 | 引數型別 | 概述 | | ------------------- | ------- | --------------------------- | | addLayout | int | xml檢視 | | set | 無參 | 邏輯處理 | | style | 無參 | dialog設定樣式 | | setDialogCancelable | Boolean | 點選空白是否消失,預設true消失,false為不消失 | | findView | int | 控制元件id,泛型為控制元件 | | dismiss | 無參 | 隱藏dialog | | getDialogView | 無參 | 獲取當前View檢視 |

DataBinding形式的提示框

DataBinding形式和普通的區別在於,不用再獲取View檢視,由普通的set擴充套件函式改為bind擴充套件函式,泛型為Binding,記得把xml檢視進行convert to data binding layout。

kotlin showVipDialog { addLayout(R.layout.layout_dialog_custom)//新增彈出的檢視 bind<LayoutDialogCustomBinding> {//邏輯處理,獲取view,繫結資料 it.dialogButtonConfirm.setOnClickListener { toast("確定") dismiss() } } }

方法一覽

除了普通的方法呼叫之外,還可以呼叫下面的方法。

| 方法名 | 引數型別 | 概述 | | ------------------ | --- | ------------------------------- | | bind | 無參 | 和set一樣進行邏輯處理,泛型為ViewDataBinding | | getDataBinding | 無參 | 獲取當前的DataBinding,用於更新檢視 | | setPendingBindings | int | 傳遞的BR,用於xml和Data資料進行繫結 |

具體的案例大家直接可以看原始碼,原始碼中提供了很多常見的效果,都是可以自定義實現的,具體的就不羅列了,本身沒有多少難度。

確認框

輸入框

底部列表

菊花載入

二、如何封裝一個Dialog

這樣的一個簡單的Dialog如何進行封裝呢?在封裝之前,我們首先要明確封裝思路,1、檢視由呼叫者傳遞,2、邏輯操作由呼叫者處理,3、樣式也由呼叫者進行設定,也就是說,我們只封裝基本的dialog使用,也就是一個殼,具體的內容,統統交給呼叫者進行處理,有了這三個思路我們就可以進行著手封裝了。

1、封裝BaseDialog

封裝Base的原因,在於統一管理子類,在於簡化子類的程式碼邏輯,便於提供公共的方法讓子類實現或呼叫,BaseDialog這裡繼承的是DialogFragment,最大的原因就是,容易通過生命週期回撥來管理彈窗,還有對於複雜樣式的彈窗,使用DialogFragment會更加方便和高效。

和之前封裝Activity一樣,做為一個抽象父類,子類要實現的無非就是,檢視的傳遞和邏輯的處理,我們就可以在父類中進行定義抽象方法,Dialog一般有自己定義的樣式,我們也可以定義一個初始化樣式的方法。

```kotlin /* * AUTHOR:AbnerMing * INTRODUCE:初始化資料 / abstract fun initData()

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:初始化樣式
 */
abstract fun initStyle()

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:傳遞的檢視
 */
abstract fun getLayoutId(): Int

```

除了必要實現的方法之外,我們還可以把一些公用的方法,定義到Base裡,如獲取View的方法,獲取控制元件的方法等,這麼做的目的,便於子類自定義實現一些效果以及減少findViewById的呼叫次數。

```kotlin /* * AUTHOR:AbnerMing * INTRODUCE:獲取View檢視 / fun findView(id: Int): View { var view = mViewSparseArray[id] if (view == null) { view = mView?.findViewById(id) mViewSparseArray.put(id, view) } return view }

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:獲取當前View檢視
 */
fun getDialogView(): View {
    return mView!!
}

```

以上只是列舉了幾個實現的方法,完整的程式碼,大家可以看原始碼中的BaseDialog類。

2、拓展ViewDataBinding形式Dialog

正常的普通Dialog就可以繼承BaseDialog,基本就可以滿足需要的,若是要和ViewDataBinding進行結合,那麼就需要拓展需求了,具體的拓展也很簡單,一是繫結View,二是繫結資料,完整的程式碼,大家可以看原始碼中BaseBindingDialog類。

繫結View

通過DataBindingUtil的bind方法,得到ViewDataBinding。

kotlin mBinding = DataBindingUtil.bind(getDialogView())

繫結資料

完成xml檢視和資料的繫結。

kotlin mBinding.setVariable(variableId, t) mBinding.executePendingBindings()

3、封裝工具類,拓展相關功能

為了更加方便的讓呼叫者使用,封裝拓展函式是很有必要的,要不然,呼叫者每次都得要繼承上邊的兩個父類,這樣的程式碼就會增加很多,還會建立很多的類,我們需要單獨的建立一個工具類,來例項化我們需要簡化的功能邏輯。

提供新增xml檢視的方法

很簡單的一個普通方法,沒什麼好說的,把傳遞的xml,賦值給重寫的getLayoutId方法即可。

kotlin /** * AUTHOR:AbnerMing * INTRODUCE:設定layout * @param mLayoutId xml佈局 */ fun addLayout(mLayoutId: Int): VipDialog { this.mLayoutId = mLayoutId return this }

提供普通使用和DataBinding形式使用方法

普通和DataBinding方法,這裡用到了介面回撥,介面的實現則在initVMData方法裡,兩個方法本身功能是一樣的,無非就是一個是普通,一個是返回ViewDataBinding。

```kotlin /* * AUTHOR:AbnerMing * INTRODUCE:初始化資料 / fun bind(block: (bind: VB) -> Unit): VipDialog { setDataCallBackListener(object : OnDialogDataCallbackListener { override fun dataCallback() { block.invoke(getDataBinding()) } }) return this }

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:初始化資料
 */
fun set(block: () -> Unit): VipDialog {
    setDataCallBackListener(object : OnDialogDataCallbackListener {
        override fun dataCallback() {
            block.invoke()
        }
    })
    return this
}

```

提供設定樣式的方法

樣式的設定也就是使用了介面回撥。

kotlin /** * AUTHOR:AbnerMing * INTRODUCE:設定樣式 */ fun style(style: () -> Unit): VipDialog { setStyleCallBackListener(object : OnStyleCallBackListener { override fun styleCallback() { style.invoke() } }) return this }

提供獲取ViewDataBinding的方法

這個方法的提供是便於拿到ViewDataBinding,有效的更新檢視資料。

kotlin /** * AUTHOR:AbnerMing * INTRODUCE:獲取ViewDataBinding */ fun <VB : ViewDataBinding> getDataBinding(): VB { return mBinding as VB }

我們看下整體的程式碼,如下:

```kotlin / AUTHOR:AbnerMing DATE:2022/11/22 INTRODUCE:例項化功能 / class VipDialog : BaseBindingDialog() {

companion object {
    fun init(): VipDialog {
        return VipDialog()
    }
}

private var mLayoutId = 0

override fun initVMData() {
    mOnDialogDataCallbackListener?.dataCallback()
}

override fun initStyle() {
    mOnStyleCallBackListener?.styleCallback()
}

override fun getLayoutId(): Int {
    return mLayoutId
}

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:獲取ViewDataBinding
 */
fun <VB : ViewDataBinding> getDataBinding(): VB {
    return mBinding as VB
}


/**
 * AUTHOR:AbnerMing
 * INTRODUCE:設定layout
 * @param mLayoutId xml佈局
 */
fun addLayout(mLayoutId: Int): VipDialog {
    this.mLayoutId = mLayoutId
    return this
}

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:初始化資料
 */
fun <VB : ViewDataBinding> bind(block: (bind: VB) -> Unit): VipDialog {
    setDataCallBackListener(object : OnDialogDataCallbackListener {
        override fun dataCallback() {
            block.invoke(getDataBinding())
        }
    })
    return this
}

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:初始化資料
 */
fun set(block: () -> Unit): VipDialog {
    setDataCallBackListener(object : OnDialogDataCallbackListener {
        override fun dataCallback() {
            block.invoke()
        }
    })
    return this
}

/**
 * AUTHOR:AbnerMing
 * INTRODUCE:設定樣式
 */
fun style(style: () -> Unit): VipDialog {
    setStyleCallBackListener(object : OnStyleCallBackListener {
        override fun styleCallback() {
            style.invoke()
        }
    })
    return this
}

private var mOnDialogDataCallbackListener: OnDialogDataCallbackListener? = null
private fun setDataCallBackListener(mOnDialogDataCallbackListener: OnDialogDataCallbackListener) {
    this.mOnDialogDataCallbackListener = mOnDialogDataCallbackListener
}

private var mOnStyleCallBackListener: OnStyleCallBackListener? = null
private fun setStyleCallBackListener(mOnStyleCallBackListener: OnStyleCallBackListener) {
    this.mOnStyleCallBackListener = mOnStyleCallBackListener
}

} ```

4、封裝拓展函式,簡化呼叫

dialog的彈出可能有很多場景,比如Activity裡,比如Fragment裡,比如一個工具類中,我們可以根據已知的場景,來定義我們的呼叫方式,目前,我定義了兩種,在Activity或者Fragment裡可以直接進行呼叫,也就是開頭的呼叫方式,當然了,大家也可以自己拓展。

```kotlin /* * AUTHOR:AbnerMing * INTRODUCE:Activity顯示Dialog / fun AppCompatActivity.showVipDialog(vipDialog: VipDialog.() -> Unit): VipDialog { val dialog = VipDialog.init() dialog.apply(vipDialog) setActivityDialog(this.supportFragmentManager, dialog) return dialog }

/* * AUTHOR:AbnerMing * INTRODUCE:Fragment顯示Dialog / fun Fragment.showVipDialog(vipDialog: VipDialog.() -> Unit): VipDialog { val dialog = VipDialog.init() dialog.apply(vipDialog) setActivityDialog(this.childFragmentManager, dialog) return dialog } ```

通過以上幾步,我們就可以實現開頭的簡單呼叫,具體的大家可以檢視相關原始碼。

三、開源地址

專案地址:https://github.com/AbnerMing888/VipDialog

四、總結及注意事項

在開頭已經闡述,這種方式易於拓展,但是程式碼量相對比較多,畢竟所有的UI和邏輯都必須獨自來處理,在專案中的解決方式為,如果很多的彈框效果一樣,建議再封裝一層,抽取公共的工具類。

還有一個需要注意的,本身擴充套件函式showVipDialog返回的就是呼叫的類,也就是一個Dialog,大家可以直接獲取變數,在其他的地方做更新Dialog或者銷燬的操作。

kotlin val dialog=showVipDialog { …… }