Android自定义弹框演变史

第一代:

1、封装基础Dialog

package com.lpf.base.widgets

import android.app.Dialog
import android.content.Context
import android.util.DisplayMetrics
import android.view.Gravity
import com.lpf.base.R

abstract class BaseDialog(mContext: Context, mTheme: Int) : Dialog(mContext, mTheme) {

    var mCanCelable = true
    var mCanceledOnTouchOutside = false
    var mGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
    var mDimAmount = 0.4f
    var mY = 140

    init {
        initDialog()
        this.setCancelable(mCanCelable)
        this.setCanceledOnTouchOutside(mCanceledOnTouchOutside)
        window.setGravity(mGravity)
        val layoutParams = window.attributes
        layoutParams.dimAmount = mDimAmount
        if (mY != -1){
            layoutParams.y = Math.round(mY * (mContext.resources.displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
            window.attributes = layoutParams
        }
    }

    abstract fun initDialog()
}

2、基于BaseDialog实现各式各样dialog,如下实现通用弹框

package com.lpf.base.widgets

import android.content.Context
import android.view.View
import com.lpf.base.R
import com.lpf.base.ext.onClick
import kotlinx.android.synthetic.main.dialog_common.*

class CommonDialog private constructor(val mContext: Context, mTheme: Int) : BaseDialog(mContext, mTheme) {

    override fun initDialog() {
        this.setContentView(R.layout.dialog_common)
        mCancelTv.onClick { dismiss() }
    }

    companion object {
        private var dialog: CommonDialog? = null
        fun create(context: Context): CommonDialog {
            if (dialog == null || dialog?.mContext != context) {
                dialog = CommonDialog(context, R.style.LightDialog)
            }else if(dialog?.isShowing == true){
                dialog?.dismiss()
            }
            return dialog!!
        }
    }

    fun setTitleText(title: String): CommonDialog {
        mTitleTv.visibility = View.VISIBLE
        mTitleTv.text = title
        return this
    }

    fun setMsgText(msg: String): CommonDialog {
        mMsgTv.visibility = View.VISIBLE
        mMsgTv.text = msg
        return this
    }

    fun setConfirmText(confirmText: String): CommonDialog {
        mConfirmTv.text = confirmText
        return this
    }

    fun setCancelText(cancelText: String): CommonDialog {
        mCancelTv.text = cancelText
        return this
    }

    fun setOnClickConfirmListener(onClick: () -> Unit): CommonDialog {
        mConfirmTv.onClick {
            onClick()
            dismiss()
        }
        return this
    }

    fun setOnClickCancelListenner(onClick: () -> Unit): CommonDialog {
        mCancelTv.onClick {
            onClick()
            dismiss()
        }
        return this
    }

    override fun dismiss() {
        mTitleTv.visibility = View.GONE
        mMsgTv.visibility = View.GONE
        super.dismiss()
    }
}

至此便可轻松实现如下弹框效果,但是我们都知道dialog必须要依附在activity上即这里的Context不能是application的,如果我们在使用过程中不是很方便获取当前显示的activity,那么怎么才能实现在程序任意处调用显示弹框的目的呢?接下来看第二代

第二代

原理:实现弹窗样式的activity

1、在style.xml中定义弹窗样式

<style name="CommonDialog" parent="@style/Theme.AppCompat.Light.Dialog.Alert">
     <item name="android:windowFrame">@null</item>
     <item name="android:windowIsFloating">true</item>
     <item name="android:windowContentOverlay">@null</item>
     <item name="windowActionBar">false</item>
     <item name="windowNoTitle">true</item>
     <item name="android:windowCloseOnTouchOutside">false</item>
</style>

2、在AndroidManifest.xml给activity添加弹框样式

<activity android:name=".ui.activity.VersionUpdateActivity"
            android:theme="@style/CommonDialog"/>

至此便解决了第一代中不能方便调用弹框的问题,并且避免了activity正在销毁时调用显示弹框引发的异常,但是美好的事情总是伴随着意想不到的坑,该方案在4.4至5.1系统下弹框会显示默认的条状title,如下

第三代

分析:针对第二代的问题,查询很多方法都无法取消到这个title,难道就要放弃这个方案了吗?还是不去考虑低版本兼容?显然这不是我们的态度,那这个问题怎么解决呢?既然我去不掉你,那我就替换你——移花接木

1、在第二代基础上对activity显示添加系统版本判断,低版本则用弹框方式

package com.hualala.provider.ui.activity

import android.os.Build
import android.os.Bundle
import android.os.TransactionTooLargeException
import android.view.View
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.lpf.base.common.router.RouterPath
import com.lpf.base.ext.onClick
import com.lpf.base.ui.activity.BaseActivity
import com.lpf.provider.R
import com.lpf.provider.data.protocol.response.CheckVersionRes
import com.lpf.provider.ui.dialog.VersionUpdateDialog
import kotlinx.android.synthetic.main.activity_version_update_dialog.*

/**
 * created by lpf on 2019/7/1
 */
@Route(path = RouterPath.HualalaProvider.PATH_VERSION_UPDATE)
class VersionUpdateActivity : BaseActivity() {

    @Autowired(name = RouterPath.HualalaProvider.BUNDLE_CHECK_VERSION)
    @JvmField
    var mCheckVersionRes: CheckVersionRes? = null
    private var mVersionUpdateDialog:VersionUpdateDialog? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mCheckVersionRes?.let {
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
                setContentView(R.layout.activity_version_update_dialog)
                mVersionTv.text = "最新版本:${it.version}"
                mMsgEt.setText(it.updateDesc)
                mCancelTv.visibility = if (it.forceUpdate != 0) {//强制更新
                    View.GONE
                } else {
                    View.VISIBLE
                }
                mConfirmTv.onClick {
                    ARouter.getInstance().build(RouterPath.HualalaProvider.PATH_LOAD_FILE)
                            .withString(RouterPath.HualalaProvider.BUNDLE_APP_URL, it.downloadUrl)
                            .navigation()
                    finish()
                }
                mCancelTv.onClick {
                    finish()
                }
            } else {
                mVersionUpdateDialog = VersionUpdateDialog.create(this)
                        .setVersionTv("最新版本:${it.version}")
                        .setMsgEt(it.updateDesc)
                        .setUpdateType(it.forceUpdate != 0)
                        .setOnClickCancelListenner {
                            finish()
                        }
                        .setOnClickConfirmListener {
                            ARouter.getInstance().build(RouterPath.HualalaProvider.PATH_LOAD_FILE)
                                    .withString(RouterPath.HualalaProvider.BUNDLE_APP_URL, it.downloadUrl)
                                    .navigation()
                            finish()
                        }
                try {//Binder传输的数据太大或积累值满载导致的异常(低概率)
                    mVersionUpdateDialog?.show()
                }catch (e: TransactionTooLargeException){
                    finish()
                }
            }
            intent.removeExtra(RouterPath.HualalaProvider.BUNDLE_CHECK_VERSION)
        }
    }

    override fun onBackPressed() {
        if (mCheckVersionRes == null || mCheckVersionRes?.forceUpdate == 0) {
            super.onBackPressed()
        }
    }

    override fun onDestroy() {
        mVersionUpdateDialog?.dismiss()
        super.onDestroy()
    }
}

2、低版本我们使用原生弹框显示

package com.hualala.provider.ui.dialog

import android.content.Context
import android.view.Gravity
import android.view.View
import com.lpf.base.ext.onClick
import com.lpf.base.widgets.BaseDialog
import com.lpf.provider.R
import com.lpf.provider.ui.activity.VersionUpdateActivity
import kotlinx.android.synthetic.main.activity_version_update_dialog.*

/**
 * created by lpf on 2019/7/4
 */
class VersionUpdateDialog private constructor(val mContext: Context, mTheme: Int) : BaseDialog(mContext, mTheme){
    override fun initDialog() {
        mGravity = Gravity.CENTER
        mY = -1
        setContentView(R.layout.activity_version_update_dialog)
    }

    companion object {
        private var dialog: VersionUpdateDialog? = null
        fun create(context: Context): VersionUpdateDialog {
            if (dialog == null || dialog?.mContext != context) {
                dialog = VersionUpdateDialog(context, R.style.CommonDialog)
            }else if(dialog?.isShowing == true){
                dialog?.dismiss()
            }
            return dialog!!
        }
    }

    fun setUpdateType(isForce: Boolean): VersionUpdateDialog{
        mCancelTv.visibility = if (isForce) {
            setCancelable(false)
            View.GONE
        } else {
            setCancelable(true)
            View.VISIBLE
        }
        return this
    }

    fun setVersionTv(version: String): VersionUpdateDialog{
        mVersionTv.text = version
        return this
    }

    fun setMsgEt(msg: String): VersionUpdateDialog {
        mMsgEt.setText(msg)
        return this
    }

    fun setOnClickConfirmListener(onClick: () -> Unit): VersionUpdateDialog {
        mConfirmTv.onClick {
            onClick()
        }
        return this
    }

    fun setOnClickCancelListenner(onClick: () -> Unit): VersionUpdateDialog {
        mCancelTv.onClick {
            onClick()
        }
        return this
    }

    override fun dismiss() {
        if (mContext is VersionUpdateActivity && !mContext.isFinishing) {
            mContext.finish()
        } else {
            super.dismiss()
        }
    }
}

3、最终效果

至此便完成了弹框的演变,需要注意的是第三代中Dialog消失逻辑要放到activity的onDestroy()中进行,不然弹框消失后会短暂的看到activity中的条状title,然后activity才消失

第四代

将第二代的style换成如下即可

   <style name="translucent" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="windowNoTitle">true</item>
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值