欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

源生控件PopupWindow

程序员文章站 2022-05-31 15:39:03
...

个人笔记,持续更新

参考与学习:
https://blog.csdn.net/u012585964/article/details/52126989

 

1.理解

PopupWindow 相当于是一种view,但是它直接继承自Object,基于WindowManager实现,挂在于Window ,悬浮在当前activity之上的。


2.popupwindow简单实现

 class KeyBoardPopupWindow {

    private final String TAG = this.getClass().getName();
    private View mLocationView;
    private View mContentView;
    private Activity mContext;

    public KeyBoardPopupWindow(View mLocationView, Activity context) {
        this.mLocationView = mLocationView;
        this.mContext = context;
        initView();
    }

    private void initView() {
        LogUtils.i(TAG, "initView");
        mContentView = LayoutInflater.from(mContext).inflate(R.layout.view_key_board,         null, false);

        mPopupWindow = new PopupWindow(mContentView,     WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT,
                true);
        mPopupWindow.setTouchable(true);

        mPswText = mContentView.findViewById(R.id.ly_psw_text);

        mPopupWindow.setFocusable(true);
        mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
        mContentView.setFocusableInTouchMode(true);
        mPopupWindow.update();
    }

    public void show() {
        if (mPopupWindow != null) {
            if (!mPopupWindow.isShowing()) {
                LogUtils.i(TAG, "show");
                mPopupWindow.showAtLocation(mLocationView, Gravity.NO_GRAVITY, 0, 0);
            }
        }
    }

    public void dismiss() {
        if (mPopupWindow != null) {
            if (mPopupWindow.isShowing()) {
                LogUtils.i(TAG, "dismiss");
                mPopupWindow.dismiss();
            }
        }
    }
}    


3.方法解读

3.1构造方法解读

3.1.1   PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

参数:
三个参数直接的优先级别为:attrs > defStyleAttr > defStyleRes
AttributeSet attrs 这个参数里面存放的是对应的设置的属性值集合.例如:android:layout_width、android:layout_height、app:text、app:num.即layout设置的属性集中获取attrs中的属性;
int defStyleAttr 在Theme中进行指定,在AndroidManifest文件中会为application设置一个主题,从系统主题中获取attrs中的属性
int defStyleRes:从资源文件定义的style中读取attrs中的属性.
 

//源码

public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

        mContext = context;
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//创建mWindowManager

        //获取资源文件等属性
        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes);
        final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground);
        mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
        mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);

        // Preserve default behavior from Gingerbread. If the animation is
        // undefined or explicitly specifies the Gingerbread animation style,
        // use a sentinel value.

        if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
            final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
            if (animStyle == R.style.Animation_PopupWindow) {
                mAnimationStyle = ANIMATION_STYLE_DEFAULT;
            } else {
                mAnimationStyle = animStyle;
            }
        } else {
            mAnimationStyle = ANIMATION_STYLE_DEFAULT;
        }

        final Transition enterTransition = getTransition(a.getResourceId(
                R.styleable.PopupWindow_popupEnterTransition, 0));
        final Transition exitTransition;
        if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
            exitTransition = getTransition(a.getResourceId(
                    R.styleable.PopupWindow_popupExitTransition, 0));
        } else {
            exitTransition = enterTransition == null ? null : enterTransition.clone();
        }


        a.recycle();//缓存Style中属性
        
        setEnterTransition(enterTransition);//设置进退场动画
        setExitTransition(exitTransition);
        setBackgroundDrawable(bg);//设置背景,如果有的话
    }


    3.1.2 PopupWindow(View contentView, int width, int height, boolean focusable)
    
参数:

contentView 因为PopupWindow没有默认布局,它不会像AlertDialog那样只setTitle,就能弹出来一个框。PopupWindow是没有默认布局的,它的布局只有通过我们自己设置才行。


            width:窗体长度
            height:窗体宽度
            focusable //是否具有获取焦点的能力

//源码

public PopupWindow(View contentView, int width, int height, boolean focusable) {
        
        if (contentView != null) {
        
            mContext = contentView.getContext();

            mWindowManager = (WindowManager)
                    mContext.getSystemService(Context.WINDOW_SERVICE);
        }

        //构造器参数
        setContentView(contentView);
        setWidth(width);
        setHeight(height);
        setFocusable(focusable);
}

 

3.2 显示方法解读

 

3.2.1  void showAtLocation(View parent, int gravity, int x, int y)

    绝对布局,DecorView大小不影响popupWindow的大小(屏幕不够大除外)

    View 提供给PopupWindow,我们的popupWindow是相对的这个View调整显示位置
    gravity 相对位置
    x,y 微调位置

 

3.2.2  void showAsDropDown(View anchor, int xoff, int yoff, int gravity)

    3.2.2.1 相对布局,DecorView大小会影响opupWindow的大小,popupWindow跟着DecorView尺寸调节
    3.2.2.2 如果希望showAsDropDown方法能够在下面空间不足时自动在anchorView的上面弹出,必须在创建PopupWindow的时候指定高度,不能用wrap_content
    

3.3 void dismiss()

//源码

public void dismiss() {

        //只有当对话框正在显示且对话框视图不为空

        if (isShowing() && mPopupView != null) {

            //重置标志位
            mIsShowing = false;

            unregisterForScrollChanged();

            try {

                //从Activity上移除对话框视图
                mWindowManager.removeViewImmediate(mPopupView);
            } finally {

                if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {        
                    

                    //移除其子View
                    ((ViewGroup) mPopupView).removeView(mContentView);
                }
                mPopupView = null;

                //设置对话框移除时的监听事件
                if (mOnDismissListener != null) {
                    mOnDismissListener.onDismiss();
                }
            }
        }
    }

3.4  setFocusable(boolean focusable)

   // 决定弹窗的外部能否响应事件
    
    public void setFocusable(boolean enable) {
        mFocusable = enable;
    }

3.5  setTouchable(boolean touchable)

用于设置PopupWindow是否响应touch事件,设置为true之后,PopupWindow内容区域 才可以响应点击事件

 

3.6  setBackgroundDrawable

如果不设置背景,点击PopupWindow最外层布局以及点击返回键PopupWindow不会消失。参数drawable随便一个什么类型的都可以,只要不为空。
    
原理:mBackground不为空的时候,PopupViewContainer会作为mContentView的Parent,extends FrameLayout ,重写了boolean dispatchKeyEvent(KeyEvent event) 和 boolean onTouchEvent(MotionEvent event)方法,他们分别负责实现了返回键处理逻辑和点击消失逻辑。
    
 加深理解:https://www.cnblogs.com/popfisher/p/5608717.html
    
3.7  setOutsideTouchable(boolean touchable)

设置touchable为true时,如果点击PopupWindow以外的区域,PopupWindow是否会消失;设置生效的前提是setTouchable(true)和setFocusable(false)

6.0之前的版本Ok
6.0之后的版本不好用,//TODO

    
3.8  setOnKeyListener(View.OnKeyListener onKeyListener)

    设置当前View,响应按键

        mContentView.setOnKeyListener(mOnKeyListener);

        private View.OnKeyListener mOnKeyListener = new View.OnKeyListener() {
        @Override
        public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {

            // TODO Auto-generated method stub
            if (keyCode == KeyEvent.KEYCODE_BACK && mPopupWindow.isShowing()) {
                mPopupWindow.dismiss();
                return true;
            }
            return false;
        }
    };


3.9  setFocusableInTouchMode

focusableInTouchMode="true",当软键盘弹出了。点击本View就会先隐藏掉软键盘,但是不会执行View的点击事件中的代码。

 

3.10 void update()


更新PopupWindow状态,如果当前已是显示状态,就从当前状态更新。
 

//Source
    
public void update() {

    if (!isShowing() || mContentView == null) {
        return;
    }

    final WindowManager.LayoutParams p = (WindowManager.LayoutParams)     mDecorView.getLayoutParams();

    boolean update = false;

    final int newAnim = computeAnimationResource();
    if (newAnim != p.windowAnimations) {
        p.windowAnimations = newAnim;
        update = true;
    }

    final int newFlags = computeFlags(p.flags);
    if (newFlags != p.flags) {
        p.flags = newFlags;
        update = true;
    }

    //7.0新增
    final int newGravity = computeGravity();
    if (newGravity != p.gravity) {
        p.gravity = newGravity;
        update = true;
    }

    if (update) {
        setLayoutDirectionFromAnchor();
        mWindowManager.updateViewLayout(mDecorView, p);
    }
}

3.11 setClippingEnabled(boolean enabled);

 

 

 

相关标签: popupWindow