今天修复一个历史遗留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>