Android Dialog和popupWidow的場(chǎng)景解析.

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的顯示吱肌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痘拆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子氮墨,更是在濱河造成了極大的恐慌纺蛆,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件规揪,死亡現(xiàn)場(chǎng)離奇詭異桥氏,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)猛铅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)字支,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人奸忽,你說(shuō)我怎么就攤上這事堕伪。” “怎么了月杉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵刃跛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我苛萎,道長(zhǎng)桨昙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任腌歉,我火速辦了婚禮蛙酪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翘盖。我一直安慰自己桂塞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布馍驯。 她就那樣靜靜地躺著阁危,像睡著了一般玛痊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狂打,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天擂煞,我揣著相機(jī)與錄音,去河邊找鬼趴乡。 笑死对省,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晾捏。 我是一名探鬼主播蒿涎,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惦辛!你這毒婦竟也來(lái)了劳秋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤裙品,失蹤者是張志新(化名)和其女友劉穎俗批,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體市怎,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岁忘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了区匠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片干像。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驰弄,靈堂內(nèi)的尸體忽然破棺而出麻汰,到底是詐尸還是另有隱情,我是刑警寧澤戚篙,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布五鲫,位于F島的核電站,受9級(jí)特大地震影響岔擂,放射性物質(zhì)發(fā)生泄漏位喂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一乱灵、第九天 我趴在偏房一處隱蔽的房頂上張望塑崖。 院中可真熱鬧,春花似錦痛倚、人聲如沸规婆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抒蚜。三九已至掘鄙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗡髓,已是汗流浹背通铲。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留器贩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓朋截,卻偏偏與公主長(zhǎng)得像蛹稍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子部服,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容