有些场景下需要显示一些提示弹窗,但把握不好弹出时机容易先弹出弹窗然后界面马上被杀掉进而看不到提示内容,例如强制下线:客户端退回登录界面并弹出提示弹窗。
如果是直接拿的栈顶activity去弹出,没有将弹窗逻辑写到具体activity中,或不好确定activty的变化就容易出现这种现象。
由于applicationContext没有AppWindowToken
,所以dialog无法使用applicationContext创建,要么就使用windowManager配合WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
使用创建全局悬浮窗。但是这种做法需要申请权限。那么,在没有悬浮权限情况下如何做到让dialog不受栈顶activity变化的影响?
我的想法是通过application.registerActivityLifecycleCallbacks
在activity变化时,关闭原来的弹窗,并重新创建一个一样的dialog并显示。
效果演示:
1. 栈顶界面被杀
2. 有新界面弹出
以下是代码实现:
kotlin
/**
* @Description 无需悬浮权限的全局弹窗,栈顶activity变化后通过反射重建,所以子类构造方法需无参
*/
open class BaseAppDialog<T : ViewModel>() : Dialog(topActivity!!.get()!!), ViewModelStoreOwner {
companion object {
private val TAG = BaseAppDialog::class.java.simpleName
private var topActivity: WeakReference<Activity>? = null
private val staticRestoreList = linkedMapOf<Class<*>, String>()
private val staticViewModelStore: ViewModelStore = ViewModelStore()
@JvmStatic
fun init(application: Application) {
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
topActivity = WeakReference(activity)
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
topActivity = WeakReference(activity)
val tempList = arrayListOf<BaseAppDialog<*>>()
val iterator = staticRestoreList.iterator()
while (iterator.hasNext()) {
val next = iterator.next()
val topName = (topActivity?.get() ?: "")::class.java.name
if (next.value != topName) { //避免onCreate创建的弹窗重复弹出
//重新创建dialog
val newInstance = Class.forName(next.key.name).getConstructor().newInstance() as BaseAppDialog<*>
tempList.add(newInstance)
Log.e(TAG, "重新创建${next.key.name},于$topName")
iterator.remove()
}
}
tempList.forEach {
it.show()
}
if (staticRestoreList.size == 0) {
staticViewModelStore.clear()
}
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
}
})
}
}
var vm: T? = null
init {
val genericClass = getGenericClass()
if (vm == null) {
(genericClass as? Class<T>)?.let {
vm = ViewModelProvider(this)[it]
}
}
//activity暂停时关闭
topActivity?.get()?.let {
(it as LifecycleOwner).lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
dismissSilent()
}
})
}
}
fun setSilentVm(viewModel: ViewModel?) {
vm = viewModel as? T
}
//用于栈顶变化时的关闭
private fun dismissSilent() {
super.dismiss()
}
override fun show() {
super.show()
staticRestoreList.put(this::class.java, (topActivity?.get() ?: "")::class.java.name)
}
override fun dismiss() {
super.dismiss()
staticRestoreList.remove(this::class.java)
}
//获取泛型实际类型
private fun getGenericClass(): Class<*>? {
val superclass = javaClass.genericSuperclass
if (superclass is ParameterizedType) {
val actualTypeArguments: Array<Type>? = superclass.actualTypeArguments
if (!actualTypeArguments.isNullOrEmpty()) {
val type: Type = actualTypeArguments[0]
if (type is Class<*>) {
return type
}
}
}
return ViewModel::class.java
}
//自己管理viewModel以便恢复数据
override fun getViewModelStore(): ViewModelStore {
return staticViewModelStore
}
}
参数传递的话,直接通过修改dialog的viewmodel变量或调用其方法来实现。
kotlin
class TipDialogVm : ViewModel() {
val content = MutableLiveData<String>("")
}
class TipDialog2 : BaseAppDialog<TipDialogVm>() {
var binding : DialogTip2Binding? = null
init {
binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.dialog_tip2, null, false)
binding?.lifecycleOwner = context as? LifecycleOwner
binding?.vm = vm
setContentView(binding!!.root)
}
}
弹出弹窗
kotlin
TipDialog2().apply {
vm?.content?.value = "嗨嗨嗨"
}.show()