Android 自定义PopupWindow技巧 前言 PopupWindow的宽高 PopupWindow定位在下左位置 PopupWindow定位在下中位置 PopupWindow定位在下右位置 PopupWindow动画 自定义PopupWindow 效果图
其实PopupWindow自定义过程是很简单的,唯一头疼的是:PopupWindow显示的定位问题。
定位问题尤为恶心一点:有时候要涉及到PopupWindow的宽高问题。我们都知道,在没show之前是拿不到宽高的,show的时候定位需要宽高,如此矛盾。以下,都给予回答。
其次的问题也有: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();
}
/**
* 显示在控件的下左方
*
* @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());
}
}
/**
* 显示在控件的下右方
*
* @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());
}
}
设计一个从PopWindow开始的时候右上角逐步展示,然后结束的时候往右上角逐步收起的动画:
styles.xml
popwindow_in.xml
<code class="language-xml hljs "><set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator"> <!--{cke_protected}{C}%3C!%2D%2D%0A%20%20%20%20%20%20%20%20%E6%97%B6%E9%97%B4%200.2%E7%A7%92%0A%20%20%20%20%20%20%20%20%E5%BC%80%E5%A7%8B%E7%9A%84%E6%97%B6%E5%80%99%20x%20y%20%E5%85%A8%E6%98%AF0%20%E6%B2%A1%E6%9C%89%E5%A4%A7%E5%B0%8F%0A%20%20%20%20%20%20%20%20%E7%BB%93%E6%9D%9F%E7%9A%84%E6%97%B6%E5%80%99%20x%20y%20%E5%85%A8%E6%98%AF1%20%E5%AE%9E%E9%99%85%E5%A4%A7%E5%B0%8F%0A%20%20%20%20%20%20%20%20pivotX%20100%25%20%E8%A1%A8%E7%A4%BA%E6%9C%80%E5%8F%B3%E8%BE%B9%0A%20%20%20%20%20%20%20%20pivotY%200%25%20%E8%A1%A8%E7%A4%BA%E6%9C%80%E9%A1%B6%E8%BE%B9%0A%20%20%20%20%20%20%20%20%E4%BB%A5%E4%B8%8A%E5%AE%9A%E4%BD%8D%E5%8F%B3%E4%B8%8A%E8%A7%92%20%E7%BC%A9%E6%94%BE%E6%97%B6%E4%B8%8D%E5%8F%98%E4%BD%8D%E7%BD%AE%0A%20%20%20%20%2D%2D%3E--> <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"> <!--{cke_protected}{C}%3C!%2D%2D%0A%20%20%20%20%20%20%20%20%E6%97%B6%E9%97%B4%200.2%E7%A7%92%0A%20%20%20%20%20%20%20%20%E5%BC%80%E5%A7%8B%E5%85%A8%E9%80%8F%E6%98%8E%0A%20%20%20%20%20%20%20%20%E7%BB%93%E6%9D%9F%E4%B8%80%E7%82%B9%E9%83%BD%E4%B8%8D%E9%80%8F%E6%98%8E%0A%20%20%20%20%2D%2D%3E--> <alpha android:duration="200" android:fromalpha="0.0" android:toalpha="1.0"> </alpha></scale></set></code>
popwindow_out.xml
<code class="language-xml hljs "><set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator"> <!--{cke_protected}{C}%3C!%2D%2D%0A%20%20%20%20%20%20%20%20%E6%97%B6%E9%97%B4%200.2%E7%A7%92%0A%20%20%20%20%20%20%20%20%E5%BC%80%E5%A7%8B%E7%9A%84%E6%97%B6%E5%80%99%20x%20y%20%E5%85%A8%E6%98%AF1%20%E5%AE%9E%E9%99%85%E5%A4%A7%E5%B0%8F%0A%20%20%20%20%20%20%20%20%E7%BB%93%E6%9D%9F%E7%9A%84%E6%97%B6%E5%80%99%20x%20y%20%E5%85%A8%E6%98%AF0%20%E6%B2%A1%E6%9C%89%E5%A4%A7%E5%B0%8F%0A%20%20%20%20%20%20%20%20pivotX%20100%25%20%E8%A1%A8%E7%A4%BA%E6%9C%80%E5%8F%B3%E8%BE%B9%0A%20%20%20%20%20%20%20%20pivotY%200%25%20%E8%A1%A8%E7%A4%BA%E6%9C%80%E9%A1%B6%E8%BE%B9%0A%20%20%20%20%20%20%20%20%E4%BB%A5%E4%B8%8A%E5%AE%9A%E4%BD%8D%E5%8F%B3%E4%B8%8A%E8%A7%92%20%E7%BC%A9%E6%94%BE%E6%97%B6%E4%B8%8D%E5%8F%98%E4%BD%8D%E7%BD%AE%0A%20%20%20%20%2D%2D%3E--> <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"> <!--{cke_protected}{C}%3C!%2D%2D%0A%20%20%20%20%20%20%20%20%E6%97%B6%E9%97%B4%200.2%E7%A7%92%0A%20%20%20%20%20%20%20%20%E5%BC%80%E5%A7%8B%E4%B8%80%E7%82%B9%E9%83%BD%E4%B8%8D%E9%80%8F%E6%98%8E%0A%20%20%20%20%20%20%20%20%E7%BB%93%E6%9D%9F%E5%85%A8%E9%80%8F%E6%98%8E%0A%20%20%20%20%2D%2D%3E--> <alpha android:duration="200" android:fromalpha="1.0" android:toalpha="0.0"> </alpha></scale></set></code>
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;
}
}
}