我們都知道杨何,Android 中的彈窗基本有兩種糕篇,一種是AlertDialog火诸,另一種是PopupWindow躲查,AlertDialog的顯示位置是固定的它浅,PopWindow 的顯示位置是我們可以設(shè)置和調(diào)整的,因此镣煮,像項目中的一些場景如:某個功能的提示說明姐霍、點擊按鈕在按鈕上方或者下方彈出菜單、新功能彈窗引導(dǎo)等典唇。由于這些彈窗的位置不固定镊折,因此都可以用PopupWindow來做。最近項目中也用到了PopupWindow彈窗功能介衔,在寫的過程中恨胚,發(fā)現(xiàn)雖然API比較簡單,但是寫一個PopupWindow炎咖,還是有點繁瑣赃泡,很多重復(fù)代碼,決定簡單封裝一下乘盼,以后添加一個彈窗就不用那么麻煩了升熊,幾行代碼就搞定了。本篇文章是對項目中使用 PopupWindow做一個筆記和總結(jié)绸栅。
一级野、首先先看一下效果圖
效果圖:
以上示例展示了4中PopupWindow 使用場景
Github地址:CustomPopWindow
二、PopupWindow 簡單介紹
首先看一下Google 官方文檔對PopupWindow的介紹:
This class represents a popup window that can be used to display an arbitrary view. The popup window is a floating container that appears on top of the current activity.
大致意思就是:popup window 是一個出現(xiàn)在當(dāng)前Activity頂層的懸浮容器粹胯,可以用來展示任意的View蓖柔。
因此只要是個View,都可以用PopupWindow 來展示矛双。這里我就不過多介紹PopupWindow 的API渊抽,挑幾個重要并且常用的說一下:
1,構(gòu)造函數(shù),這個不用多說议忽,多個重載函數(shù)懒闷,穿不同的參數(shù)。
public PopupWindow(int width, int height)
public PopupWindow(View contentView, int width, int height)
public PopupWindow(View contentView, int width, int height, boolean focusable)
2,設(shè)置顯示的View:
public void setContentView(View contentView)
3,設(shè)置展示的寬愤估、高帮辟,構(gòu)造函數(shù)傳了寬高就不用重新設(shè)置
// 設(shè)置寬,其實構(gòu)造函數(shù)也是調(diào)用的這個方法
public void setWidth(int width)
//設(shè)置高
public void setHeight(int height)
4玩焰,設(shè)置是否獲取焦點
public void setFocusable(boolean focusable)
5,設(shè)置點擊PopupWindow 以外區(qū)域是否可以隱藏PopupWindow
public void setOutsideTouchable(boolean touchable)
注意:這里要注意一下由驹,有時侯我們希望觸摸PopupWindow 以外區(qū)域就隱藏PopupWindow,理論上我們只需要調(diào)用 setOutsideTouchable(ture)設(shè)置為ture就可以了,但是實際上只設(shè)置這個屬性是不行的昔园,必須設(shè)置背景蔓榄,也就是說要和setBackgroundDrawable(Drawable background)同時使用才有效,不然默刚,點擊PopupWindow以外區(qū)域是不能隱藏掉的甥郑。
6,隱藏PopupWindow
public void dismiss()
7,設(shè)置dissmiss 回調(diào)監(jiān)聽
public void setOnDismissListener(OnDismissListener onDismissListener)
8,顯示PopupWindow
//直接顯示在參照View 的左下方
public void showAsDropDown(View anchor)
// 顯示在參照View的左下方荤西,可以通過xoff澜搅,yOff,來調(diào)節(jié)x,y方向的偏移
public void showAsDropDown(View anchor, int xoff, int off)
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity)
//顯示在指定位置,相對于整個屏幕的window而言邪锌,通過gravity調(diào)解顯示在左勉躺、上、右觅丰、下饵溅、中. x,y調(diào)整兩個方向的偏移
public void showAtLocation(View parent, int gravity, int x, int y)
以上就是PopupWindow 重要的并且常用的API。
三妇萄、封裝通用PopupWindow,CustomPopWindow,使用鏈?zhǔn)降姆绞脚渲貌@示
由于每次寫PopupWindow都要寫很多重復(fù)代碼概说,因此簡單的封裝了一個CustomPopWindow.封裝了PopupWindow 的一些常用API,使用Builder模式嚣伐,就像寫AlertDialog 一樣,鏈?zhǔn)脚渲谩?/p>
使用方法:
1萍丐,簡便寫法:
CustomPopWindow popWindow = new CustomPopWindow.PopupWindowBuilder(this)
.setView(R.layout.pop_layout1)//顯示的布局轩端,還可以通過設(shè)置一個View
// .size(600,400) //設(shè)置顯示的大小,不設(shè)置就默認(rèn)包裹內(nèi)容
.setFocusable(true)//是否獲取焦點逝变,默認(rèn)為ture
.setOutsideTouchable(true)//是否PopupWindow 以外觸摸dissmiss
.create()//創(chuàng)建PopupWindow
.showAsDropDown(mButton1,0,10);//顯示PopupWindow
以上就是彈出一個簡單的PopupWindow基茵,是不是看起來很優(yōu)雅和簡單,還可以簡單一點:
CustomPopWindow popWindow = new CustomPopWindow.PopupWindowBuilder(this)
.setView(R.layout.pop_layout1)//顯示的布局
.create()//創(chuàng)建PopupWindow
.showAsDropDown(mButton1,0,10);//顯示PopupWindow
如果是一個簡單的只展示文案的彈窗壳影,就可以只設(shè)置一個View拱层,就可以了,很簡單吧Q邕帧8啤!
2,展示一個PopupWindow 彈窗菜單(像手機QQ烙肺,微信的頂部菜單)
View contentView = LayoutInflater.from(this).inflate(R.layout.pop_menu,null);
//處理popWindow 顯示內(nèi)容
handleLogic(contentView);
//創(chuàng)建并顯示popWindow
mCustomPopWindow= new CustomPopWindow.PopupWindowBuilder(this)
.setView(contentView)
.create()
.showAsDropDown(mButton3,0,20);
如果PopupWindow 展示的內(nèi)容需要在程序代碼中設(shè)置或者響應(yīng)點擊事件等纳猪,可以現(xiàn)獲取到這個View,然后處理一些顯示和點擊事件邏輯桃笙,再交給CustomPopWindow 創(chuàng)建顯示氏堤。比如響應(yīng)菜單點擊事件的邏輯處理:
/**
* 處理彈出顯示內(nèi)容、點擊事件等邏輯
* @param contentView
*/
private void handleLogic(View contentView){
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mCustomPopWindow!=null){
mCustomPopWindow.dissmiss();
}
String showContent = "";
switch (v.getId()){
case R.id.menu1:
showContent = "點擊 Item菜單1";
break;
case R.id.menu2:
showContent = "點擊 Item菜單2";
break;
case R.id.menu3:
showContent = "點擊 Item菜單3";
break;
case R.id.menu4:
showContent = "點擊 Item菜單4";
break;
case R.id.menu5:
showContent = "點擊 Item菜單5" ;
break;
}
Toast.makeText(MainActivity.this,showContent,Toast.LENGTH_SHORT).show();
}
};
contentView.findViewById(R.id.menu1).setOnClickListener(listener);
contentView.findViewById(R.id.menu2).setOnClickListener(listener);
contentView.findViewById(R.id.menu3).setOnClickListener(listener);
contentView.findViewById(R.id.menu4).setOnClickListener(listener);
contentView.findViewById(R.id.menu5).setOnClickListener(listener);
}
}
3,展示一個ListView,其實跟上面是一樣的搏明,這里貼一下實例代碼:
private void showPopListView(){
View contentView = LayoutInflater.from(this).inflate(R.layout.pop_list,null);
//處理popWindow 顯示內(nèi)容
handleListView(contentView);
//創(chuàng)建并顯示popWindow
mListPopWindow= new CustomPopWindow.PopupWindowBuilder(this)
.setView(contentView)
.size(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)//顯示大小
.create()
.showAsDropDown(mButton4,0,20);
}
private void handleListView(View contentView){
RecyclerView recyclerView = (RecyclerView) contentView.findViewById(R.id.recyclerView);
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
MyAdapter adapter = new MyAdapter();
adapter.setData(mockData());
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
以上就是對于 PopupWindow的封裝和使用示例鼠锈。封裝了PopupWindow 常用的API,沒有展示完全星著,還有像添加顯示和隱藏動畫setAnimationStyle(int animationStyle)购笆,隱藏回調(diào)監(jiān)聽等等。需要的朋友自己試試强饮。
最后由桌,貼出CustomPopWindow的源碼:
/**
*
* 自定義PopWindow類,封裝了PopWindow的一些常用屬性邮丰,用Builder模式支持鏈?zhǔn)秸{(diào)用
* Created by zhouwei on 16/11/28.
*/
public class CustomPopWindow {
private Context mContext;
private int mWidth;
private int mHeight;
private boolean mIsFocusable = true;
private boolean mIsOutside = true;
private int mResLayoutId = -1;
private View mContentView;
private PopupWindow mPopupWindow;
private int mAnimationStyle = -1;
private boolean mClippEnable = true;//default is true
private boolean mIgnoreCheekPress = false;
private int mInputMode = -1;
private PopupWindow.OnDismissListener mOnDismissListener;
private int mSoftInputMode = -1;
private boolean mTouchable = true;//default is ture
private View.OnTouchListener mOnTouchListener;
private CustomPopWindow(Context context){
mContext = context;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
/**
*
* @param anchor
* @param xOff
* @param yOff
* @return
*/
public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff){
if(mPopupWindow!=null){
mPopupWindow.showAsDropDown(anchor,xOff,yOff);
}
return this;
}
public CustomPopWindow showAsDropDown(View anchor){
if(mPopupWindow!=null){
mPopupWindow.showAsDropDown(anchor);
}
return this;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff, int gravity){
if(mPopupWindow!=null){
mPopupWindow.showAsDropDown(anchor,xOff,yOff,gravity);
}
return this;
}
/**
* 相對于父控件的位置(通過設(shè)置Gravity.CENTER行您,下方Gravity.BOTTOM等 ),可以設(shè)置具體位置坐標(biāo)
* @param parent
* @param gravity
* @param x the popup's x location offset
* @param y the popup's y location offset
* @return
*/
public CustomPopWindow showAtLocation(View parent, int gravity, int x, int y){
if(mPopupWindow!=null){
mPopupWindow.showAtLocation(parent,gravity,x,y);
}
return this;
}
/**
* 添加一些屬性設(shè)置
* @param popupWindow
*/
private void apply(PopupWindow popupWindow){
popupWindow.setClippingEnabled(mClippEnable);
if(mIgnoreCheekPress){
popupWindow.setIgnoreCheekPress();
}
if(mInputMode!=-1){
popupWindow.setInputMethodMode(mInputMode);
}
if(mSoftInputMode!=-1){
popupWindow.setSoftInputMode(mSoftInputMode);
}
if(mOnDismissListener!=null){
popupWindow.setOnDismissListener(mOnDismissListener);
}
if(mOnTouchListener!=null){
popupWindow.setTouchInterceptor(mOnTouchListener);
}
popupWindow.setTouchable(mTouchable);
}
private PopupWindow build(){
if(mContentView == null){
mContentView = LayoutInflater.from(mContext).inflate(mResLayoutId,null);
}
if(mWidth != 0 && mHeight!=0 ){
mPopupWindow = new PopupWindow(mContentView,mWidth,mHeight);
}else{
mPopupWindow = new PopupWindow(mContentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
if(mAnimationStyle!=-1){
mPopupWindow.setAnimationStyle(mAnimationStyle);
}
apply(mPopupWindow);//設(shè)置一些屬性
mPopupWindow.setFocusable(mIsFocusable);
mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mPopupWindow.setOutsideTouchable(mIsOutside);
if(mWidth == 0 || mHeight == 0){
mPopupWindow.getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
//如果外面沒有設(shè)置寬高的情況下剪廉,計算寬高并賦值
mWidth = mPopupWindow.getContentView().getMeasuredWidth();
mHeight = mPopupWindow.getContentView().getMeasuredHeight();
}
mPopupWindow.update();
return mPopupWindow;
}
/**
* 關(guān)閉popWindow
*/
public void dissmiss(){
if(mPopupWindow!=null){
mPopupWindow.dismiss();
}
}
public static class PopupWindowBuilder{
private CustomPopWindow mCustomPopWindow;
public PopupWindowBuilder(Context context){
mCustomPopWindow = new CustomPopWindow(context);
}
public PopupWindowBuilder size(int width,int height){
mCustomPopWindow.mWidth = width;
mCustomPopWindow.mHeight = height;
return this;
}
public PopupWindowBuilder setFocusable(boolean focusable){
mCustomPopWindow.mIsFocusable = focusable;
return this;
}
public PopupWindowBuilder setView(int resLayoutId){
mCustomPopWindow.mResLayoutId = resLayoutId;
mCustomPopWindow.mContentView = null;
return this;
}
public PopupWindowBuilder setView(View view){
mCustomPopWindow.mContentView = view;
mCustomPopWindow.mResLayoutId = -1;
return this;
}
public PopupWindowBuilder setOutsideTouchable(boolean outsideTouchable){
mCustomPopWindow.mIsOutside = outsideTouchable;
return this;
}
/**
* 設(shè)置彈窗動畫
* @param animationStyle
* @return
*/
public PopupWindowBuilder setAnimationStyle(int animationStyle){
mCustomPopWindow.mAnimationStyle = animationStyle;
return this;
}
public PopupWindowBuilder setClippingEnable(boolean enable){
mCustomPopWindow.mClippEnable =enable;
return this;
}
public PopupWindowBuilder setIgnoreCheekPress(boolean ignoreCheekPress){
mCustomPopWindow.mIgnoreCheekPress = ignoreCheekPress;
return this;
}
public PopupWindowBuilder setInputMethodMode(int mode){
mCustomPopWindow.mInputMode = mode;
return this;
}
public PopupWindowBuilder setOnDissmissListener(PopupWindow.OnDismissListener onDissmissListener){
mCustomPopWindow.mOnDismissListener = onDissmissListener;
return this;
}
public PopupWindowBuilder setSoftInputMode(int softInputMode){
mCustomPopWindow.mSoftInputMode = softInputMode;
return this;
}
public PopupWindowBuilder setTouchable(boolean touchable){
mCustomPopWindow.mTouchable = touchable;
return this;
}
public PopupWindowBuilder setTouchIntercepter(View.OnTouchListener touchIntercepter){
mCustomPopWindow.mOnTouchListener = touchIntercepter;
return this;
}
public CustomPopWindow create(){
//構(gòu)建PopWindow
mCustomPopWindow.build();
return mCustomPopWindow;
}
}
}
好了娃循,本篇文章到此結(jié)束,以上就是PopupWindow的一些介紹和一個簡單的封裝斗蒋。要源碼的同學(xué)捌斧,請看 Github地址:CustomWindow,如果覺得對你有用的話泉沾,歡迎start捞蚂。