guolindev / PermissionX

An open source Android library that makes handling runtime permissions extremely easy.
Apache License 2.0
3.53k stars 406 forks source link

大神,自定义弹窗好像很麻烦啊 #17

Closed jingzz1 closed 4 years ago

jingzz1 commented 4 years ago

每次使用,都得自己new一个对象,有没有办法做一个全局的方法,把context传给我,然后我统一new一个弹窗给你传回去呢?

guolindev commented 4 years ago

肯定不行呀,全局的弹窗就得持有引用,持有引用就可能会造成内存泄漏。

关于自定义弹窗麻烦的问题是因为自定义弹窗本来就是一件很麻烦的事情,下个版本的PermissionX当中我将会加入内置自定义弹窗的实现来简化这个过程。

jingzz1 commented 4 years ago

传layoutId给你,这个layoug xml里包含你指定的控件和控件id,这样也可以啊

guolindev commented 4 years ago

那你就是只想定义了layout.xml了,这个我也想过,其实最初的设计方式我就是想这么做的,让使用者传一个view进来,我只替换dialog中的view部分。

但是如果这样做的话,后期一定会有很多使用者要求,改变dialog样式,改变dialog背景,改变dialog圆角弧度,这些只传一个view进来是实现不了的,所以我仔细思考之后,就决定整个dialog的实现都交给使用者去自定义了,这样灵活度会非常高。

jingzz1 commented 4 years ago

那使用代理模式可以吗?RationaleDialog里需要的NegativeButton,PositiveButton和setContentView的id都可以交给代理类去处理,像这样:

class RationaleDialog(context:Context,val helper:RationaleHelper):Dialog(context,helper.getThemeResId()?:0){
    init {

        setContentView(helper.getLayoutResID())
        setCancelable(helper.isCancelable())
        val positiveView = helper.getPositiveButton(this)
        val negativeButton = helper.getNegativeButton(this)
        helper.initDialog(this)

    }
}

interface RationaleHelper{
    //动态设置dialog某些样式
    fun initDialog(dialog: RationaleDialog)

    @LayoutRes //dialog layout
    fun getLayoutResID(): Int
    @StyleRes //dialog style
    fun getThemeResId(): Int?
    //dialog外关闭dialog
    fun isCancelable(): Boolean
    fun getPositiveButton(rationaleDialog: RationaleDialog): View
    fun getNegativeButton(rationaleDialog: RationaleDialog): View
}

interface DialogEngine{
    fun getRationaleHelper(context:Context, message:String, deniedList:List<String>):RationaleHelper
}

//PermissionX添加初始化方法
class PermissionX{
    companion object{
        lateinit var engine:DialogEngine
        fun initDialgHelper(engine:DialogEngine){
            this.engine = engine
        }
    }
}

class ExplainScope{
    fun showRequestReasonDialog(context:Context,message:String,deniedList:List<String>){
        val rationaleHelper = PermissionX.engine.getRationaleHelper(context, message, deniedList)
        RationaleDialog(context,rationaleHelper).show()
    }
}

//使用的时候只要实现initDialgHelper方法,把自定义的RationaleHelper返回回去就行

PermissionX.initDialgHelper(object :DialogEngine{
        override fun getRationaleHelper(context: Context, message: String,deniedList: List<String>
        ): RationaleHelper {
            return CustomHelper(message)
        }
    })

//实现CustomHelper

class CustomHelper(val explain:String = ""):RationaleHelper{
    override fun getLayoutResID(): Int = R.layout.custom_dialog

    override fun getThemeResId(): Int = R.style.CustomDialog

    override fun isCancelable(): Boolean = false

    override fun getPositiveButton(dialog: RationaleDialog): View = dialog.findViewById(R.id.btnPositive)

    override fun getNegativeButton(dialog: RationaleDialog): View = dialog.findViewById(R.id.btnPositive)
    override fun initDialog(dialog: RationaleDialog) {
        dialog.window?.let {
            //透明背景
            it.setBackgroundDrawableResource(android.R.color.transparent)
        }
        dialog.findViewById<TextView>(R.id.tvExplain).text = explain
    }
}

scope显示自定义弹窗

.onExplainRequestReason { scope, deniedList, beforeRequest ->
    val message = "PermissionX需要您同意以下权限才能正常使用"
    scope.showRequestReasonDialog(context, message, deniedList)
}

由于kotlin支持扩展函数,所以我kotlin写了个扩展方法

fun ExplainScope.showRequestReasonDialog(context:Context, message:String, deniedList:List<String>){
    CustomDialog(context,message,deniedList).show()
}

但我们公司的项目是kotlin+java,在java调用自定义弹窗的时候,总觉得很不方便

guolindev commented 4 years ago

你这种实现也并没有简化任何的开发过程呀,虽然看上去好像用CustomHelper封装了很多自定义Dialog的实现,但是在CustomHelper中你自定义的getLayoutResID(),getThemeResId(),它们所对应的布局不还是要由你自己去编写。

另外不要尝试把Dialog做成全局的,好像所有界面都得重用它才行,这种持有Activity引用的类做成全局是很危险的,你管理不好它的生命周期就会内存泄漏。每次用到就new一次,用完就释放,这才是最安全的。