1)几个常用的构造方法
我们在文档中可以看到,提供给我们的PopupWindow的构造方法有九种之多,这里只贴实际 开发中用得较多的几个构造方法:
public PopupWindow (Context context)
public PopupWindow(View contentView, int width, int height)
public PopupWindow(View contentView)
public PopupWindow(View contentView, int width, int height, boolean focusable)
参数就不用多解释了吧,contentView是PopupWindow显示的View,focusable是否显示焦点
2)常用的一些方法
下面介绍几个用得较多的一些方法,其他的可自行查阅文档:
setContentView(View contentView):设置PopupWindow显示的View
getContentView():获得PopupWindow显示的View
showAsDropDown(View anchor):相对某个控件的位置(正左下方),无偏移
showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置,有偏移
showAtLocation(View parent, int gravity, int x, int y): 相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移 PS:parent这个参数只要是activity中的view就可以了!
setWidth/setHeight:设置宽高,也可以在构造方法那里指定好宽高, 除了可以写具体的值,还可以用WRAP_CONTENT或MATCH_PARENT, popupWindow的width和height属性直接和第一层View相对应。
setFocusable(true):设置焦点,PopupWindow弹出后,所有的触屏和物理按键都由PopupWindows 处理。其他任何事件的响应都必须发生在PopupWindow消失之后,(home 等系统层面的事件除外)。 比如这样一个PopupWindow出现的时候,按back键首先是让PopupWindow消失,第二次按才是退出 activity,准确的说是想退出activity你得首先让PopupWindow消失,因为不并是任何情况下按back PopupWindow都会消失,必须在PopupWindow设置了背景的情况下 。
setAnimationStyle(int):设置动画效果
Activity上简单实现PopWindow
//无任何效果的弹窗
private void showNoneEffect() {
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View vPopupWindow = inflater.inflate(R.layout.popupwindow, null, false);//引入弹窗布局
popupWindow = new PopupWindow(vPopupWindow, ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.WRAP_CONTENT, true);
//引入依附的布局
View parentView = LayoutInflater.from(PopupWindowActivity.this).inflate(R.layout.layout_popupwindow, null);
//相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移
popupWindow.showAtLocation(parentView, Gravity.BOTTOM, 0, 0);
}
PopWindow工具类
/**
* popupwindow 底层实际上是通过 windowmanager 添加 一个 叫 DecorView(FrameLayout)的方式实现的。
* <p>
* popupwindow 一般设计到三个 view,{1.mDecorView(FrameLayout),2.mBackgroundView(FrameLayout),3.mContentView(我们要显示的layout)}
* <p>
* 1 mDecorView: popupWindow 的根view,它的大小就是通过 设置popouWindow 的 宽高来决定的(setHeight,setWidth)
* <p>
* 2 mBackgroundView:它不一定会创建,取决于 是否调用了 setBackgroundDrawable 给 popupWindow 设置背景,如果设置了那么会创建一个 mBackgroundView
* 然后添加到mDecorView 上,如下:
* <p>
* if (mBackground != null) { // 是否设置里背景
* mBackgroundView = createBackgroundView(mContentView);
* mBackgroundView.setBackground(mBackground);
* } else {
* mBackgroundView = mContentView;
* }
* <p>
* //把我们的 mContentView 的传进来 创建 mBackgroundView
* <p>
* private PopupBackgroundView createBackgroundView(View contentView) {
* final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
* final int height;
* if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
* height = WRAP_CONTENT;
* } else {
* height = MATCH_PARENT;
* }
* <p>
* final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
* final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
* MATCH_PARENT, height);
* <p>
* // 把我们的 mContentView 添加到 mBackgroundView上面,
* // mContentView的宽度是 MATCH_PARENT(和mBackgroundView的宽度一致)。
* // mContentView的高度 取决于 mContentView.getLayoutParams() ,要么是MATCH_PARENT,要么是 WRAP_CONTENT
* backgroundView.addView(contentView, listParams);
* <p>
* return backgroundView;
* }
* <p>
* mDecorView = createDecorView(mBackgroundView);
* <p>
* // 通过把mBackgroundView 传进来 创建 mDecorView,
* private PopupDecorView createDecorView(View contentView) {
* final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
* final int height;
* <p>
* // 如果 mContentView 的 layoutParams != null && layoutParams.height == WRAP_CONTENT 时,高度为 wrap_content,否则为 match_parent
* if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
* height = WRAP_CONTENT;
* } else {
* height = MATCH_PARENT;
* }
* <p>
* final PopupDecorView decorView = new PopupDecorView(mContext);
* // 把 mBackgroundView添加到了 mDecorView上
* // mBackgroundView 的宽度是 MATCH_PARENT(和 mDecorView 一样宽,也就是说等于 setWidth的值)
* <p>
* //mBackgroundView 的高度 取决于mBackgroundView.getLayoutParams(),要么是MATCH_PARENT,要么是 WRAP_CONTENT
* <p>
* decorView.addView(contentView, MATCH_PARENT, height);
* decorView.setClipChildren(false);
* decorView.setClipToPadding(false);
* <p>
* return decorView;
* }
* <p>
* <p>
* <p>
* <p>
* 3 mContentView,这就是我们调用popupwindow 的 setContentView 方法传进去我们要显示的内容view
* <p>
* <p>
* 如果设置了pop 的backgrounddrawable 那么创建顺序是
* <p>
* 根据 contentview 创建 backgroundview,再根据backgroundview 创建 decorview,最后 windowmanager add decorview
* <p>
* <p>
* 如果没有设置pop 的backgrounddrawable的背景,那么创建顺序是
* <p>
* 直接把 contentview 赋值给 backgroundview。 更具 backgroundview 创建 decorview ,最后 windowmanager add decorview
* <p>
* <p>
* <p>
* decorview 的宽高是由 setHeight 和 setWidth 决定的。
* <p>
* backgroundview 和 contentview 的 宽度都是不能修改的,都为 match_parent,高度要么是 wrap_content ,要么是 match_parent,只有当
* <p>
* contentview 的 layoutparams 不为 null, 并且其 layoutparams.height == wrap_content 时 ,高度为wrap_content 否则其他情况都为match_parent
*/
/**
*
*
* 使用方式:
* 1. 自己写一个子类继承 在子类内部去调用setContentView(view)以及对view 的初始化
* 2. 直接new 一个 MvpCommonPopView 实例,调用 setContentView(view);。在调用者里面对 view 进行初始化
*
* 然后调用 showCenter(view)方法 进行显示,把pop 显示在屏幕中间,view 参数可以是任意一个 view .
*
* 该pop 可以显示 一下几个功能:<p/>
*
* 1. 可以设置pop 外部区域点击时是否关闭pop :{@link #setTouchOutsideDismiss(boolean)}<p/> // 默认不关闭
* 2. 可以设置按返回键是否可以关闭pop : {@link #setOnBackKeyDismiss(boolean)}<p/> // 默认是按返回键关闭
* 3. 可是设置点击pop 外部时事件是否传递到 pop 的后面 : {@link #setOutsideCanTouch(boolean)}<p/> // 默认不传递
*
*
*
*/
public class MvpCommonPopView extends PopupWindow {
private boolean isOutsideTouchDismiss; // 点击pop 外部是否消失
private boolean isBackKeyDismiss = true;// 按返回键是否消失
private Activity context;
public MvpCommonPopView(Context context, int width, int height) {
super(context);
setHeight(height); //
setWidth(width);
this.context = (Activity) context;
init(context);
}
// 这个构造方法,pop 的高度是WRAP_CONTENT,宽度是 MATCH_PARENT
public MvpCommonPopView(Context context) {
this(context,WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.WRAP_CONTENT);
}
protected void init(Context context) {
// 在比较老的版本上,不设置背景就不能响应返回键和点击外部消失的,但是新版本google api 已经修改了,为了保险起见,设个背景最好
setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
backgroundAlpha( 0.5f);
/**
* 设置widow 是否获取焦点,默认false,如果为false,那么点击pop以外的区域,事件会传递到popu后面的(behind)界面上.
* 点击pop 以外的区域pop 不会消失,注意如果pop 里面有输入框,那么输入框不能弹出键盘
* 如果设置为true,pop 会有焦点,点击外部时 pop 会消失。
*/
setFocusable(true);
// 这个方法是控制是否当pop 以外的区域点击时是否发送 MotionEvent.ACTION_OUTSIDE 通知 pop,true 通知,false 不通知,但是 这个方法必须要 setFocusable = false ,setTouchable = true 时才有效果
setOutsideTouchable(true);
setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
if ((event.getAction() == MotionEvent.ACTION_DOWN) // 当 setFocusable为 true 点击外部会收到这个事件
&& ((x < 0) || (x >= v.getWidth()) || (y < 0) || (y >= v.getHeight()))) {
if (isOutsideTouchDismiss) {
dismiss();
}
return true;
}
/* if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { // ACTION_OUTSIDE 事件是 当 setOutsideTouchable(true) 方法 设置为true 时,才会被接受。否则不会接收这个事件
if (isOutsideTouchDismiss) {
CommonPopView.super.dismiss();
return true;
}
}*/
/**
* 返回 true 不拦截,走默认行为
*
* @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);
* }
* }
*/
return false;
}
});
}
/**
* 设置点击pop 外部是否消失,这个方法只有在给pop 设置的
* 宽高不是Match_parent 才有效,因为设置为了Match_parent这个都是pop,那就没有点击外部只说
* @param dismiss
*/
//
public void setTouchOutsideDismiss(boolean dismiss) {
isOutsideTouchDismiss = dismiss;
}
// view: 任意一个已经显示在界面上的view,注意如果这个view 没有添加的 window 上时,这个方法会报错。
// 因为 pop 在显示的时候需要通过 view.getWidowToken.
public void showCenter(View view){
showAtLocation(view, Gravity.CENTER,0,0);
}
public void setOnBackKeyDismiss(boolean dismiss) {
isBackKeyDismiss = dismiss;
}
@Override
public void dismiss() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StackTraceElement traceElement = stackTrace[3];
String fileName = traceElement.getFileName(); // 拿到调用 dismiss 方法的类名
fileName = fileName.substring(0, fileName.lastIndexOf("."));
if (!fileName.equals(PopupWindow.class.getSimpleName())) { // 如果调用者不是从系统来的,那么就是我们认为的
destroy();
super.dismiss();
} else {
if (!isBackKeyDismiss) {
return;
} else {
destroy();
super.dismiss();
}
}
}
private void destroy(){
backgroundAlpha(1);
this.context = null;
}
/**
* 设置pop 外部是否可点击,也就是点击pop 的外部 能把事件传递到pop 的 behind
* <p>
* 注意:如果 设置为true,那么再设置点击外部关闭 pop 将会无效,因此在为true 想关闭 pop ,必须手动关闭,调用close
*
* @param touch
*/
public void setOutsideCanTouch(boolean touch) {
// 设置为false, 点击pop 外部,事件会传递到 pop 的behind,设置为true ,不会。虽然设置为false ,但是 pop 里面如果有 输入框任然可以获取焦点,弹出键盘,这和 setFocusable = false 不一样。
if(MvpUtils.hasQ()){
setTouchModal(!touch);
}
}
/**
* 设置添加屏幕的背景透明度
*
* @param bgAlpha
*/
public void backgroundAlpha( float bgAlpha) {
WindowManager.LayoutParams lp = context.getWindow().getAttributes();
lp.alpha = bgAlpha;
context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
context.getWindow().setAttributes(lp);
}
}
创建类继承PopWindow工具类
public class AgreementPop extends MvpCommonPopView {
private TextView mBtnAgree;
private TextView mBtnCancel;
private IPopClickListener mListener;
public AgreementPop(Context context) {
super(context);
initView(context);
}
public void setListener(IPopClickListener listener) {
this.mListener = listener;
}
private void initView(Context context){
//充气布局
View v = LayoutInflater.from(context).inflate(R.layout.layout_splash_pop,null);
//获取控件
mBtnAgree = v.findViewById(R.id.splash_pop_btn_agree);
mBtnCancel = v.findViewById(R.id.splash_pop_btn_stop);
//确定监听
mBtnAgree.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mListener != null){
mListener.onAgree();
}
}
});
//取消监听
mBtnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mListener != null){
mListener.onCancel();
}
}
});
//不获取焦点
mBtnCancel.setFocusable(false);
mBtnAgree.setFocusable(false);
setContentView(v);
//无法返回
setOnBackKeyDismiss(false);
}
//接口 四个
public interface IPopClickListener{
void onCancel();
void onAgree();
//服务协议
void onUserAgreement();
//隐私政策
void onPrivacyPolicy();
}
}
使用:
AgreementPop agreementPop = new AgreementPop(this);
//监听
agreementPop.setListener(new AgreementPop.IPopClickListener() {
@Override
public void onCancel() {
//退出 清除sp并 返回退出应用
MvpManager.launchFail();
//退出
finish();
}
@Override
public void onAgree() {
//显示引导页
agreementPop.dismiss();
//跳转到引导页
showGuidePage();
}
@Override
public void onUserAgreement() {
}
@Override
public void onPrivacyPolicy() {
}
});









网友评论