PopupWindow使用

一.PopWindow构成3要素
1.contentView  window的内容
2.width     window的宽度
3.height    window的高度
     
     其构造方法:
     public PopupWindow(View contentView, int width, int height, boolean focusable);
    
     其实质上是使用WindowManager,在整个窗口之上添加了一个浮动的View。且看其代码:
     if(contentView != null) {
          mContext = contentView.getContext();
          mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
     }

二.show()的过程
     
      public  void  showAsDropDown(View anchor,  int  xoff,  int  yoff) {
         if  (isShowing() ||  mContentView  ==  null ) {
           
  return ;
        }
        registerForScrollChanged(anchor, xoff, yoff);
       
  mIsShowing  =  true ;
       
  mIsDropdown  =  true ;
        WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken());
        preparePopup(p);
        updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff));
       
  if  ( mHeightMode  < 0) p. height  =  mLastHeight  =  mHeightMode ;
       
  if  ( mWidthMode  < 0) p. width  =  mLastWidth  =  mWidthMode ;
        p.
windowAnimations  = computeAnimationResource();
        invokePopup(p);
    }

第一步:通过 createPopupLayout(),创建一个WindowManager.LayoutParams,因为WindowManager在addView()时,需要指定LayoutParams参数,以此来定义窗口的大小。
     该方法里只是纯粹的new WindowManager.LayoutParams(),并设置了相关参数。注意这里有个方法 computeFlags() ,会根据设置的相关属性,如: mFocusable, mTouchable, mOutsideTouchable,  mNotTouchModal 等计算出相关flag,WindowManager的flag属性,会决定是否拦截touch event、是否拦截key event,以及touch event, key event的分发等等

第二步: preparePopup(),主要是创建mPopupView(View mPopupView),mPopupView是最终PopupWindow里要显示的内容。
      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 ;
        }
     
     在这里可以看到是否设置 mBackground, 对创建 mPopupView有很大影响:如果没有设置过 mBackground ,则mPopupView直接为mContentView;如果设置过 mBackground ,则会先创建一个PopupViewContainer,其实质上是一个FrameLayout,然后将mContentView填充进去。
      PopupViewContainer的作用是什么呢?
     其重写了几个方法:
      public  boolean  dispatchKeyEvent(KeyEvent event);
      public  boolean  dispatchTouchEvent(MotionEvent ev);
      public  boolean  onTouchEvent(MotionEvent event);
     可以见到, PopupViewContainer会先处理key event、touch event等等,并默认实现了在点击PopupWindow以外的区域,dismiss window的功能。所以要实现点击空白区域,将PopupWindow给dismiss()掉的效果,必须得设置 mBackground。 如果不设置的话,默认是不会有这些效果的,必须得对mContentView进行重写以上几个方法,才能实现类似的效果。

第三步: updateAboveAnchor(),计算PopupWindow是否要在anchorView之上还是之下显示。

第四步: invokePopup(),使用WindowManager将mPopupView添加到Window当中。
      mWindowManager .addView( mPopupView , p);

三.常用方法
     public  void  setBackgroundDrawable(Drawable background);
     设置window的背景,如果要在点击空白区域能dismiss()的话,必须得设置

      public  void  setTouchModal( boolean  touchModal)
     设置为true的话,touch event可以穿透该PopupWindow,传递到后面的window,这是个隐藏方法

      public  void  setAnimationStyle( int  animationStyle)
     设置窗口进出的动画

      public  void  setOutsideTouchable( boolean  touchable) 

      public  void  setFocusable( boolean  focusable) 


三.实例
1.不设置任何属性
     View view = getLayoutInflater().inflate(R.layout. pop ,  null );
     PopupWindow pw =   new   PopupWindow(view, 200, ViewGroup.LayoutParams. WRAP_CONTENT );
     pw.showAsDropDown(v);
     效果:PopupWindow不会获得焦点,里面的任何控件均得不到响应。区域外面的响应正常。

2.设置focusable属性
     View view = getLayoutInflater().inflate(R.layout. pop null );
     PopupWindow pw =  new  PopupWindow(view, 200, ViewGroup.LayoutParams. WRAP_CONTENT );
     pw.setFocusable(true);
     pw.showAsDropDown(v);
     效果:PopupWindow获得焦点,里面的控件能够获得响应,拦截掉所有touch events, key events等。其区域外面的控件均不会得到响应,返回键也无响应。
     
3.设置background
          View view = getLayoutInflater().inflate(R.layout. pop null );
     PopupWindow pw =  new  PopupWindow(view, 200, ViewGroup.LayoutParams. WRAP_CONTENT );
     pw.setFocusable(true);
     pw.setBackgroundDrawable(new BitmapDrawable());
     pw.showAsDropDown(v);
     效果:PopupWindow获得焦点,里面的控件能够获得响应。按返回键或者是PopupWindow外面的区域,会dismiss掉PopupWindow。
     
4.设置outsideTouchable属性
          View view = getLayoutInflater().inflate(R.layout. pop null );
     PopupWindow pw =  new  PopupWindow(view, 200, ViewGroup.LayoutParams. WRAP_CONTENT );
     pw.setFocusable(true);
      pw.setOutsideTouchable( true );
     pw.setBackgroundDrawable(new BitmapDrawable());
     pw.showAsDropDown(v);
     效果:与3的效果一样

5.如何既能让PopupWindow里的控件获得响应,又能让window外的EditText能够响应输入法而能够输入文本呢?
     pw.setFocusable(false);     //这样window后面的控件,才能获得焦点
     pw.setOutsideTouchable( true );
    重写PopupWindow的mContentView,使其一直获得焦点
     boolean flag = true;

     public   boolean   hasFocus() {
          return   super .hasFocus() ||   flag ;
     }

     public   boolean   isFocused() {
          return super.isFocused() || flag;

     }

     public   boolean   hasWindowFocus() {
          return   super .hasWindowFocus() ||   flag ;
     }
     public   boolean   isInTouchMode() {
          return   super .isInTouchMode() ||   flag ;
     }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值