Android Dialog和popupWidow的场景解析.

Dialog常用方法:

addContentView(View view, ViewGroup.LayoutParams params):

在原有布局基础上添加啊新的View,需要传入LayoutParamers对布局进行设置。

LayoutParamers是ViewGroup的一个内部类,通过LayoutParams来对ViewGroup进行设置,包括View的宽高属性。在LayoutParamers中还有MarginLayoutParams内部类,

用来设置Margin属性

dispatchKeyEvent(KeyEvent event):

拦截按键事件,通过重写此事件可以对按键事件进行拦截,

dispatchTouchEvent(MotionEvent ev):

同理可以进行触摸事件的拦截。

getWindow():获取window,可以通过getWindow()方法获取到Dialog依附于的Window,Window是一个抽象类,public abstract class Window {.....}

这是在安卓源码中的定义,其实对于Android来讲,我们看到的任何一个界面其实本质上来讲都是一个Window,包括Activity、Dialog所有能看到的界面其实都是通过Window来实现的。Window的唯一实现类是PhoneWindow,对于任何一个显示的界面来讲,所有都是依附于PhoneWindow。

hide():通过hide方法能够对Dialog进行隐藏。通过源码可以发现

```

/**

* Hide the dialog, but do not dismiss it.

*/

public void hide() {

if (mDecor !=null) {

mDecor.setVisibility(View.GONE);

}

}

```

只是对Dialog依附于的View进行了隐藏,但是没有dismiss。讲到hide方法自然就要说到dismiss()方法,通过源码中可以发现,

```

try {

mWindowManager.removeViewImmediate(mDecor);

} finally {

if (mActionMode != null) {

mActionMode.finish();

}

mDecor = null;

mWindow.closeAllPanels();

onStop();

mShowing = false;

sendDismissMessage();

}

```

只有调用dismiss方法才真正的移除了View。

isShowing():判断Dialog是否正在显示

requestWindowFeature(int featureId):设置windows的样式,其实这个方法只是直接调用了Window的设置样式的方法,本身并没有进行什么处理。

通过源码可以看到 

```

public final boolean requestWindowFeature(int featureId) {

return getWindow().requestFeature(featureId);

}

```

setCancelable(boolean flag):BACK键是否可以使Dialog进行dismiss

setCanceledOnTouchOutside(boolean cancel):触摸外部是否可以进行dismiss

setContentView(View view):设置布局,在自定义Dialog中,这个方法是十分常用的,我们在开发中自己写好的布局就是通过这个方法添加到Dialog中,可以发现在Activity

中也存在这个方法。原理上都是一样的。都是通过Window进行设置。

show():显示,如果在show()方法中进行一些处理数据等的操作的话,不要重写show方法。Dialog中提供了类似于Activity的onStart方法,名字也叫作onStart方法,可以重写这个方法进行一些其他的操作。从源码中可以看到在show方法中会调用onStart方法。

cancel():对话框消失,回调DialogInterface.OnCancelListener,其实也调用了dismiss()

onCreate(Bundle savedInstanceState)同于Activity

onStop()与onStart方法一样,会在dismissDialog方法中调用,这个方法也就是dismiss方法中调用的方法。

PopupWindow方法:

dismiss():此方法是隐藏PopupWindow的方法

isShowing():正在显示

setBackgroundDrawable(Drawable background):设置背景,此参数可以为空

setContentView(View contentView):与Dialog一样,设置显示的布局

setFocusable(boolean focusable):设置可以获取焦点

setOnDismissListener(PopupWindow.OnDismissListener onDismissListener):隐藏监听

setOutsideTouchable(boolean touchable):外部可点击进行dismiss

showAsDropDown(View anchor, int xoff, int yoff):调用此方法进行PopupWindow的显示,设置显示的位置,相对于某一个View的偏移量。有好几个重构方法。意思其实都是一样。

showAsDropDown(View anchor)

showAtLocation(View parent, int gravity, int x, int y):此方法也用于显示,但是相对应得view存在差别,showAsDropDown是相对于某一个view进行定位,showAtLocation是相对于父布局进行定位。

Dialog和PopupWindow的场景解析

对于Dialog和PopupWindow来讲,都是可以用来弹出提示信息,在App开发中都是比较常用,对于一个应用场景来讲,很多时候用这两种都可以实现。在不同的场景下可以根据二者的区别和特点来选择使用哪一个。PopupWindow的特点是定位更准确,宽高和边界都比较清晰。对于弹出Tags来讲很适合。Dialog相对于更独立,相当于一个新的界面,或者是应用程序的Loading,往往使用Dialog更好。

对于二者的区别网上的总结有很多,这里我就不再说了,网上的说法有一些是正确的,但也有错误的情况。最后想说一下同样是弹出式对话框,或者说弹出式控件(也可能叫控件不太恰当,就不详细追究了)二者在本质上的区别。这里就得需要同源码上来分析了。

以下是Dialog的构造函数

```

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);

}

```

可以发现,对于Dialog来讲,每次我们new一个Dialog的时候,其实都会new一个PhoneWindow,前面已经说了,PhoneWindow是Window的实现类。所以说,对于Dialog来讲,每一个Dialog相当于一个新的Window,Activity其实也是一个Window。所以说,当我们在创建Activity的过程中,也就是onCreate方法中调用Dialog的show方法是可以弹出Dialog的。因为Dialog是独立的Window。相当于一个新的Window盖住了Acitvity这个Window。并且在Dialog显示的时候,Activity只是被遮盖住了。但是并没有调用生命周期的onPuse和onStop方法。有的人可能会问,为什么另一个Activity盖住了这个Activity就会调用这个方法呢。因为Activity是通过Activity栈进行管理的,猜测只有在Activity栈中发生了变化才会对Activity的生命周期产生变化。Dialog并没有入Activity栈,只是遮挡住了Activity。这是Dialog的实现原理,下面说PopupWindow的实现原理。通过PopupWindow的源码可以发现,在PopupWindow的实现过程中并没有出现Window这个东西。在Dialog中Dialog的DecorView是通过Window拿到的,而PopupWindow的DecorView其实是这么一个东西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);

}

}

```

可以发现,PopupDecorView是继承于FrameLayout。也就是说PopupWindow本质上就是一个View,就相当于我们写布局时将某一个View摆在另一个View的上下左右的某一个位置。所以在显示PopupWindow的时候要传入一个View或者ViewGroup,而Dialog只需要传一个Context。并且如果在onCreate的时候显示Dialog的话程序会崩溃,显示这样一个报错信息,Unable to add window -- token null is not valid; is your activity running?说明在Activity完全显示出来的时候,或者说界面上的View完全显示出来的时候PopupWindow是无法显示出来的。因为PopupWindow会依附于某一个具体的view,所以在创建的时候view并没有加载出来,所以会报错。所以如果想要在界面显示的时候就弹出PopupWindow需要重写onWindowFocusChanged方法,判断Activity完全显示,并且已经拿到了焦点。这个时候才能进行PopupWindow的显示。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容