【PopupWindow】Android 自定义弹框PopupWindow参数详解及相关问题分析

本文介绍了Android中PopupWindow的使用,包括参数详解如背景遮盖层透明度、动画效果设置、焦点和触摸事件处理,以及键盘输入模式和显示位置的控制。同时,文章提供了示例代码,并讨论了在不同场景下的应用策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目的

平时在接触PoupoWidow时遇到的问题比较多,写这篇文章主要是将自己所了解的一些细节记录下来,避免以后忘记;
该文章主要介绍PopupWindow的参数详情、针对不同场景而产生的不同使用方法;下面PopupWindow用简称pw表示

具体实现

下面的实例没有进行具体需求的分拣哦!不能直接复制,不过直接复制过去pw是能正常显示的,只是会出现些许小问题,因为部分参数出现了冲突,请仔细下面的分析在设置具体的参数哦!

private PopupWindow mPopupWindow;
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_popupwindow, null);
*//创建PopupWindow实例*
mPopupWindow = new PopupWindow(view,LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT,true);
*//设置动画效果*
mPopupWindow.setAnimationStyle(R.style.shop_popup_window_anim);
*//设置遮盖层,需要在dismiss弹框后还原,即1.0f ,设置范围0~1f*
  backgroundAlpha(0.7f);
mPopupWindow.setFocusable(true);
mPopupWindow.setBackgroundDrawable(new BitmapDrawable(mContext.getResources(), (Bitmap) null));
mPopupWindow.setOutsideTouchable(false);
mPopupWindow.setTouchInterceptor(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (!mPopupWindow.isOutsideTouchable()){
                    *//当setOutsideTouchable设置为false时,拦截点击事件*
                    View mView = mPopupWindow.getContentView();
                    if (mView!= null){
                        mView.dispatchTouchEvent(motionEvent);
                    }
                }
                return mPopupWindow.isFocusable() && !mPopupWindow.isOutsideTouchable();
            }
        });
mPopupWindow.getContentView().setFocusableInTouchMode(true);
mPopupWindow.getContentView().setFocusableInTouchMode(true);
mPopupWindow.getContentView().setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View view, int i, KeyEvent keyEvent) {
                Log.e(Const.TAG, "onKey: "+i );
                return false;
            }
        });
 *//键盘输入的方式*
 *//软键盘 SOFT_INPUT_ADJUST_RESIZE 软键盘弹出后PopupWindow会自动调整左边,不被遮挡,INPUT_METHOD_NEEDED 允许输入*
mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
View rootView = LayoutInflater.from(mContext).inflate(R.layout.main,null);
mPopupWindow.showAtLocation(rootView,Gravity.CENTER,0,0);
        mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                Log.e(Const.TAG, "onDismiss: " );
                *//点击pw以外地方的监听,包括虚拟按键back键*
                dismiss();
            }
        });

参数分析

pw的样式多种多样,不同场景的布局也有差异即参数设置不尽相同,根据具体需求进行自定义

1.alpha pw背景遮盖层的设置,模糊程度

private void backgroundAlpha(float alpha) {
        WindowManager.LayoutParams lp = mContext.getWindow().getAttributes();
        lp.alpha = alpha;
        mContext.getWindow().setAttributes(lp);
 }

alpha 取值范围 0 ~ 1 f;值越小模糊程度越高,1f为还原状态;所以当设置了alpha后,dismiss pw后应当还原

2.setAnimationStyle(int animationStyle) 设置动画效果,下面介绍一种比较简单的,根据需求可自行定制

styles.xml 中定义
R.style.shop_popup_window_anim

<style name="shop_popup_window_anim" parent="android:Animation">
        <item name="android:windowEnterAnimation">@anim/pop_enter_anim</item>
        <item name="android:windowExitAnimation">@anim/pop_exit_anim</item>
 </style>

res目录下新建资源文件夹 anim
动画布局xml文件
pop_enter_anim

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="200"
        android:fromYDelta="100%p"
        android:toYDelta="0" />
    <alpha
        android:duration="200"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

pop_exit_anim

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="200"
        android:fromYDelta="0"
        android:toYDelta="50%p" />
    <alpha
        android:duration="200"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
</set>

3. setFocusable(boolean focusable) 设置pw焦点

focusable=true时,可以获取焦点,而且点击pw外区域或者back虚拟按键都可dismiss pw
focusable=false 时,无法获取焦点,同理无论pw以外区域还是点击虚拟back键都无法dismiss pw
比如当pw 布局中存在EditText,参数设为false时,输入框无法获取焦点

问题:如果想要pw能获取到焦点,同时点击pw外区域又不想dismiss pw,可参看下面方法

4.setOutsideTouchable(boolean touchable)

目前setOutsideTouchable 不管是true还是false 设置后,点击pw外的区域和点击back键都可以dismiss pw,所以如果想要实现点击pw以外区域无法dismiss pw,则需要将目前setOutsideTouchable设置为false的同时也要将 setFocusable 设置为false,就比较尴尬;
不过可以通过重写setOnTouchListener()监听来拦截点击事件的方法来让点击失效

mPopupWindow.setTouchInterceptor(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (!mPopupWindow.isOutsideTouchable()){
                    *//当setOutsideTouchable设置为false时,拦截点击事件*
                    View mView = mPopupWindow.getContentView();
                    if (mView!= null){
                        mView.dispatchTouchEvent(motionEvent);
                    }
                }
                return mPopupWindow.isFocusable() && !mPopupWindow.isOutsideTouchable();
            }
        });

这样就可以解决第三点中的问题了

4. setOnKeyListener(View.OnKeyListener l) 监听物理按键回调

如果仅仅只设置这个方法,是不能监听物理按键的,也就是说pw弹出时不会触发
解决办法:需要在popupWindow的主View上设置一个参数

mPopupWindow.getContentView().setFocusableInTouchMode(true);

这样pw弹出时就可以监听物理按键了

mPopupWindow.getContentView().setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View view, int i, KeyEvent keyEvent) {
                Log.e(Const.TAG, "onKey: "+i );
                return false;
            }
        });

目前还存在一个虚拟按键的监听问题,比如虚拟按键Back键,暂时没办法进行监听,尝试了很多方法都没有成功;
在setFocusable 设置为true时,点击虚拟back键时,pw是会被dismiss的,后续如果找到办法将会补充

5. 键盘输入模式

mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

PopupWindow.INPUT_METHOD_NEEDED:允许输入
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE:软键盘弹出后pw会自动调整位置,不被遮挡
其他模式请查看官方文档

6、设置popupWindow位置并显示

方法一:showAsDropDown(View anchor, int xoff, int yoff, int gravity) 附着某个控件(anchor)

mPopupWindow.showAsDropDown(findViewById(R.id.***),-100,100,0);

该方法通过某个具体控件(anchor)来确定位置,以控件anchor为原点,xoff和yoff进行偏移,不管偏移量是多少,pw不会跑出屏幕
anchor: 控件实例化后的对象,且该控件必须显示在屏幕上,不能消失
gravity: 经测试没啥作用
xoff: 向X轴方向的偏移量
yoff: 向Y轴方向的偏移量

方法二:showAtLocation(View parent, int gravity, int xoff, int yoff) 设置屏幕坐标位置

mPopupWindow.showAtLocation(/*rootView*/parent,Gravity.CENTER,0,0);

该方法可设置pw显示在屏幕的具体位置,同理可通过xoff和yoff进行偏移,不管偏移量是多少,pw不会跑出屏幕;

parent: 这个View只要能获取的屏幕的标识即可,控件是什么都没有影响,可以使用方法一中的控件anchor,也可以自定义一个控件 rootView,只要将该rooView加入到屏幕中即可,对PW的位置没有影响

View rootView = LayoutInflater.from(mContext).inflate(R.layout.main,null);

其中布局 R.layout.main 没有要求,随便搞个xml布局即可
另外如果pw的布局宽或者高的大小撑满了整个屏幕,参数设置可能不起作用,因为pw不会跑出屏幕
gravity: 就是于 LinearLayout 中的gravity属性,可设置pw的大概方位,如Top,left,right,bottom等
xoff: 向X轴方向的偏移量
yoff: 向Y轴方向的偏移量

7. setOnDismissListener(PopupWindow.OnDismissListener onDismissListener) 方法

mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                Log.e(Const.TAG, "onDismiss: " );
                //点击弹出框以外地方的监听,包括虚拟按键back键
                dismiss();
            }
        });

该方法在pw dismiss消失时触发,所以一些清除操作可放在这进行,比如遮盖层的还原

public void dismiss() {
        if (mPopupWindow != null) {
            backgroundAlpha(1.0f);
            mPopupWindow.dismiss();
            mPopupWindow = null;
        }
    }

好了,目前关于PopupWidow的整理就这些,其他后续再补充…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值