Dialog常用方法:
addContentView(View view, ViewGroup.LayoutParams params):
在原有布局基礎(chǔ)上添加啊新的View抓半,需要傳入LayoutParamers對(duì)布局進(jìn)行設(shè)置。
LayoutParamers是ViewGroup的一個(gè)內(nèi)部類格嘁,通過(guò)LayoutParams來(lái)對(duì)ViewGroup進(jìn)行設(shè)置笛求,包括View的寬高屬性。在LayoutParamers中還有MarginLayoutParams內(nèi)部類糕簿,
用來(lái)設(shè)置Margin屬性
dispatchKeyEvent(KeyEvent event):
攔截按鍵事件探入,通過(guò)重寫(xiě)此事件可以對(duì)按鍵事件進(jìn)行攔截,
dispatchTouchEvent(MotionEvent ev):
同理可以進(jìn)行觸摸事件的攔截懂诗。
getWindow():獲取window蜂嗽,可以通過(guò)getWindow()方法獲取到Dialog依附于的Window,Window是一個(gè)抽象類殃恒,public abstract class Window {.....}
這是在安卓源碼中的定義植旧,其實(shí)對(duì)于Android來(lái)講,我們看到的任何一個(gè)界面其實(shí)本質(zhì)上來(lái)講都是一個(gè)Window离唐,包括Activity病附、Dialog所有能看到的界面其實(shí)都是通過(guò)Window來(lái)實(shí)現(xiàn)的。Window的唯一實(shí)現(xiàn)類是PhoneWindow亥鬓,對(duì)于任何一個(gè)顯示的界面來(lái)講完沪,所有都是依附于PhoneWindow。
hide():通過(guò)hide方法能夠?qū)ialog進(jìn)行隱藏嵌戈。通過(guò)源碼可以發(fā)現(xiàn)
```
/**
* Hide the dialog, but do not dismiss it.
*/
public void hide() {
if (mDecor !=null) {
mDecor.setVisibility(View.GONE);
}
}
```
只是對(duì)Dialog依附于的View進(jìn)行了隱藏覆积,但是沒(méi)有dismiss。講到hide方法自然就要說(shuō)到dismiss()方法熟呛,通過(guò)源碼中可以發(fā)現(xiàn)宽档,
```
try {
mWindowManager.removeViewImmediate(mDecor);
} finally {
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;
mWindow.closeAllPanels();
onStop();
mShowing = false;
sendDismissMessage();
}
```
只有調(diào)用dismiss方法才真正的移除了View。
isShowing():判斷Dialog是否正在顯示
requestWindowFeature(int featureId):設(shè)置windows的樣式庵朝,其實(shí)這個(gè)方法只是直接調(diào)用了Window的設(shè)置樣式的方法雌贱,本身并沒(méi)有進(jìn)行什么處理。
通過(guò)源碼可以看到?
```
public final boolean requestWindowFeature(int featureId) {
return getWindow().requestFeature(featureId);
}
```
setCancelable(boolean flag):BACK鍵是否可以使Dialog進(jìn)行dismiss
setCanceledOnTouchOutside(boolean cancel):觸摸外部是否可以進(jìn)行dismiss
setContentView(View view):設(shè)置布局偿短,在自定義Dialog中,這個(gè)方法是十分常用的馋没,我們?cè)陂_(kāi)發(fā)中自己寫(xiě)好的布局就是通過(guò)這個(gè)方法添加到Dialog中昔逗,可以發(fā)現(xiàn)在Activity
中也存在這個(gè)方法。原理上都是一樣的篷朵。都是通過(guò)Window進(jìn)行設(shè)置勾怒。
show():顯示婆排,如果在show()方法中進(jìn)行一些處理數(shù)據(jù)等的操作的話,不要重寫(xiě)show方法笔链。Dialog中提供了類似于Activity的onStart方法段只,名字也叫作onStart方法,可以重寫(xiě)這個(gè)方法進(jìn)行一些其他的操作鉴扫。從源碼中可以看到在show方法中會(huì)調(diào)用onStart方法赞枕。
cancel():對(duì)話框消失,回調(diào)DialogInterface.OnCancelListener坪创,其實(shí)也調(diào)用了dismiss()
onCreate(Bundle savedInstanceState)同于Activity
onStop()與onStart方法一樣炕婶,會(huì)在dismissDialog方法中調(diào)用,這個(gè)方法也就是dismiss方法中調(diào)用的方法莱预。
PopupWindow方法:
dismiss():此方法是隱藏PopupWindow的方法
isShowing():正在顯示
setBackgroundDrawable(Drawable background):設(shè)置背景柠掂,此參數(shù)可以為空
setContentView(View contentView):與Dialog一樣,設(shè)置顯示的布局
setFocusable(boolean focusable):設(shè)置可以獲取焦點(diǎn)
setOnDismissListener(PopupWindow.OnDismissListener onDismissListener):隱藏監(jiān)聽(tīng)
setOutsideTouchable(boolean touchable):外部可點(diǎn)擊進(jìn)行dismiss
showAsDropDown(View anchor, int xoff, int yoff):調(diào)用此方法進(jìn)行PopupWindow的顯示依沮,設(shè)置顯示的位置涯贞,相對(duì)于某一個(gè)View的偏移量。有好幾個(gè)重構(gòu)方法危喉。意思其實(shí)都是一樣宋渔。
showAsDropDown(View anchor)
showAtLocation(View parent, int gravity, int x, int y):此方法也用于顯示,但是相對(duì)應(yīng)得view存在差別姥饰,showAsDropDown是相對(duì)于某一個(gè)view進(jìn)行定位傻谁,showAtLocation是相對(duì)于父布局進(jìn)行定位。
Dialog和PopupWindow的場(chǎng)景解析
對(duì)于Dialog和PopupWindow來(lái)講列粪,都是可以用來(lái)彈出提示信息审磁,在App開(kāi)發(fā)中都是比較常用,對(duì)于一個(gè)應(yīng)用場(chǎng)景來(lái)講岂座,很多時(shí)候用這兩種都可以實(shí)現(xiàn)态蒂。在不同的場(chǎng)景下可以根據(jù)二者的區(qū)別和特點(diǎn)來(lái)選擇使用哪一個(gè)。PopupWindow的特點(diǎn)是定位更準(zhǔn)確费什,寬高和邊界都比較清晰钾恢。對(duì)于彈出Tags來(lái)講很適合。Dialog相對(duì)于更獨(dú)立鸳址,相當(dāng)于一個(gè)新的界面瘩蚪,或者是應(yīng)用程序的Loading,往往使用Dialog更好稿黍。
對(duì)于二者的區(qū)別網(wǎng)上的總結(jié)有很多疹瘦,這里我就不再說(shuō)了,網(wǎng)上的說(shuō)法有一些是正確的巡球,但也有錯(cuò)誤的情況言沐。最后想說(shuō)一下同樣是彈出式對(duì)話框邓嘹,或者說(shuō)彈出式控件(也可能叫控件不太恰當(dāng),就不詳細(xì)追究了)二者在本質(zhì)上的區(qū)別险胰。這里就得需要同源碼上來(lái)分析了汹押。
以下是Dialog的構(gòu)造函數(shù)
```
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == 0) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
```
可以發(fā)現(xiàn),對(duì)于Dialog來(lái)講起便,每次我們new一個(gè)Dialog的時(shí)候棚贾,其實(shí)都會(huì)new一個(gè)PhoneWindow,前面已經(jīng)說(shuō)了缨睡,PhoneWindow是Window的實(shí)現(xiàn)類鸟悴。所以說(shuō),對(duì)于Dialog來(lái)講奖年,每一個(gè)Dialog相當(dāng)于一個(gè)新的Window细诸,Activity其實(shí)也是一個(gè)Window。所以說(shuō)陋守,當(dāng)我們?cè)趧?chuàng)建Activity的過(guò)程中震贵,也就是onCreate方法中調(diào)用Dialog的show方法是可以彈出Dialog的。因?yàn)镈ialog是獨(dú)立的Window水评。相當(dāng)于一個(gè)新的Window蓋住了Acitvity這個(gè)Window猩系。并且在Dialog顯示的時(shí)候,Activity只是被遮蓋住了中燥。但是并沒(méi)有調(diào)用生命周期的onPuse和onStop方法寇甸。有的人可能會(huì)問(wèn),為什么另一個(gè)Activity蓋住了這個(gè)Activity就會(huì)調(diào)用這個(gè)方法呢疗涉。因?yàn)锳ctivity是通過(guò)Activity棧進(jìn)行管理的拿霉,猜測(cè)只有在Activity棧中發(fā)生了變化才會(huì)對(duì)Activity的生命周期產(chǎn)生變化。Dialog并沒(méi)有入Activity棧咱扣,只是遮擋住了Activity绽淘。這是Dialog的實(shí)現(xiàn)原理,下面說(shuō)PopupWindow的實(shí)現(xiàn)原理闹伪。通過(guò)PopupWindow的源碼可以發(fā)現(xiàn)沪铭,在PopupWindow的實(shí)現(xiàn)過(guò)程中并沒(méi)有出現(xiàn)Window這個(gè)東西。在Dialog中Dialog的DecorView是通過(guò)Window拿到的偏瓤,而PopupWindow的DecorView其實(shí)是這么一個(gè)東西PopupDecorView杀怠。下面是源碼:
```
private class PopupDecorView extends FrameLayout {
private TransitionListenerAdapter mPendingExitListener;
public PopupDecorView(Context context) {
super(context);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (getKeyDispatcherState() == null) {
return super.dispatchKeyEvent(event);
}
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.startTracking(event, this);
}
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null && state.isTracking(event) && !event.isCanceled()) {
dismiss();
return true;
}
}
return super.dispatchKeyEvent(event);
} else {
return super.dispatchKeyEvent(event);
}
}
```
可以發(fā)現(xiàn),PopupDecorView是繼承于FrameLayout。也就是說(shuō)PopupWindow本質(zhì)上就是一個(gè)View厅克,就相當(dāng)于我們寫(xiě)布局時(shí)將某一個(gè)View擺在另一個(gè)View的上下左右的某一個(gè)位置赔退。所以在顯示PopupWindow的時(shí)候要傳入一個(gè)View或者ViewGroup,而Dialog只需要傳一個(gè)Context已骇。并且如果在onCreate的時(shí)候顯示Dialog的話程序會(huì)崩潰离钝,顯示這樣一個(gè)報(bào)錯(cuò)信息,Unable to add window -- token null is not valid; is your activity running褪储?說(shuō)明在Activity完全顯示出來(lái)的時(shí)候卵渴,或者說(shuō)界面上的View完全顯示出來(lái)的時(shí)候PopupWindow是無(wú)法顯示出來(lái)的。因?yàn)镻opupWindow會(huì)依附于某一個(gè)具體的view鲤竹,所以在創(chuàng)建的時(shí)候view并沒(méi)有加載出來(lái)浪读,所以會(huì)報(bào)錯(cuò)。所以如果想要在界面顯示的時(shí)候就彈出PopupWindow需要重寫(xiě)onWindowFocusChanged方法辛藻,判斷Activity完全顯示碘橘,并且已經(jīng)拿到了焦點(diǎn)。這個(gè)時(shí)候才能進(jìn)行PopupWindow的顯示吱肌。