47.Android 自定义PopupWindow技巧

47.Android 自定义PopupWindow技巧


前言

其实PopupWindow自定义过程是很简单的,唯一头疼的是:PopupWindow显示的定位问题。

定位问题尤为恶心一点:有时候要涉及到PopupWindow的宽高问题。我们都知道,在没show之前是拿不到宽高的,show的时候定位需要宽高,如此矛盾。以下,都给予回答

其次的问题也有:PopupWindow的显示、消失动画设计问题。

以下也在我自定义的PopupWindow中提供了三个简单的定位方法:

  • 显示在控件的下左位置

  • 显示在控件的下中位置

  • 显示在控件的下右位置


PopupWindow的宽高

我们可以在PopupWindow初始化的时候,强制绘制Layout,而拿到PopupWindow的宽高。

// 用于保存PopupWindow的宽度
private int width;
// 用于保存PopupWindow的高度
private int height;

public CustomPopupWindow(Activity activity) {
    super(activity);
    this.activity = activity;
    this.initPopupWindow();
}

private void initPopupWindow() {
    LayoutInflater inflater = (LayoutInflater) activity
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    this.contentView = inflater.inflate(R.layout.popupwindow_custom, null);
    this.setContentView(contentView);
    // 设置弹出窗体的宽
    this.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
    // 设置弹出窗体的高
    this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
    // 设置弹出窗体可点击
    this.setTouchable(true);
    this.setFocusable(true);
    // 设置点击是否消失
    this.setOutsideTouchable(true);
    //设置弹出窗体动画效果
    this.setAnimationStyle(R.style.PopupAnimation);
    //实例化一个ColorDrawable颜色为半透明
    ColorDrawable background = new ColorDrawable(0x4f000000);
    //设置弹出窗体的背景
    this.setBackgroundDrawable(background);
    // 绘制
    this.mandatoryDraw();
}

/**
 * 强制绘制popupWindowView,并且初始化popupWindowView的尺寸
 */
private void mandatoryDraw() {
    this.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
    /**
     * 强制刷新后拿到PopupWindow的宽高
     */
    this.width = this.contentView.getMeasuredWidth();
    this.height = this.contentView.getMeasuredHeight();
}

PopupWindow定位在下左位置

/**
 * 显示在控件的下左方
 *
 * @param parent parent
 */
public void showAtDropDownLeft(View parent) {
    if (parent.getVisibility() == View.GONE) {
        this.showAtLocation(parent, 0, 0, 0);
    } else {
        // x y
        int[] location = new int[2];
        //获取在整个屏幕内的绝对坐标
        parent.getLocationOnScreen(location);
        this.showAtLocation(parent, 0, location[0], location[1] + parent.getHeight());
    }
}

PopupWindow定位在下中位置

/**
 * 显示在控件的下中方
 *
 * @param parent parent
 */
public void showAtDropDownCenter(View parent) {
    if (parent.getVisibility() == View.GONE) {
        this.showAtLocation(parent, 0, 0, 0);
    } else {
        // x y
        int[] location = new int[2];
        //获取在整个屏幕内的绝对坐标
        parent.getLocationOnScreen(location);
        this.showAtLocation(parent, 0, location[0] / 2 + parent.getWidth() / 2 - this.width / 6, location[1] + parent.getHeight());
    }
}

PopupWindow定位在下右位置

/**
 * 显示在控件的下右方
 *
 * @param parent parent
 */
public void showAtDropDownRight(View parent) {
    if (parent.getVisibility() == View.GONE) {
        this.showAtLocation(parent, 0, 0, 0);
    } else {
        // x y
        int[] location = new int[2];
        //获取在整个屏幕内的绝对坐标
        parent.getLocationOnScreen(location);
        this.showAtLocation(parent, 0, location[0] + parent.getWidth() - this.width, location[1] + parent.getHeight());
    }
}

PopupWindow动画

设计一个从PopWindow开始的时候右上角逐步展示,然后结束的时候往右上角逐步收起的动画:

styles.xml

<style name="PopupAnimation" parent="android:Animation" mce_bogus="1">
    <item name="android:windowEnterAnimation">@anim/popwindow_in</item>
    <item name="android:windowExitAnimation">@anim/popwindow_out</item>
</style>

popwindow_in.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator">

    <!--
        时间 0.2秒
        开始的时候 x y 全是0 没有大小
        结束的时候 x y 全是1 实际大小
        pivotX 100% 表示最右边
        pivotY 0% 表示最顶边
        以上定位右上角 缩放时不变位置
    -->
    <scale
        android:duration="200"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="100%"
        android:pivotY="0%"
        android:toXScale="1.0"
        android:toYScale="1.0" />
    <!--
        时间 0.2秒
        开始全透明
        结束一点都不透明
    -->
    <alpha
        android:duration="200"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

popwindow_out.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator">
    <!--
        时间 0.2秒
        开始的时候 x y 全是1 实际大小
        结束的时候 x y 全是0 没有大小
        pivotX 100% 表示最右边
        pivotY 0% 表示最顶边
        以上定位右上角 缩放时不变位置
    -->
    <scale
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="100%"
        android:pivotY="0%"
        android:toXScale="0.0"
        android:toYScale="0.0" />

    <!--
        时间 0.2秒
        开始一点都不透明
        结束全透明
    -->
    <alpha
        android:duration="200"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
</set>

自定义PopupWindow

public class CustomPopupWindow extends android.widget.PopupWindow {

    private Activity activity;
    private View contentView;

    // 用于保存PopupWindow的宽度
    private int width;
    // 用于保存PopupWindow的高度
    private int height;

    public CustomPopupWindow(Activity activity) {
        super(activity);
        this.activity = activity;
        this.initPopupWindow();
    }

    private void initPopupWindow() {
        LayoutInflater inflater = (LayoutInflater) activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.contentView = inflater.inflate(R.layout.popupwindow_custom, null);
        this.setContentView(contentView);
        // 设置弹出窗体的宽
        this.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
        // 设置弹出窗体的高
        this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        // 设置弹出窗体可点击
        this.setTouchable(true);
        this.setFocusable(true);
        // 设置点击是否消失
        this.setOutsideTouchable(true);
        //设置弹出窗体动画效果
        this.setAnimationStyle(R.style.PopupAnimation);
        //实例化一个ColorDrawable颜色为半透明
        ColorDrawable background = new ColorDrawable(0x4f000000);
        //设置弹出窗体的背景
        this.setBackgroundDrawable(background);
        // 绘制
        this.mandatoryDraw();
    }

    /**
     * 强制绘制popupWindowView,并且初始化popupWindowView的尺寸
     */
    private void mandatoryDraw() {
        this.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        /**
         * 强制刷新后拿到PopupWindow的宽高
         */
        this.width = this.contentView.getMeasuredWidth();
        this.height = this.contentView.getMeasuredHeight();
    }

    /**
     * 显示在控件的下右方
     *
     * @param parent parent
     */
    public void showAtDropDownRight(View parent) {
        if (parent.getVisibility() == View.GONE) {
            this.showAtLocation(parent, 0, 0, 0);
        } else {
            // x y
            int[] location = new int[2];
            //获取在整个屏幕内的绝对坐标
            parent.getLocationOnScreen(location);
            this.showAtLocation(parent, 0, location[0] + parent.getWidth() - this.width, location[1] + parent.getHeight());
        }
    }

    /**
     * 显示在控件的下左方
     *
     * @param parent parent
     */
    public void showAtDropDownLeft(View parent) {
        if (parent.getVisibility() == View.GONE) {
            this.showAtLocation(parent, 0, 0, 0);
        } else {
            // x y
            int[] location = new int[2];
            //获取在整个屏幕内的绝对坐标
            parent.getLocationOnScreen(location);
            this.showAtLocation(parent, 0, location[0], location[1] + parent.getHeight());
        }
    }

    /**
     * 显示在控件的下中方
     *
     * @param parent parent
     */
    public void showAtDropDownCenter(View parent) {
        if (parent.getVisibility() == View.GONE) {
            this.showAtLocation(parent, 0, 0, 0);
        } else {
            // x y
            int[] location = new int[2];
            //获取在整个屏幕内的绝对坐标
            parent.getLocationOnScreen(location);
            this.showAtLocation(parent, 0, location[0] / 2 + parent.getWidth() / 2 - this.width / 6, location[1] + parent.getHeight());
        }
    }

    public static class PopupWindowBuilder {
        private static String activityHashCode;
        private static CustomPopupWindow popupWindow;
        public static PopupWindowBuilder ourInstance;

        public static PopupWindowBuilder getInstance(Activity activity) {
            if (ourInstance == null) ourInstance = new PopupWindowBuilder();
            String hashCode = String.valueOf(activity.hashCode());
            /**
             * 不同一个Activity
             */
            if (!hashCode.equals(String.valueOf(activityHashCode))) {
                activityHashCode = hashCode;
                popupWindow = new CustomPopupWindow(activity);
            }
            return ourInstance;
        }

        public PopupWindowBuilder setTouchable(boolean touchable) {
            popupWindow.setTouchable(touchable);
            return this;
        }

        public PopupWindowBuilder setAnimationStyle(int animationStyle) {
            popupWindow.setAnimationStyle(animationStyle);
            return this;
        }

        public PopupWindowBuilder setBackgroundDrawable(Drawable background) {
            popupWindow.setBackgroundDrawable(background);
            return this;
        }

        public CustomPopupWindow getPopupWindow() {
            popupWindow.update();
            return popupWindow;
        }

    }

}

效果图

popupwindow

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
当您需要更高级的弹窗样式和交互时,可以使用 PopupWindow 来创建自定义布局的弹窗。下面是使用 PopupWindow 创建自定义布局的步骤: 1. 创建自定义布局文件:首先,创建一个 XML 文件来定义您的自定义布局。例如,您可以创建一个名为 `custom_popup.xml` 的文件,并在其定义您希望显示的布局。 2. 实例化 PopupWindow:在您的 Activity 或 Fragment ,实例化 PopupWindow 对象。 ```java LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View customView = inflater.inflate(R.layout.custom_popup, null); PopupWindow popupWindow = new PopupWindow(customView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); ``` 3. 设置 PopupWindow 属性:根据需要,设置 PopupWindow 的属性,例如背景、动画效果、焦点等。 ```java popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); popupWindow.setFocusable(true); // 设置动画效果 popupWindow.setAnimationStyle(R.style.PopupAnimation); ``` 4. 设置布局的控件和事件:通过 `customView` 获取布局的控件,并设置相应的事件监听器。 ```java Button button = customView.findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 处理点击事件 } }); ``` 5. 显示弹窗:使用 `showAtLocation()` 或 `showAsDropDown()` 方法显示弹窗。`showAtLocation()` 方法可以显示在指定的位置,而 `showAsDropDown()` 方法则可以显示在某个视图的下方。 ```java View anchorView = findViewById(R.id.anchor_view); // 锚点视图 popupWindow.showAsDropDown(anchorView); // 或者使用 showAtLocation() 方法 ``` 这样,您就可以使用 PopupWindow 创建自定义布局的弹窗。希望对您有所帮助!如果有任何问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值