看篇文章之前,建议先看看前面几篇,这样理解会更深刻,下面我们来看看PopupWindow的显示过程
一、PopupWindow的创建
public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
mContext = context;
// 得到一个WindowManager对象
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
}
二、setContentView
public void setContentView(View contentView) {
if (isShowing()) {
return;
}
mContentView = contentView;
if (mContext == null && mContentView != null) {
mContext = mContentView.getContext();
}
if (mWindowManager == null && mContentView != null) {
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
}
就是将contentView赋值给成员变量mContentView,并且确保mWindowManager对象不为null。
三、显示过程
我们这里以showAtLocation函数为例,进行查看
public void showAtLocation(IBinder token, int gravity, int x, int y) {
if (isShowing() || mContentView == null) {
return;
}
unregisterForScrollChanged();
mIsShowing = true;
mIsDropdown = false;
// 1、获取布局属性
WindowManager.LayoutParams p = createPopupLayout(token);
p.windowAnimations = computeAnimationResource();
// 2、做一些准备工作
preparePopup(p);
if (gravity == Gravity.NO_GRAVITY) {
gravity = Gravity.TOP | Gravity.START;
}
p.gravity = gravity;
p.x = x;
p.y = y;
if (mHeightMode < 0) p.height = mLastHeight = mHeightMode;
if (mWidthMode < 0) p.width = mLastWidth = mWidthMode;
// 3、进行显示
invokePopup(p);
}
从前面的分析我们知道,WindowManager添加一个view需要两个参数,一个是需要添加的view对象,一个是布局属性,在前面的setContent函数已经给出了需要添加的view了,对于布局属性就在createPopupLayout函数中生成。
private WindowManager.LayoutParams createPopupLayout(IBinder token) {
WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.gravity = Gravity.START | Gravity.TOP;
p.width = mLastWidth = mWidth;
p.height = mLastHeight = mHeight;
if (mBackground != null) {
p.format = mBackground.getOpacity();
} else {
p.format = PixelFormat.TRANSLUCENT;
}
p.flags = computeFlags(p.flags);
p.type = mWindowLayoutType;
p.token = token;
p.softInputMode = mSoftInputMode;
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
return p;
}
下面看看显示过程,其实猜都可以猜到,我们的view以及该view对应的布局属性已经准备好了,下面就是直接调用windowManager的addView方法,下面我们看看invokePopup函数。
private void invokePopup(WindowManager.LayoutParams p) {
if (mContext != null) {
p.packageName = mContext.getPackageName();
}
mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
setLayoutDirectionFromAnchor();
mWindowManager.addView(mPopupView, p);
}
这里看到使用的是mPopupView,但是在前面setContentView中设置的是mContentView,其实mPopupView对应的就是mContentView,它的赋值操作在preparePopup函数中。
private void preparePopup(WindowManager.LayoutParams p) {
if (mBackground != null) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
int height = ViewGroup.LayoutParams.MATCH_PARENT;
if (layoutParams != null &&
layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
// when a background is available, we embed the content view
// within another view that owns the background drawable
PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, height
);
popupViewContainer.setBackgroundDrawable(mBackground);
popupViewContainer.addView(mContentView, listParams);
mPopupView = popupViewContainer;
} else {
mPopupView = mContentView;
}
mPopupViewInitialLayoutDirectionInherited =
(mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
这里对mContentView做了一些处理然后赋值给了mPopupView。
从这里我们可以看到,对应界面的显示,其实思路都是一样的,都是使用的WindowManager,也就是说WindowManagerService对我们所有的UI会进行管理。