问题一:show方法引起的异常
DialogFragment中的一段代码,使用了ft.commit()
public void show ( @NonNull FragmentManager manager, @Nullable String tag) {
mDismissed = false ;
mShownByMe = true ;
FragmentTransaction ft = manager. beginTransaction ( ) ;
ft. add ( this , tag) ;
ft. commit ( ) ;
}
导致出现问题: 在Activity的onSaveInstanceState方法之后,调用了show方法,则抛出如下异常 Can not perform this action after onSaveInstanceState
java. lang. IllegalStateException: Can not perform this action after onSaveInstanceState
at androidx. fragment. app. FragmentManagerImpl. checkStateLoss ( FragmentManagerImpl. java: 1536 )
at androidx. fragment. app. FragmentManagerImpl. enqueueAction ( FragmentManagerImpl. java: 1558 )
at androidx. fragment. app. BackStackRecord. commitInternal ( BackStackRecord. java: 317 )
at androidx. fragment. app. BackStackRecord. commit ( BackStackRecord. java: 282 )
at androidx. fragment. app. DialogFragment. show ( DialogFragment. java: 155 )
解决方案一(可以同时解决Fragment already added问题) 使用commitAllowingStateLoss,但是忽略了DialogFragment中的两个变量的状态mDismissed = false和mShownByMe = true,查看代码后发现这两个变量即使忽略,也不会产生影响
fun show ( manager: FragmentManager) {
val ft = manager. beginTransaction ( )
ft. add ( this , "myTag" )
ft. commitNowAllowingStateLoss ( )
}
解决方案二 重写了show方法,判断FragmentManager的状态后再去commit
isStateSaved available from appcompat >= 26.0.0 or androidx
override fun show ( manager: FragmentManager, tag: String? ) {
if ( ! manager. isDestroyed && ! manager. isStateSaved) {
super . show ( manager, tag)
}
}
解决方案三: 可以先记录状态,在Activity恢复显示后在执行操作(例如在onStart中处理)
问题二:代码的耦合问题
场景:例如我们app需要向用户发放优惠券,先要调用后台接口判断是否优惠券,这时候我们需要在Activity中请求接口,判断有优惠券后才去打开Dialog。这样Activity的业务量就会越来越多,这种情况不符合“低耦合,高内聚”的思想 解决办法: 在Activity中只需调用一行代码即可
DialogCoupon. getCoupon ( requireActivity ( ) , "home_dialog" )
companion object {
fun newInstance ( ) : DialogCoupon {
val fragment = DialogCoupon ( )
val args = Bundle ( )
fragment. arguments = args
return fragment
}
fun getCoupon ( activity: FragmentActivity, from: String? ) {
activity. lifecycleScope. launch {
val dialogRepository = DialogRepositoryProvider. getInstance ( )
val coupon = dialogRepository. getCouponList ( )
if ( ! coupon. unclaimed. isNullOrEmpty ( ) ) {
val dialogContent = dialogRepository. getCouponDialog ( )
val dialog = newInstance ( )
dialog. dialogContent = dialogContent
dialog. code = coupon. unclaimed[ 0 ] . code
dialog. from = from
dialog. show ( activity. supportFragmentManager, "DialogCoupon" )
}
}
}
}
在Dialog中创建一个伴生对象companion object,其中getCoupon用于获取后台接口,接口返回数据后,判断是否有优惠券,然后请求优惠券的内容接口,将内容传给Dialog中的属性,然后打开Dialog
activity.lifecycleScope.launch 关联Activity的生命期启动一个协程,在Activity销毁后,协程自动停止工作 完整代码
private const val TAG = "DialogCoupon"
class DialogCoupon : DialogFragment ( ) {
private lateinit var binding: StoreDialogCouponBinding
override fun show ( manager: FragmentManager, tag: String? ) {
if ( ! manager. isDestroyed && ! manager. isStateSaved) {
super . show ( manager, tag)
}
}
override fun onCreateView ( inflater: LayoutInflater, container: ViewGroup? ,
savedInstanceState: Bundle? ) : View {
Log. d ( TAG, "onCreateView: " )
binding = DataBindingUtil. inflate ( inflater, R. layout. store_dialog_coupon, container, false )
binding. lifecycleOwner = this
binding. ivClose. setOnClickListener { dismissAllowingStateLoss ( ) }
return binding. root
}
override fun onCreateDialog ( savedInstanceState: Bundle? ) : Dialog {
val dialog = Dialog ( requireContext ( ) , theme)
val window: Window? = dialog. window
window? . setBackgroundDrawable ( ColorDrawable ( Color. TRANSPARENT) )
window? . setDimAmount ( 0.8f )
return dialog
}
companion object {
fun newInstance ( ) : DialogCoupon {
val fragment = DialogCoupon ( )
val args = Bundle ( )
fragment. arguments = args
return fragment
}
fun getCoupon ( activity: FragmentActivity, from: String? ) {
activity. lifecycleScope. launch {
val dialogRepository = DialogRepositoryProvider. getInstance ( )
val coupon = dialogRepository. getCouponList ( )
if ( ! coupon. unclaimed. isNullOrEmpty ( ) ) {
val dialogContent = dialogRepository. getCouponDialog ( )
val dialog = newInstance ( )
dialog. dialogContent = dialogContent
dialog. code = coupon. unclaimed[ 0 ] . code
dialog. from = from
dialog. show ( activity. supportFragmentManager, "DialogCoupon" )
}
}
}
}
}
问题三:Dialog宽度没有铺满
设置Dialog的宽度为MATCH_PARENT 在onCreateDialog中设置是无效的,因为在onActivityCreated时候才去加载了setContentView,这时候DecorView被创建
override fun onActivityCreated ( savedInstanceState: Bundle? ) {
super . onActivityCreated ( savedInstanceState)
val window: Window? = dialog? . window
val lp = window? . attributes
lp? . width = WindowManager. LayoutParams. MATCH_PARENT
lp? . height = WindowManager. LayoutParams. WRAP_CONTENT
window? . attributes = lp
}
问题4:Dialog中弹出下一个Dialog
需要使用requireActivity().supportFragmentManager,如果使用childFragmentManager则会和上一个Dialog一起关闭
DialogCouponSuccess. newInstance ( )
. show ( requireActivity ( ) . supportFragmentManager
, "DialogCouponSuccess" )
参考资料