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;
}
}
}