为了账号安全,请及时绑定邮箱和手机立即绑定

Android 封装一个通用的PopupWindow

标签:
Android

先上效果图:

GIF.gif

完整代码地址已上传Github:CommonPopupWindow

PopupWindow这个类用来实现一个弹出框,可以使用任意布局的View作为其内容,这个弹出框是悬浮在当前activity之上的。

一般PopupWindow的使用:

//准备PopupWindow的布局ViewView popupView = LayoutInflater.from(this).inflate(R.layout.popup, null);//初始化一个PopupWindow,width和height都是WRAP_CONTENTPopupWindow popupWindow = new PopupWindow(
   ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);//设置PopupWindow的视图内容popupWindow.setContentView(popupView);//点击空白区域PopupWindow消失,这里必须先设置setBackgroundDrawable,否则点击无反应popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));
popupWindow.setOutsideTouchable(true);//设置PopupWindow动画popupWindow.setAnimationStyle(R.style.AnimDown);//设置是否允许PopupWindow的范围超过屏幕范围popupWindow.setClippingEnabled(true);//设置PopupWindow消失监听popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {    @Override
    public void onDismiss() {

    }
 });//PopupWindow在targetView下方弹出popupWindow.showAsDropDown(targetView);

上面就是PopupWindow通常需要设置的各个方法,不难,但是稍微有点繁琐,有些是可以复用的,所以封装了一个通用的CommonPopupWindow:

CommonPopupWindow继承自PopupWindow,拥有PopupWindow的各个属性方法,使用类似建造者模式,和AlertDialog的使用方式差不多,CommonPopupWindow使用举例:

CommonPopupWindow popupWindow = new CommonPopupWindow.Builder(this)         //设置PopupWindow布局
        .setView(R.layout.popup_down) 
         //设置宽高
        .setWidthAndHeight(ViewGroup.LayoutParams.MATCH_PARENT, 
                           ViewGroup.LayoutParams.WRAP_CONTENT)         //设置动画
        .setAnimationStyle(R.style.AnimDown)         //设置背景颜色,取值范围0.0f-1.0f 值越小越暗 1.0f为透明
        .setBackGroundLevel(0.5f)         //设置PopupWindow里的子View及点击事件 
        .setViewOnclickListener(new CommonPopupWindow.ViewInterface() {            @Override
            public void getChildView(View view, int layoutResId) {
                TextView tv_child = (TextView) view.findViewById(R.id.tv_child);
                tv_child.setText("我是子View");
            }
        })         //设置外部是否可点击 默认是true
        .setOutsideTouchable(true)         //开始构建
        .create();//弹出PopupWindowpopupWindow.showAsDropDown(view);
  • CommonPopupWindow 设置背景:

这里使用的是WindowManager.LayoutParams.alpha属性,看下官网解释:An alpha value to apply to this entire window. An alpha of 1.0 means fully opaque and 0.0 means fully transparent .alpha 值适用于整个Window,α为1.0时表示完全不透明而0.0表示完全透明,默认是1.0,当PopupWindow弹出时通过设置alpha在(0.0,1.0)之间设置灰色背景,当PopupWindow消失时恢复默认值。

private void setBackGroundLevel(float level) {
    mWindow = ((Activity) context).getWindow();
    WindowManager.LayoutParams params = mWindow.getAttributes();
    params.alpha = level;
    mWindow.setAttributes(params);
    }
  • 计算CommonPopupWindow 宽高:

//设置测量模式为UNSPECIFIED可以确保测量不受父View的影响int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(w, h);//得到测量宽度int mWidth=view.getMeasuredWidth();//得到测量高度int mHeight=view.getMeasuredHeight();

注:在测量宽高时遇到一种情况,如图所示:
uncertain.png

如果设置TextView 的 android:layout_width="wrap_content",那么测量不出TextView 准确的height,当设置width为某个确定值时,也能得到准确的height了。

  • CommonPopupWindow 设置动画:

如设置向右动画:

.setAnimationStyle(R.style.AnimHorizontal);

在style.xml文件中设置:

<style name="AnimHorizontal" parent="@android:style/Animation">
     <item name="android:windowEnterAnimation">@anim/push_scale_left_in</item>
     <item name="android:windowExitAnimation">@anim/push_scale_left_out</item>
 </style>

android:windowEnterAnimation、android:windowExitAnimation分别为Popupwindow弹出和消失动画

进入动画为anim目录下的 push_scale_left_in.xml:

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromXScale="0.0"
    android:fromYScale="1.0"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:toXScale="1.0"
    android:toYScale="1.0" />

消失动画为 push_scale_left_out.xml:

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:toXScale="0.0"
    android:toYScale="1.0" />
  • CommonPopupWindow 弹出:

因为CommonPopupWindow继承自PopupWindow,所以可以直接使用PopupWindow中的弹出方法,常用的下面三种:

public void showAsDropDown(View anchor)public void showAsDropDown(View anchor, int xoff, int yoff)public void showAtLocation(View parent, int gravity, int x, int y)

其中,showAsDropDown是显示在参照物anchor的周围,xoff、yoff分别是X轴、Y轴的偏移量,如果不设置xoff、yoff,默认是显示在anchor的下方;showAtLocation是设置在父控件的位置,如设置Gravity.BOTTOM表示在父控件底部弹出,xoff、yoff也是X轴、Y轴的偏移量。

如上面向右弹出例子,分别使用showAsDropDown和showAtLocation来实现:
right.png

showAsDropDown:

popupWindow.showAsDropDown(view, view.getWidth(), -view.getHeight());

showAsDropDown默认展示在button的下面,通过改变X轴和Y轴的偏移量(X轴向右偏移button的宽度,Y轴向上偏移button的高度),实现在Button右边弹出。

showAtLocation:

int[] positions = new int[2];
view.getLocationOnScreen(positions);
popupWindow.showAtLocation(findViewById(android.R.id.content), 
         Gravity.START| Gravity.TOP , positions[0] + view.getWidth(), positions[1]);

使用了View的getLocationOnScreen方法来获得View在屏幕中的坐标位置,传入的参数必须是一个有2个整数的数组,分别代表View的X、Y坐标,即是View的左上角的坐标,这里的View是Button,知道了Button左上角的坐标,就可以得到要展示的PopupWindow的左上角的坐标为(positions[0] + view.getWidth(), positions[1]),从而实现在Button右边弹出。

最后贴一下代码 CommonPopupWindow.java:

public class CommonPopupWindow extends PopupWindow { final PopupController controller; @Override
 public int getWidth() {     return controller.mPopupView.getMeasuredWidth();
 } @Override
 public int getHeight() {     return controller.mPopupView.getMeasuredHeight();
 } public interface ViewInterface {     void getChildView(View view, int layoutResId);
 } private CommonPopupWindow(Context context) {
     controller = new PopupController(context, this);
 } @Override
 public void dismiss() {     super.dismiss();
     controller.setBackGroundLevel(1.0f);
 } public static class Builder {     private final PopupController.PopupParams params;     private ViewInterface listener;     public Builder(Context context) {
         params = new PopupController.PopupParams(context);
     }     /**
      * @param layoutResId 设置PopupWindow 布局ID
      * @return Builder
      */
     public Builder setView(int layoutResId) {
         params.mView = null;
         params.layoutResId = layoutResId;         return this;
     }     /**
      * @param view 设置PopupWindow布局
      * @return Builder
      */
     public Builder setView(View view) {
         params.mView = view;
         params.layoutResId = 0;         return this;
     }     /**
      * 设置子View
      *
      * @param listener ViewInterface
      * @return Builder
      */
     public Builder setViewOnclickListener(ViewInterface listener) {         this.listener = listener;         return this;
     }     /**
      * 设置宽度和高度 如果不设置 默认是wrap_content
      *
      * @param width 宽
      * @return Builder
      */
     public Builder setWidthAndHeight(int width, int height) {
         params.mWidth = width;
         params.mHeight = height;         return this;
     }     /**
      * 设置背景灰色程度
      *
      * @param level 0.0f-1.0f
      * @return Builder
      */
     public Builder setBackGroundLevel(float level) {
         params.isShowBg = true;
         params.bg_level = level;         return this;
     }     /**
      * 是否可点击Outside消失
      *
      * @param touchable 是否可点击
      * @return Builder
      */
     public Builder setOutsideTouchable(boolean touchable) {
         params.isTouchable = touchable;         return this;
     }     /**
      * 设置动画
      *
      * @return Builder
      */
     public Builder setAnimationStyle(int animationStyle) {
         params.isShowAnim = true;
         params.animationStyle = animationStyle;         return this;
     }     public CommonPopupWindow create() {         final CommonPopupWindow popupWindow = new CommonPopupWindow(params.mContext);
         params.apply(popupWindow.controller);         if (listener != null && params.layoutResId != 0) {
            listener.getChildView(popupWindow.controller.mPopupView, params.layoutResId);
         }
         CommonUtil.measureWidthAndHeight(popupWindow.controller.mPopupView);         return popupWindow;
        }
    }
}

PopupController.java:

class PopupController {    private int layoutResId;//布局id
    private Context context;    private PopupWindow popupWindow;
    View mPopupView;//弹窗布局View
    private View mView;    private Window mWindow;

    PopupController(Context context, PopupWindow popupWindow) {        this.context = context;        this.popupWindow = popupWindow;
    }    public void setView(int layoutResId) {
        mView = null;        this.layoutResId = layoutResId;
        installContent();
    }    public void setView(View view) {
        mView = view;        this.layoutResId = 0;
        installContent();
    }    private void installContent() {        if (layoutResId != 0) {
            mPopupView = LayoutInflater.from(context).inflate(layoutResId, null);
        } else if (mView != null) {
            mPopupView = mView;
        }
        popupWindow.setContentView(mPopupView);
    }    /**
     * 设置宽度
     *
     * @param width  宽
     * @param height 高
     */
    private void setWidthAndHeight(int width, int height) {        if (width == 0 || height == 0) {            //如果没设置宽高,默认是WRAP_CONTENT
            popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
            popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        } else {
            popupWindow.setWidth(width);
            popupWindow.setHeight(height);
        }
    }    /**
     * 设置背景灰色程度
     *
     * @param level 0.0f-1.0f
     */
    void setBackGroundLevel(float level) {
        mWindow = ((Activity) context).getWindow();
        WindowManager.LayoutParams params = mWindow.getAttributes();
        params.alpha = level;
        mWindow.setAttributes(params);
    }    /**
     * 设置动画
     */
    private void setAnimationStyle(int animationStyle) {
        popupWindow.setAnimationStyle(animationStyle);
    }    /**
     * 设置Outside是否可点击
     *
     * @param touchable 是否可点击
     */
    private void setOutsideTouchable(boolean touchable) {
        popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));//设置透明背景
        popupWindow.setOutsideTouchable(touchable);//设置outside可点击
        popupWindow.setFocusable(touchable);
    }    static class PopupParams {        public int layoutResId;//布局id
        public Context mContext;        public int mWidth, mHeight;//弹窗的宽和高
        public boolean isShowBg, isShowAnim;        public float bg_level;//屏幕背景灰色程度
        public int animationStyle;//动画Id
        public View mView;        public boolean isTouchable = true;        public PopupParams(Context mContext) {            this.mContext = mContext;
        }        public void apply(PopupController controller) {            if (mView != null) {
                controller.setView(mView);
            } else if (layoutResId != 0) {
                controller.setView(layoutResId);
            } else {                throw new IllegalArgumentException("PopupView's contentView is null");
            }
            controller.setWidthAndHeight(mWidth, mHeight);
            controller.setOutsideTouchable(isTouchable);//设置outside可点击
            if (isShowBg) {                //设置背景
                controller.setBackGroundLevel(bg_level);
            }            if (isShowAnim) {
                controller.setAnimationStyle(animationStyle);
            }
        }
    }
}

原文链接:http://www.apkbus.com/blog-720227-68102.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消