第一代:
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>