《DialogFragment系列一之源碼分析》
《DialogFragment系列二之Dialog封裝》
《DialogFragment系列三之AlertDialog實(shí)現(xiàn)》
《DialogFragment系列四之StatusDialog(Progress搔谴、Success、Error)實(shí)現(xiàn)》
《DialogFragment系列五之ItemDialog(eg:BottomDialog)實(shí)現(xiàn)》
《DialogFragment系列六之常見問題》
前幾篇通過對(duì)DialogFragment的源碼分析定義了一個(gè)BaseDialog進(jìn)而實(shí)現(xiàn)了AlertDialog桩撮、StatusDialog敦第,在此期間遇到了幾個(gè)比較經(jīng)典的問題,與讀者分享一下店量。
問題一:onViewCreated()不回調(diào)
筆者想通過onViewCreated()來設(shè)置布局控件芜果,但是怎么也不回調(diào),以前一直使用Fragment都會(huì)回調(diào)垫桂,現(xiàn)在怎么不回調(diào)了呢?帶著疑問粟按,進(jìn)去看了一下源碼诬滩,發(fā)現(xiàn)了貓膩霹粥。首先看下onViewCreated()在哪里被調(diào)用了,看下源碼:
moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
.......
case Fragment.CREATED:
// This is outside the if statement below on purpose; we want this to run
// even if we do a moveToState from CREATED => *, CREATED => CREATED, and
// * => CREATED as part of the case fallthrough above.
ensureInflatedFragmentView(f);
if (newState > Fragment.CREATED) {
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
if (f.mContainerId == View.NO_ID) {
throwException(new IllegalArgumentException(
"Cannot create fragment "
+ f
+ " for a container view with no id"));
}
container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
String resName;
try {
resName = f.getResources().getResourceName(f.mContainerId);
} catch (NotFoundException e) {
resName = "unknown";
}
throwException(new IllegalArgumentException(
"No view found for id 0x"
+ Integer.toHexString(f.mContainerId) + " ("
+ resName
+ ") for fragment " + f));
}
}
f.mContainer = container;
f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mInnerView = f.mView;
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
container.addView(f.mView);
}
if (f.mHidden) {
f.mView.setVisibility(View.GONE);
}
//這里被調(diào)用
f.onViewCreated(f.mView, f.mSavedFragmentState);
dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
false);
// Only animate the view if it is visible. This is done after
// dispatchOnFragmentViewCreated in case visibility is changed
f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
&& f.mContainer != null;
} else {
f.mInnerView = null;
}
}
f.performActivityCreated(f.mSavedFragmentState);
dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
.......
}
通過查看源碼發(fā)現(xiàn)疼鸟,onViewCreate()在moveToState()中即performCreateView創(chuàng)建contentView之后被調(diào)用后控,進(jìn)入onViewCreate()方法要通過一個(gè)判斷語句f.mView != null,那我們看下f.mView 是哪來的空镜,繼續(xù)回溯源碼:
void performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
mPerformedCreateView = true;
mViewLifecycleOwner = new LifecycleOwner() {
@Override
public Lifecycle getLifecycle() {
if (mViewLifecycleRegistry == null) {
mViewLifecycleRegistry = new LifecycleRegistry(mViewLifecycleOwner);
}
return mViewLifecycleRegistry;
}
};
mViewLifecycleRegistry = null;
//這里啊浩淘,在這里被賦值
mView = onCreateView(inflater, container, savedInstanceState);
if (mView != null) {
// Initialize the LifecycleRegistry if needed
mViewLifecycleOwner.getLifecycle();
// Then inform any Observers of the new LifecycleOwner
mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner);
} else {
if (mViewLifecycleRegistry != null) {
throw new IllegalStateException("Called getViewLifecycleOwner() but "
+ "onCreateView() returned null");
}
mViewLifecycleOwner = null;
}
}
通過回溯源碼,發(fā)現(xiàn)mView在performCreateView()里被唯一賦值吴攒,而且來源就是平時(shí)熟悉的onCreateView()张抄,performCreateView()是在創(chuàng)建contentView使用,在moveToState()中先調(diào)用performCreateView()創(chuàng)建contentView洼怔,然后再調(diào)用onViewCreated()署惯。既然定位到了onCreateView()那去看看其返回值:
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return null;
}
Fragment中默認(rèn)返回null,而且Dialog中onCreateView()也使用Fragment的返回值,所以此時(shí)mView就會(huì)為null
@Nullable
@Override
public final View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//getDialog().setCancelable(setCancelable());
getDialog().setCanceledOnTouchOutside(setCancelable());
setCancelable(setCancelable());
//設(shè)置背景透明
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
return super.onCreateView(inflater, container, savedInstanceState);
}
綜上镣隶,onViewCreated()不回調(diào)是因?yàn)閛nCreateView返回值為null极谊,但是在Dialog中沒有使用onCreateView去加載布局,是在onCreateDialog中加載的布局安岂,所以可以在onCreateDialog()中主動(dòng)回調(diào)onViewCreated()此問題就迎刃而解了轻猖。
@NonNull
@Override
public final android.app.Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
View dialogLayout = LayoutInflater.from(getContext()).inflate(setLayoutRes(), null);
builder.setView(dialogLayout);
//主動(dòng)回調(diào)
onViewCreated(dialogLayout, null);
return builder.create();
}
問題二:設(shè)置getDialog().setCancelable(setCancelable())后點(diǎn)擊返回鍵Dialog還是會(huì)dismiss
設(shè)置了getDialog().setCancelable(setCancelable())后點(diǎn)擊返回鍵Dialog還是會(huì)dismiss,很納悶域那,源碼里邏輯也是攔截返回事件通過boolean mCancelable來判斷是否dismiss咙边,代碼如下:
public void onBackPressed() {
if (mCancelable) {
cancel();
}
}
真是百思不得其解,而后返回到DialogFragment查看琉雳,發(fā)現(xiàn)了一個(gè)相似的方法样眠,如下:
boolean mCancelable = true;
public void setCancelable(boolean cancelable) {
mCancelable = cancelable;
if (mDialog != null) mDialog.setCancelable(cancelable);
}
罪魁禍?zhǔn)捉K于找到了,原來DialogFragment默認(rèn)設(shè)置了mCancelable翠肘,而默認(rèn)值是true檐束,所以導(dǎo)致了getDialog().setCancelable(setCancelable())不管作用,那可以直接通過DialogFragment的setCancelable()來設(shè)置束倍,代碼如下:
@Nullable
@Override
public final View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//getDialog().setCancelable(setCancelable());
setCancelable(setCancelable());
getDialog().setCanceledOnTouchOutside(setCancelable());
//設(shè)置背景透明
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
return super.onCreateView(inflater, container, savedInstanceState);
}
protected boolean setCancelable() {
return dialogParams.isCancelable;
}
到此被丧,此問題被解決
問題三:出現(xiàn)非contentView的背景
運(yùn)行代碼發(fā)現(xiàn)出現(xiàn)了非contentView的背景,此背景是DialogFragment的默認(rèn)背景绪妹,去掉即可甥桂,代碼如下:
@Nullable
@Override
public final View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
getDialog().setCancelable(setCancelable());
getDialog().setCanceledOnTouchOutside(setCancelable());
setCancelable(setCancelable());
//設(shè)置背景透明
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
return super.onCreateView(inflater, container, savedInstanceState);
}
以上就是在實(shí)現(xiàn)Dialog的過程遇到的三個(gè)問題以及解決辦法,特此記錄一下并分享給讀者邮旷!