续前言PopupWindow点击外部区域消失(二)
既然我们知道了为什么一定要先设置 mBackground 然后才能 dismiss PopupWindow,那么下面就来看怎么不用设置也能破之。。
首先还要来看这里:
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.");
}
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.setBackground(mBackground);
popupViewContainer.addView(mContentView, listParams);
mPopupView = popupViewContainer;
}else {
**mPopupView = mContentView;**
}
mPopupView.setElevation(mElevation);
mPopupViewInitialLayoutDirectionInherited =
(mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
既然 mBackgroud == null 的时候 mPopupView = mContentView;
那么我们可以为什么不可以通过自定义该View来处理它的点击事件呢,
在这里我自定义了一个LinearLayout ,下面来看它的实现,
public class MyLinearLayout extends LinearLayout{
public MyLinearLayout(Context context) {
this(context,null);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
private onMyDismissListener onMyDismissListener;
public void setOnMyDismissListener(onMyDismissListener onMyDismissListener) {
this.onMyDismissListener = onMyDismissListener;
}
private static final String TAG = "PopupWindow.MyLinearLayout";
private OnTouchListener mTouchInterceptor;
/**
* Set a callback for all touch events being dispatched to the popup
* window.
*/
public void setTouchInterceptor(OnTouchListener l) {
mTouchInterceptor = l;
}
@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) {
KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.startTracking(event, this);
}
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null && state.isTracking(event) && !event.isCanceled()) {
if(onMyDismissListener != null){
onMyDismissListener.onDismiss();
}
return true;
}
}
return super.dispatchKeyEvent(event);
} else {
return super.dispatchKeyEvent(event);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
return true;
}
return super.dispatchTouchEvent(ev);
}
@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()))) {
if(onMyDismissListener != null){
onMyDismissListener.onDismiss();
}
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
if(onMyDismissListener != null){
onMyDismissListener.onDismiss();
}
return true;
} else {
return super.onTouchEvent(event);
}
}
}
细心的朋友应该会发现这段代码与 PopupViewContainer 的代码特别相似,不错就是照着它的来写的。。值得一提的是这个地方
if(onMyDismissListener != null){
onMyDismissListener.onDismiss();
}
这里呢,我定义了一个接口来实现它的 dismiss() 功能。
public interface onMyDismissListener {
void onDismiss();
}
下面来看它的具体用法:
public class MyPopupWindow implements onMyDismissListener{
private static final String TAG = "MyPopupWindow";
public Context mContext;
private PopupWindow mPopupWindow;
private PopupWindowType type;
public PopupWindowType getType() {
return type;
}
public void setType(PopupWindowType type) {
this.type = type;
}
public MyPopupWindow(Context context,PopupWindowType type, int width, int height,
boolean focusable) {
this.mContext = context;
if(type == null){
type = PopupWindowType.TYPE_1;
}
View view = inflaterView(type);
mPopupWindow = new PopupWindow(view, width, height, focusable);
// mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
Drawable background = mPopupWindow.getBackground();
Log.v(TAG, ""+background);
mPopupWindow.setAnimationStyle(R.style.Animation);
mPopupWindow.setOutsideTouchable(focusable);
}
private View inflaterView(PopupWindowType type){
View contentView;
switch (type) {
case TYPE_1:
default:
contentView = LayoutInflater.from(mContext).inflate(R.layout.popup_view, null);
try {
MyLinearLayout layout = (MyLinearLayout) contentView.findViewById(R.id.mll);
layout.setOnMyDismissListener(this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e(TAG, "You must set a id name is rId's name " + " in rootlayout");
}
break;
}
return contentView;
}
@Override
public void onDismiss() {
mPopupWindow.dismiss();
}
public void showAtLocation(View findViewById, int gravity, int x, int y) {
// TODO Auto-generated method stub
mPopupWindow.showAtLocation(findViewById, gravity, x, y);
}
}
大家可以看到第 42,43行 我们并没有给他设置 mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
它实现了上述的接口,然后在 onDismiss 里调用了 mPopupWindow.dismiss(); 然后来看,第57,58行代码给layout设置监听方法,它的布局是这样的:
<?xml version="1.0" encoding="utf-8"?>
<com.xfchen.opensource.widget.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/shape_popup_bg"
android:orientation="vertical" >
// 可以自行添加布局
</com.xfchen.opensource.widget.MyLinearLayout>
然后在Activity里调用它就可以了,
private void showPopupWindow(){
MyPopupWindow myPopupWindow = new MyPopupWindow(this, PopupWindowType.TYPE_1, LayoutParams.MATCH_PARENT, 500, true);
myPopupWindow.showAtLocation(MainActivity.this.findViewById(R.id.rlMain), Gravity.BOTTOM|Gravity.CENTER_VERTICAL, 0, 0);
}
这里是设置它从底部显示。。然后就大功告成了,这个并不是什么特别适用的功能,只是为了证明我们的猜想是正确的。。如有不足,请不吝赐教!
代码下载地址