Android 封装一个通用的PopupWindow
*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發布
先上效果圖:
完整代碼地址已上傳Github:CommonPopupWindow
PopupWindow這個類用來實現一個彈出框,可以使用任意布局的View作為其內容,這個彈出框是懸浮在當前activity之上的。
一般PopupWindow的使用:
//準備PopupWindow的布局View View popupView = LayoutInflater.from(this).inflate(R.layout.popup, null); //初始化一個PopupWindow,width和height都是WRAP_CONTENT PopupWindow 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() {@Overridepublic 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() {@Overridepublic void getChildView(View view, int layoutResId) {TextView tv_child = (TextView) view.findViewById(R.id.tv_child);tv_child.setText("我是子View");}})//設置外部是否可點擊 默認是true.setOutsideTouchable(true)//開始構建.create(); //彈出PopupWindow popupWindow.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 寬高:
注:在測量寬高時遇到一種情況,如圖所示:
如果設置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來實現:
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;@Overridepublic int getWidth() {return controller.mPopupView.getMeasuredWidth();}@Overridepublic int getHeight() {return controller.mPopupView.getMeasuredHeight();}public interface ViewInterface {void getChildView(View view, int layoutResId);}private CommonPopupWindow(Context context) {controller = new PopupController(context, this);}@Overridepublic 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;//布局idprivate Context context;private PopupWindow popupWindow;View mPopupView;//彈窗布局Viewprivate 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_CONTENTpopupWindow.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;//布局idpublic Context mContext;public int mWidth, mHeight;//彈窗的寬和高public boolean isShowBg, isShowAnim;public float bg_level;//屏幕背景灰色程度public int animationStyle;//動畫Idpublic 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);}}} } 復制代碼轉載于:https://juejin.im/post/5a33e7b85188256584545746
總結
以上是生活随笔為你收集整理的Android 封装一个通用的PopupWindow的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu安装amd/ati显卡驱动
- 下一篇: 为确保Bard能给出高质量答案 谷歌员工