PopuWindow
前言
我只是想学习一下
PopuWindow
实现popuwindow的语法。
1. 首先唤醒一个布局
View contentView = LayoutInflater.from(this).inflate(R.layout.popuwindow_layout, null);
2. 然后创建一个popuwindow
PopupWindow popupWindow = new PopupWindow(contentView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);
第一个参数为popuwindow设置一个布局因为popuwindow没有自带的布局,二三 参数是设置宽高,第四个是设置是否取到焦点。
当然popuwindow的构造函数可是很多的
比如下面
显示popuwindow
- 唤醒了布局,创建了popuwindow最后就是显示了。 显示的方法都在这里
解释一下
//相对某个控件的位置(正左下方),无偏移
showAsDropDown(View anchor):
相对某个控件的位置,有偏移;xoff表示x轴的偏移,正值表示向左,负值表示向右;yoff表示相对y轴的偏移,正值是向下,负值是向上;
showAsDropDown(View anchor, int xoff, int yoff, int gravity)
相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移
showAtLocation(View parent, int gravity, int x, int y):
// 显示在v这个空间的下方
popupWindow.showAsDropDown(v);
// 可以显示在mCoor布局的任何地方
popupWindow.showAtLocation(mCoor, Gravity.BOTTOM, 0, 0);
这样的话可以成功显示了但是这里有一个bug就是不设置背景的时候会出现一个问题就是只可以点popuwindow其他地方都不可以点,包括后退键。
所以我们应该设置背景
popupWindow.setBackgroundDrawable(new BitmapDrawable(getResources(), BitmapFactory.decodeFile(null)));
至于为什么出现这种情况 我们需要看一下源码 那个后一点再说
为popuwindow加动画
首先定义一下显示动画
一个简单的透明度显示
出现时候的动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<alpha
android:duration="5000"
android:fromAlpha="0"
android:toAlpha="1"/>
</set>
消失时候的动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="5000"
android:fromAlpha="1"
android:toAlpha="0"/>
</set>
然后在style中定义
<style name="contextMenuAnim" parent="@android:style/Animation.Activity">
<item name="android:windowEnterAnimation">@anim/anim</item>
<item name="android:windowExitAnimation">@anim/anim2</item>
</style>
然后在文件中设置
popupWindow.setAnimationStyle(R.style.contextMenuAnim);
在这里说明一下为什么要为popuwindow添加背景
源码
点进去 setBackgroundDrawable
源码太长了 只显示一小部分
public void setBackgroundDrawable(Drawable background) {
mBackground = background;
}
然后我们看看在显示的时候有没有用到mBackground
popuWindow在显示的时候调用preparePopup(p);
public void showAtLocation(IBinder token, int gravity, int x, int y) {
if (isShowing() || mContentView == null) {
return;
}
TransitionManager.endTransitions(mDecorView);
unregisterForScrollChanged();
mIsShowing = true;
mIsDropdown = false;
final WindowManager.LayoutParams p = createPopupLayoutParams(token);
preparePopup(p);
// Only override the default if some gravity was specified.
if (gravity != Gravity.NO_GRAVITY) {
p.gravity = gravity;
}
p.x = x;
p.y = y;
invokePopup(p);
}
我们跟着点进去(其实是搜索mBackground得到的)
private void preparePopup(WindowManager.LayoutParams p) {
if (mContentView == null || mContext == null || mWindowManager == null) {
throw new IllegalStateException("You must specify a valid content view by "
+ "calling setContentView() before attempting to show the popup.");
}
// The old decor view may be transitioning out. Make sure it finishes
// and cleans up before we try to create another one.
if (mDecorView != null) {
mDecorView.cancelTransitions();
}
// When a background is available, we embed the content view within
// another view that owns the background drawable.
if (mBackground != null) {
mBackgroundView = createBackgroundView(mContentView);
mBackgroundView.setBackground(mBackground);
} else {
mBackgroundView = mContentView;
}
mDecorView = createDecorView(mBackgroundView);
// The background owner should be elevated so that it casts a shadow.
mBackgroundView.setElevation(mElevation);
// We may wrap that in another view, so we'll need to manually specify
// the surface insets.
final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
p.hasManualSurfaceInsets = true;
mPopupViewInitialLayoutDirectionInherited =
(mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
看看这一个
if (mBackground != null) {
mBackgroundView = createBackgroundView(mContentView);
mBackgroundView.setBackground(mBackground);
} else {
mBackgroundView = mContentView;
}
当背景为空的时候mBackgroundView = mContentView;
mContentView
什么 先看看
我找到了它在这里赋值
public void setContentView(View contentView) {
if (isShowing()) {
return;
}
mContentView = contentView;
。
。
。
省 略
}
然后这个方法在这里调用
public PopupWindow(View contentView, int width, int height, boolean focusable) {
if (contentView != null) {
mContext = contentView.getContext();
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
setContentView(contentView);
setWidth(width);
setHeight(height);
setFocusable(focusable);
}
可以看到mContentView
就是popuwindow的布局,那么说背景为空时就会套上一层布局
然后我们看看这层布局干了什么
一步步去查找
用背景创建了什么
mDecorView = createDecorView(mBackgroundView);
继续点击
private PopupDecorView createDecorView(View contentView) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
final int height;
if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
height = ViewGroup.LayoutParams.MATCH_PARENT;
}
final PopupDecorView decorView = new PopupDecorView(mContext);
decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
decorView.setClipChildren(false);
decorView.setClipToPadding(false);
return decorView;
}
返回decorView
而这个又干了些什么继续点final PopupDecorView decorView = new PopupDecorView(mContext);
好了最终大BOSS出现了
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);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismiss();
return true;
} else {
return super.onTouchEvent(event);
}
}
详细分析请看http://blog.csdn.net/harvic880925/article/details/49278705
看了之后我的总结是没有设置setOutsideTouchable(false)将生效
参考http://blog.csdn.net/harvic880925/article/details/49278705