解决DialogFragment闪烁或者闪屏的问题

今天修复一个历史遗留BUG;项目中的dialog基本都是通过DialogFragment来实现的,但是同时也有个问题,比如再加载网页的时候,加载之前xshow出个dialog,网页加载完后dismiss,但是如果网络很好,网页加载的速度很快,不到1s就加载完成的话,就会出现一个问题,界面会闪一下,如果你的Activity的主题是默认的话,就表现为黑屏一下,如果是透明的主题,就表现为可以看到上一个页面,然后立马恢复虽然这个闪屏的时间很短,估计只有几ms,但是看着还是很不爽的。

  • 最开始的解决方案是想办法设置一个dialog的最短显示时间,这样就不会出现这个闪屏的情况,但是后面的情况很复杂,设置一个最短的显示时间就要用到定时器,达到最短时间后再dismiss,如果调用延时的dismiss后要立马finish当前页面的话,就会出现定时器结束后获取不到dialog对应的fragment管理器。
public void hideLoading() {
    
    
        long cancelTime = System.currentTimeMillis() - loadingStartTime;
        if (cancelTime > LOAD_MIN_TIME){
    
    
            if (null != mLoadingFragment) {
    
    
                mLoadingFragment.dismissAllowingStateLoss();
            }
            return;
        }
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
    
    
            @Override
            public void run() {
    
    
                if (null != mLoadingFragment) {
    
    
                    mLoadingFragment.dismissAllowingStateLoss();
                }
            }
        },cancelTime);
    }

而且想想也不应该这么做,治标不治本,于是打算从DialogFragment下手。

  • 既然是在dismiss的时候出现的闪屏,那么就先从*mLoadingFragment.dismissAllowingStateLoss()*入手看看,跳到DialogFragment源码里看一下
/**
     * Dismiss the fragment and its dialog.  If the fragment was added to the
     * back stack, all back stack state up to and including this entry will
     * be popped.  Otherwise, a new transaction will be committed to remove
     * the fragment.
     */
    public void dismiss() {
    
    
        dismissInternal(false);
    }

    /**
     * Version of {@link #dismiss()} that uses
     * {@link FragmentTransaction#commitAllowingStateLoss()
     * FragmentTransaction.commitAllowingStateLoss()}.  See linked
     * documentation for further details.
     */
    public void dismissAllowingStateLoss() {
    
    
        dismissInternal(true);
    }
    
    void dismissInternal(boolean allowStateLoss) {
    
    
        if (mDismissed) {
    
    
            return;
        }
        mDismissed = true;
        mShownByMe = false;
        if (mDialog != null) {
    
    
            mDialog.dismiss();
            mDialog = null;
        }
        mViewDestroyed = true;
        if (mBackStackId >= 0) {
    
    
            getFragmentManager().popBackStack(mBackStackId,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            mBackStackId = -1;
        } else {
    
    
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.remove(this);
            if (allowStateLoss) {
    
    
                ft.commitAllowingStateLoss();
            } else {
    
    
                ft.commit();
            }
        }
    }

看一下上面那段注释

Dismiss the fragment and its dialog. If the fragment was added to theback stack, all back stack state up to and including this entry will be popped. Otherwise, a new transaction will be committed to remove the fragment.
(关闭Fragment及其Dialog。 如果将Fragment添加到后台堆栈,则将弹出直至该条目的所有后台堆栈状态。 否则,将提交一个新事务以删除该Fragment。)

大概意思看着好像和闪屏有关系。
DialogFragment代码量很少,简单看一下其他的生命周期函数。

    @Override
    public void onStop() {
    
    
        super.onStop();
        if (mDialog != null) {
    
    
            mDialog.hide();
        }
    }

    /**
     * Remove dialog.
     */
    @Override
    public void onDestroyView() {
    
    
        super.onDestroyView();
        if (mDialog != null) {
    
    
            // Set removed here because this dismissal is just to hide
            // the dialog -- we don't want this to cause the fragment to
            // actually be removed.
            mViewDestroyed = true;
            mDialog.dismiss();
            mDialog = null;
        }
    }

在stop方法里先是隐藏了dialog,然后再destoryView里dismiss了,既然dismiss不能随便用,那就我们手动先调用hide,反正dialogFragment里的onDestoryView会最后调用dismiss,于是在我们的LoadingFragment里写一个dismiss方法来执行hide,然后在我们需要关掉loading的地方调用我们自己重写的dismiss就行了

解决方案一
    public void dismiss() {
    
    
        if (getDialog() != null)
            getDialog().hide();
    }
解决方案二

设置dialog的样式

@Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
    
    
       Dialog dialog = new Dialog(getActivity(), R.style.loadingDialogStyle);
       .....
    }
    <style name="loadingDialogStyle">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">false</item>//默认为false,设置了这个为true会出现闪屏
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
        <item name="android:backgroundDimEnabled">true</item>
    </style>

猜你喜欢

转载自blog.csdn.net/qq_17282141/article/details/108577907