相关文章推荐
憨厚的小马驹  ·  Wireshark中[TCP Window ...·  2 年前    · 
瘦瘦的人字拖  ·  日期选择器date、week、time、da ...·  2 年前    · 
聪明伶俐的跑步鞋  ·  Anaconda+pytorch+cuda安 ...·  2 年前    · 
踢足球的茄子  ·  使用基于CBCT扫描的数字模型对3D打印临时 ...·  2 年前    · 
个性的跑步机  ·  Android 中 Base64 转换成 ...·  2 年前    · 
Code  ›  DialogFragment踩坑记开发者社区
dialog fragment dialogfragment
https://cloud.tencent.com/developer/article/1883800
谦逊的书包
2 年前
韦东锏
0 篇文章

DialogFragment踩坑记

前往专栏
腾讯云
开发者社区
文档 意见反馈 控制台
首页
学习
活动
专区
工具
TVP
最新优惠活动
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
韦东锏
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
社区首页 > 专栏 > Android码农 > DialogFragment踩坑记

DialogFragment踩坑记

作者头像
韦东锏
发布 于 2021-09-29 15:09:08
1.4K 0
发布 于 2021-09-29 15:09:08
举报

忙完了美国大选,可以继续更新公众号了

DialogFragment推出来已经很久了,网上相关的文档一大堆,但是稍微不注意,还是会踩坑,本篇基于自身经历总结

  • 要选android X下的DialogFragment
  • 普通的Fragment还是DialogFragment
  • 同时设置不要Title跟背景
  • 隐藏Dialog
  • 不保留活动
  • 显示的逻辑
  • 推荐使用DialogFragment

要选android X下的DialogFragment

DialogFragment有两个不同的包名

  1. androidx.fragment.app.DialogFragment
  2. android.app.DialogFragment

为了兼容各个Android版本的,记得要选择第一种DialogFragment,当然,系统源码也很贴心的把它Framework的DialogFragment设置为不推荐使用,而且还注明让你使用support包下面的DialogFragment,当然项目也基本都从support转成了Android x,感觉这个注释可以更新下了

普通的Fragment还是DialogFragment

先看下系统onCreate的方法

mShowsDialog = mContainerId == 0; 只有当mContainerId等于0的时候,才会生成dialog,不然的话,跟普通的Fragment没有任何区别,所以要用DialogFragment#show()方法来展示

DialogFragment如果只是当做普通的Fragment,建议使用普通的Fragment就可以了,这样代码逻辑比较清晰

同时设置不要Title跟背景

想同时不要Title跟背景,这个时候,可以去网上搜索下,可以发现一大堆的答案,都是需要繁琐的配置各种theme,最少也需要十几行代码,其实看下源码,就可以轻松知道,只要下面一行代码就可以了

在onCreate9()的时候设置下 setStyle(STYLE_NO_FRAME, 0)

先看下DialogFragment#setupDialog代码

public void setupDialog(@NonNull Dialog dialog, int style) {                 
    switch (style) {                                                         
        case STYLE_NO_INPUT:                                                 
            Window window = dialog.getWindow();                              
            if (window != null) {                                            
                window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);    
            // fall through...                                               
        case STYLE_NO_FRAME:                                                 
        case STYLE_NO_TITLE:                                                 
            dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);            

可以看到,设置NO_FRAME会带Window.FEATURE_NO_TITLE的feature,所以设置NO_Frame可以同时实现没有title的效果 另外,看下NO_FRAME是如何实现没有背景的

public void setStyle(@DialogStyle int style, @StyleRes int theme) {
    mStyle = style;                                                
    if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {    
        mTheme = android.R.style.Theme_Panel;                      
    if (theme != 0) {                                              
        mTheme = theme;                                            

可以发现, 最终实际生效的,其实是mTheme这个主题参数 ,我们继续看下系统R.style.Theme_Panel的源码

<style name="Theme.Panel">
    <item name="windowBackground">@color/transparent</item>
    <item name="colorBackgroundCacheHint">@null</item>
    <item name="windowFrame">@null</item>
    <item name="windowContentOverlay">@null</item>
    <item name="windowAnimationStyle">@null</item>
    <item name="windowIsFloating">true</item>
    <item name="backgroundDimEnabled">false</item>
    <item name="windowIsTranslucent">true</item>
    <item name="windowNoTitle">true</item>
</style>

所以设置了NO_FRAME,相当于帮我们设置了上面的Theme属性,其实我们也可以自己手动设置上面的theme,效果也是一样的,不过就会无谓的增加许多代码了

隐藏Dialog

很容易想到,调用DialogFragment#dismiss方法来隐藏dialog,不过这个方法在线上运行,很容易会报下面的错误

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

可以看下实际报错的地方

private void checkStateLoss() {                                          
    if (isStateSaved()) {                                                
        throw new IllegalStateException(                                 
                "Can not perform this action after onSaveInstanceState");

在实际项目中,很可能展示dialog后,用户按了home键去操作其他的,这个时候调用dismiss,就会触发这个报错

可以改成DialogFragment#dismissAllowingStateLoss方法,就不会走到checkStateLoss,就可以轻易规避这个问题了

特别强调的是,这个报错自测阶段很不容易暴露出来,但是一上线上环境就容易发生,需要注意规避

不保留活动

这是一个不得不考虑的场景,实际情况下,发生了不保留活动,业务这边的逻辑一般是重置了,所以也是不需要再展示dialog,不过发生不保留,系统会自动重新展示dialog,这个时候,需要手动关闭dialog

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)                    
    if (savedInstanceState != null) {                                
        //处理不保留活动下的场景,这个时候,返回会重新展示,改成主动关闭,不展示
        dismissAllowingStateLoss()                                               

显示的逻辑

大多数场景,显示dialog,一般都是用户在操作的时候,这个时候调用DialogFragment#show()方法展示是没有问题的 先看下源码

public void show(@NonNull FragmentManager manager, @Nullable String tag) {
    mDismissed = false;                                                   
    mShownByMe = true;                                                    
    FragmentTransaction ft = manager.beginTransaction();                  
    ft.add(this, tag);                                                    
    ft.commit();                                                          

其实内部是通过commit方法,而不是commitAllowingStateLoss方法,所以当业务方有可能在页面不可见的时候调用展示dialog,也还是会触发崩溃

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

作为规避,有两个方案 一 在页面不可见的时候,调用展示dialog无效

fun showDialog(activity:FragmentActivity) {
    if (activity.isFinishing || activity.isDestroyed) {   
        return                                                     
    if (activity.supportFragmentManager.isStateSaved) {           
        //已经走到save状态,不展示dialog                             
        return                                                     

二 先不展示,在下次页面返回可见的时候再展示 在监听到onResume的时候,触发Dialog的显示,代码就不贴了

推荐使用DialogFragment

建议展示Dialog的地方,统一使用DialogFragment,更好的处理生命周期的各种场景,而且在Fragment回收后,也可以自动帮我们关闭Dialog,避免逻辑异常

public void onDestroyView() {              
    super.onDestroyView();                 
    if (mDialog != null) {                 
        mViewDestroyed = true;             
        //fragment销毁后,会自动关闭dialog
        mDialog.setOnDismissListener(null);
        mDialog.dismiss();                 
        if (!mDismissed) {                 
 
推荐文章
憨厚的小马驹  ·  Wireshark中[TCP Window Full] 和 [Zero Window]帧的含义_wireshark tcp window full-CSDN博客
2 年前
瘦瘦的人字拖  ·  日期选择器date、week、time、datetime、datetime-local类型 - 码农编程进阶笔记 - 博客园
2 年前
聪明伶俐的跑步鞋  ·  Anaconda+pytorch+cuda安装全流程介绍|常见问题解决|conda与pip|pycharm配置 - 知乎
2 年前
踢足球的茄子  ·  使用基于CBCT扫描的数字模型对3D打印临时假体制造的准确性进行评估。,PLOS ONE - X-MOL
2 年前
个性的跑步机  ·  Android 中 Base64 转换成 图片_android base64zhuan_merbng的博客-CSDN博客
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号