本文旨在介紹Android中的兩種彈窗PopupWindow和Dialog的區(qū)別。以及關(guān)于寫(xiě)代碼的一些思考订歪。
其實(shí)之前各類(lèi)彈窗脖祈,都是使用PopupWindow來(lái)實(shí)現(xiàn)的。因?yàn)镻opupWindow能實(shí)現(xiàn)這些需求刷晋,雖然有的時(shí)候有些麻煩盖高,比如前面的文章有寫(xiě)到PopupWindow去實(shí)現(xiàn)蒙版遮罩的問(wèn)題。它本身是沒(méi)有蒙版提供的眼虱。
我們項(xiàng)目中使用各種方法去給它增加遮蓋層喻奥。如今看來(lái)確實(shí)是有些愚蠢的。我不介意說(shuō)出以前存在的問(wèn)題捏悬,因?yàn)榘l(fā)現(xiàn)問(wèn)題并改進(jìn)問(wèn)題才是學(xué)習(xí)的過(guò)程撞蚕。當(dāng)然也希望看到這篇文章的小伙伴有自己的想法可以多多溝通,如果也有跟過(guò)去的我一樣盲目的使用PopupWindow來(lái)做需求的同學(xué)过牙,可以從這篇文章中學(xué)到東西甥厦。
這次遇到PopupWindow中的輸入框無(wú)法復(fù)制粘貼,慣例先去網(wǎng)上搜了一下抒和,發(fā)現(xiàn)很多人遇到同樣的問(wèn)題矫渔,解決方案全部都是換成DialogFragment就好了。得到這個(gè)結(jié)果后我有點(diǎn)不服氣摧莽,為啥我PopupWindow復(fù)制粘貼就不行庙洼,為啥就非得換成DialogFragment。關(guān)于為啥PopupWindow復(fù)制粘貼不行镊辕,我開(kāi)始各種搜索油够,也去學(xué)習(xí)了一下長(zhǎng)按復(fù)制粘貼的源碼,但是感覺(jué)自己的智商不是很夠征懈,到現(xiàn)在也沒(méi)弄明白到底為啥不行石咬。如果有知道的小伙伴希望不吝賜教。
通過(guò)這個(gè)問(wèn)題我開(kāi)始思考卖哎,為什么我不管什么彈窗類(lèi)的需求都用PopupWindow來(lái)實(shí)現(xiàn)鬼悠,到底它有啥好的?既然它這么好亏娜,那dialog存在的意義又是什么呢焕窝。其實(shí)以前的做法說(shuō)白了,就是有項(xiàng)目里有了一套沒(méi)什么問(wèn)題的類(lèi)似代碼以后维贺,再來(lái)相關(guān)的需求它掂,不外乎就是復(fù)制粘貼去實(shí)現(xiàn)。所以就導(dǎo)致了一直用PopupWindow的這個(gè)問(wèn)題溯泣。其實(shí)在用的時(shí)候虐秋,尤其是在強(qiáng)行給PopupWindow加入灰色蒙版的時(shí)候榕茧,我就有個(gè)疑問(wèn),為什么PopupWindow不自帶蒙版客给,還要自己去實(shí)現(xiàn)用押,而且網(wǎng)上處理這類(lèi)問(wèn)題的文章并不多。這次真的想弄明白官方為什么要提供兩種彈窗以后起愈,這些疑惑也就想通了只恨。
簡(jiǎn)單介紹:
Dialog
默認(rèn)帶灰色遮蓋層,會(huì)覆蓋整個(gè)屏幕抬虽,使dialog下層的內(nèi)容不可點(diǎn)擊官觅,只有先取消dialog才能繼續(xù)點(diǎn)擊其他內(nèi)容。
所以當(dāng)你的需求阐污,是需要一個(gè)灰色覆蓋層的時(shí)候休涤,優(yōu)先考慮使用Dialog。
Dialog可以指定顯示在屏幕個(gè)各個(gè)位置笛辟,可以通過(guò)設(shè)置偏移實(shí)現(xiàn)顯示在屏幕的某個(gè)位置上功氨。
灰色蒙層的透明度可控,展示位置可控手幢,頁(yè)面布局可控捷凄。
AlertDialog
沒(méi)啥說(shuō)的,是Dialog的一個(gè)子類(lèi)围来,就是為了更方便使用而存在的跺涤。
DialogFragment
谷歌官網(wǎng)推薦的Dialog實(shí)現(xiàn)方式,使用這個(gè)可以讓Dialog的生命周期可控监透。
用法可以參考官網(wǎng)對(duì)DialogFragment的介紹:https://developer.android.google.cn/guide/topics/ui/dialogs#java
PopupWindow
不帶灰色遮蓋層桶错,可以顯示在屏幕的任意位置,也可以基于某個(gè)view的位置顯示胀蛮。
其他能實(shí)現(xiàn)的功能基本與dialog一致院刁。所以其實(shí)這兩個(gè)確實(shí)是可以互相替代的。
總結(jié)粪狼,我的理解:
一般來(lái)說(shuō)退腥,我們開(kāi)發(fā)需求的時(shí)候很少會(huì)使用到系統(tǒng)自帶的樣式,基本上都需要自定義樣式的再榄。在這一點(diǎn)上阅虫,Dialog和PopupWindow并沒(méi)什么區(qū)別,都是可以做到的不跟。
1.那么通常當(dāng)需求中需要實(shí)現(xiàn)灰色蒙版的時(shí)候,建議使用DialogFragment實(shí)現(xiàn)米碰,自帶蒙版窝革。
2.當(dāng)需求的彈窗里面有輸入框的時(shí)候购城,建議使用DialogFragment(因?yàn)橐话阏6夹枰С謴?fù)制粘貼的)
3.如果需求是要求基于某個(gè)view的位置去顯示,那么建議使用PopupWindow虐译。如果不帶蒙版效果瘪板,建議使用PopupWindow。
具體情況還是具體分析啦漆诽。不過(guò)通過(guò)這次讓我明白一個(gè)道理侮攀,官方提供的同類(lèi)View是針對(duì)不同情況下存在更合適使用的。雖然很多功能我們通過(guò)一套邏輯就能實(shí)現(xiàn)厢拭,但是還是應(yīng)該多去思考一下不同的實(shí)現(xiàn)方式到底有什么不同兰英,選一個(gè)更合適更合理的方法。
下面附一下我學(xué)習(xí)了DialogFragment后供鸠,對(duì)它的一個(gè)簡(jiǎn)單基類(lèi)封裝畦贸,旨在對(duì)同類(lèi)彈窗的一個(gè)封裝,減少每個(gè)彈窗都要去初始化dialog代碼楞捂,讓子類(lèi)只需要處理業(yè)務(wù)邏輯薄坏。
```
/**
?*?基于DialogFragment實(shí)現(xiàn)的dialog基類(lèi)(目的:基類(lèi)完成初始化彈窗等一些配置工作,子類(lèi)做邏輯處理)
?*?通過(guò)實(shí)現(xiàn)getLayoutId()返回布局文件寨闹,實(shí)現(xiàn)自定義布局
?*?通過(guò)實(shí)現(xiàn)initView()完成布局初始化胶坠。
?*?通過(guò)調(diào)用?show(FragmentManager?manager,?String?tag)?方法展示彈窗。
?*?DialogFragment的dismiss處理了fragment的移除操作繁堡,因此每次使用時(shí)直接New對(duì)象即可沈善,不需考慮復(fù)用問(wèn)題(親試:考慮的話會(huì)存在一些問(wèn)題)。
?*
?*?可配置帖蔓,彈窗位置(當(dāng)居頂部顯示的時(shí)候矮瘟,可配置距頂部的百分比setMarginTop)
?*?可配置是否可取消
?*?可配置灰色蒙版透明度
?*
*?Time:2021/2/4
*?Author:yang.dong
*/
public?abstract?class?BaseDialogFragmentextends?DialogFragment?{
protected?ViewmLayout;
? ? private?AlertDialogdialog;
? ?@NonNull
@Override
? ?public?DialogonCreateDialog(@Nullable?Bundle?savedInstanceState)?{
if(dialog?==null){
AlertDialog.Builder?builder?=new?AlertDialog.Builder(getActivity());
? ? ? ? ? ?LayoutInflater?inflater?=?getActivity().getLayoutInflater();
? ? ? ? ? ?mLayout?=?inflater.inflate(getLayoutId(),?null);
? ? ? ? ? ?builder.setView(mLayout);
? ? ? ? ? ?dialog?=?builder.create();
? ? ? ? ? ?Window?dialogWindow?=dialog.getWindow();
? ? ? ? ? ?WindowManager.LayoutParams?lp?=?dialogWindow.getAttributes();
? ? ? ? ? ?dialogWindow.setDimAmount(alpha);?//設(shè)置灰色遮蓋層的透明度
? ? ? ? ? ?dialogWindow.setBackgroundDrawableResource(android.R.color.transparent);?//dialog會(huì)有默認(rèn)背景,設(shè)置透明后塑娇,可以完美展示自己的布局
? ? ? ? ? ?dialogWindow.setGravity(gravity);?//設(shè)置居頂部顯示
? ? ? ? ? ?if(gravity?==?Gravity.TOP){//距離屏幕頂部
? ? ? ? ? ? ? ?Display?d?=?getActivity().getWindowManager().getDefaultDisplay();?//?獲取屏幕寬澈侠、高用
? ? ? ? ? ? ? ?lp.y?=?(int)(d.getHeight()?*marginTop);?//?新位置Y坐標(biāo)
? ? ? ? ? ?}
dialogWindow.setAttributes(lp);
? ? ? ? ? ?dialog.setCanceledOnTouchOutside(cancelable);
? ? ? ?}
initView();
? ? ? ? return?dialog;
? ?}
@Override
? ?public?void?show(FragmentManager?manager,?String?tag)?{
//防止重復(fù)添加fragment崩潰
? ? ? ?FragmentTransaction?transaction?=?manager.beginTransaction();
? ? ? ?Fragment?fragment?=?manager.findFragmentByTag(tag);
? ? ? ? if(fragment?!=null){
transaction.remove(fragment);
? ? ? ?}
transaction.commit();
? ? ? ? super.show(manager,?tag);
? ?}
private?float?alpha?=0.3f;
? ? private?float?marginTop?=0.3f;?//?距離頂部的高度(當(dāng)Gravity為top時(shí),生效)
? ?private?int?gravity?=?Gravity.TOP;
? ? private?boolean?cancelable?=false;?//點(diǎn)擊灰色區(qū)域是否可以取消埋酬,默認(rèn)不取消
? ?private?void?setAlpha(float?alpha){
this.alpha?=?alpha;
? ?}
protected?void?setMarginTop(float?marginTop){
this.marginTop?=?marginTop;
? ?}
protected?void?setGravity(int?gravity){//Gravity.TOP
? ? ? ?this.gravity?=?gravity;
? ?}
protected?void?setCancel(boolean?cancelable){//Gravity.TOP
? ? ? ?this.cancelable?=?cancelable;
? ?}
protected?abstract?int?getLayoutId();
? ? protected?abstract?void?initView();
}
```
另外在使用的過(guò)程中哨啃,遇到兩個(gè)問(wèn)題:
1.一直覺(jué)得在實(shí)例化對(duì)象的時(shí)候(也就是點(diǎn)擊按鈕出現(xiàn)彈窗的時(shí)候),不應(yīng)該每次都去創(chuàng)建fragment實(shí)例写妥,而應(yīng)該去判一個(gè)空拳球,創(chuàng)建過(guò)一次以后,再次點(diǎn)擊就只去修改UI珍特。之前使用PopupWindow的時(shí)候一直采用這種做法祝峻。但是這次真的是,去這樣處理感覺(jué)給自己挖了一個(gè)好大的坑。具體現(xiàn)象就是莱找,第二次點(diǎn)擊后UI無(wú)法正常刷新酬姆。
嘗試解決了好久,最后我妥協(xié)了奥溺。還是每次都去new一個(gè)新的吧辞色!也去看了一下dismiss方法的源碼,dismiss后是會(huì)把fragment給移除掉的浮定。并且在Profiler中去觀察了我瘋狂點(diǎn)擊按鈕相满,一直出彈窗的情況下內(nèi)存變化。確實(shí)真的沒(méi)啥變化桦卒。放心去new就好了立美。(歡迎指教)
2.快速點(diǎn)擊導(dǎo)致閃退。其實(shí)這個(gè)問(wèn)題我是為了解決第一個(gè)問(wèn)題闸盔,在網(wǎng)上找答案的時(shí)候悯辙,看別人使用這個(gè)遇到的坑,說(shuō)快速連著點(diǎn)兩下就會(huì)閃退迎吵。我試了一下 躲撰, 果然特喵的閃退了。還真的是击费。拢蛋。。我想不出來(lái)當(dāng)時(shí)的心情蔫巩。感覺(jué)這種問(wèn)題真的不該我們來(lái)處理的谆棱。一個(gè)方案是別人幫我踩的坑,如上對(duì)show()方法的一個(gè)重寫(xiě)圆仔。重寫(xiě)了以后連續(xù)點(diǎn)兩次以后確實(shí)不閃退了垃瞧。不過(guò)感覺(jué)體驗(yàn)也不是特別好,彈窗還是會(huì)出現(xiàn)兩次坪郭,第一個(gè)先消失然后出現(xiàn)第二個(gè)个从。所以我又給點(diǎn)擊事件加上了防止連續(xù)點(diǎn)擊的處理。兩手準(zhǔn)備吧算是~
這次的分享就到這里啦歪沃,歡迎指教嗦锐,大家加油哦