美文网首页
PopupWindow 笔记

PopupWindow 笔记

作者: chauI | 来源:发表于2017-03-04 15:38 被阅读130次
    • 简单的调用
    • 背景变暗
    • 一些不同版本导致的小坑

    简单调用

    // 一个自定义的布局,作为显示的内容
    View contentView = LayoutInflater.from(mContext).inflate(R.layout.pop_window, null);
    PopupWindow popupWindow = new PopupWindow(contentView,
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);
    
    popupWindow.setTouchable(true);
    popupWindow.setTouchInterceptor(new OnTouchListener() {
      @Override
        public boolean onTouch(View v, MotionEvent event) {
    
            popupWindow.dismiss();//点击自身关闭
    
            // 返回 false 表示不拦截 touch 事件,
            // 拦截后 PopupWindow 的 onTouchEvent 不被调用,这样点击外部区域无法 dismiss
            return false;
        }
    });
    popupWindow.showAsDropDown(view);//将窗口显示在 view 的下面
    

    指定弹出位置的一些方法:

    //showAtLocation()显示在指定位置,有两个方法重载:
    
    public void showAtLocation(View parent, int gravity, int x, int y)
    public void showAtLocation(IBinder token, int gravity, int x, int y)
     
    //showAsDropDown()显示在一个参照物View的周围,有三个方法重载:
    
    public void showAsDropDown(View anchor)
    public void showAsDropDown(View anchor, int xoff, int yoff)
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity)
    

    重写 PopupWindow

    public popupWindow(Context context){
        this.context = context;
        LayoutInflater inflater = LayoutInflater.from(context);
        rootView = inflater.inflate(R.layout.popup,null);
        setContentView(rootView);
    
        this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        this.setFocusable(true);//设置成可获取焦点状态
    
        //当单击Back键或者其他地方使其消失、需要设置这个属性。
        rootView.setOnTouchListener(this);
        rootView.setOnKeyListener(this);
        rootView.setFocusable(true);
        rootView.setFocusableInTouchMode(true);
    
        this.setAnimationStyle(R.style.dialog_anim);//设置出现和消失的动画
    
        //实例化一个ColorDrawable颜色为半透明
        ColorDrawable dw = new ColorDrawable(0xb0000000);
        //设置SelectPicPopupWindow弹出窗体的背景
        this.setBackgroundDrawable(dw);
        //这样设置虽然有半透明的背景,但是出现的方式不太对
    
    }
    
    
    @Override
    public void setOnDismissListener(OnDismissListener onDismissListener) {
        //在弹出框消失时
        super.setOnDismissListener(onDismissListener);
    }
    
    
    //点back键消失
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && this.isShowing()) {
             //如果点击了返回键,而且弹出框已经出现
             this.dismiss();
             return true;
        }
        return false;
    }
    
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int height = rootView.findViewById(R.id.popup_linear).getTop();
        // 这个height 是弹出框中的 LinearLayout 在屏幕的位置
        int y = (int) event.getY();
        //获取点击的 y 轴,点击的位置如果在弹出框的上面,是 y < height 
        if (event.getAction() == MotionEvent.ACTION_UP) {
            if (y < height) {
                dismiss();
            }
        }
        return true;
    }
    

    调用

    LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(getContext())
                    .inflate(R.layout.me_layout, null);
    popupWindow popupwindow = new popupWindow(getContext());
    popupwindow.showAtLocation(linearLayout, Gravity.BOTTOM,0,0);
    //这里的 linearLayout 是布局中的最外层的 LinearLayout ,所以这个 pop 出现在最下面
    

    这里的例子没有获取布局中的按钮设置 onClick 事件,
    这个是和在其他地方一样的,不表

    背景变暗

    搜到的很多资料的方法都基本一样,用的最多的就是:

    WindowManager.LayoutParams lp = getWindow().getAttributes();  
    lp.alpha = 0.7f;  
    getWindow().setAttributes(lp);  //让背景变暗
    mPopupWindow.setOnDismissListener(new OnDismissListener() {  
     @Override  
        public void onDismiss() {  
            //在关闭 PopupWindow 之后,恢复背景
            WindowManager.LayoutParams lp = getWindow().getAttributes();  
            lp.alpha = 1f;  
            getWindow().setAttributes(lp);  
        }  
    });  
    

    这个方法有局限性,还有一些是靠添加一个半透明的 View:
    动画弹出 PopupWindow 并使背景变暗
    还有一些值得参考的:
    Android - 利用动画实现背景逐渐变暗

    其实如果需要这种效果,还是用 Dialog 会比较简单和理想,
    详细见于我的另一篇 Dialog 弹出框 笔记

    6.0 版本不能 dismiss

    简而言之,6.0 版本下的 PopupWindow 需要指定背景,否则点击外部和返回键都不能 dismiss,setOutsideTouchable(true) 无效,

    popupWindow.setBackgroundDrawable(new BitmapDrawable());
    

    对比 6.0 之前的版本,发现原因是 preparePopup() 的改动导致:

    //6.0 版本下的源码
    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.setBackgroundDrawable(mBackground);
            popupViewContainer.addView(mContentView, listParams);
    
            mPopupView = popupViewContainer;
        } else {
            mPopupView = mContentView;
        }
        mPopupWidth = p.width;
        mPopupHeight = p.height;
    }
    
    //6.0 之前的版本
    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.
        p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);
    
        mPopupViewInitialLayoutDirectionInherited =
        (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
    }
    

    两者最大区别在于 6.0 以前不管有没有设置 background 都会创建一个 PopupDecorView,而这个 PopupDecorView 正是负责处理 PopupWindow 的点击事件。
    6.0 下 mBackground == null 则不会创建 PopupDecorView,导致不能响应到点击事件。

    7.0 下 PopupWindow 跑到了界面的顶部

    7.0 下 PopupWindow.update( ) 方法会导致 PopupWindow的位置出现在界面顶部:

    public void update() {
        if (!isShowing() || mContentView == null) {
            return;
        }
        ......
        final int newGravity = computeGravity();
        if (newGravity != p.gravity) {
            p.gravity = newGravity;
            update = true;
        }
        ......
    }
    

    原因在于 update() 方法中调用的 computeGravity():

    private int computeGravity() {
       int gravity = Gravity.START | Gravity.TOP;
       if (mClipToScreen || mClippingEnabled) {
           gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
       }
       return gravity;
    }
    

    相关文章

      网友评论

          本文标题:PopupWindow 笔记

          本文链接:https://www.haomeiwen.com/subject/glvrgttx.html