1、佈局文件
lay_menupopwin
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@color/dot_trans_white"
android:orientation="vertical">
<TextView
android:id="@+id/text2"
android:layout_width="match_parent"
android:layout_height="45dp"
android:text="@string/t_menu_logout"
android:textColor="@color/red"
android:gravity="center"
android:textSize="@dimen/textsize_h4"/>
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="@color/transparent"/>
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="45dp"
android:text="@string/t_menu_cancel"
android:textColor="@color/black"
android:gravity="center"
android:textSize="@dimen/textsize_h4"/>
</LinearLayout>
2、anim
bottom_enter.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromYDelta="100%p"
android:toYDelta="0"
android:duration="200" />
<alpha
android:fromAlpha="0.7"
android:toAlpha="1.0"
android:duration="200" />
</set>
bottom_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromYDelta="0"
android:toYDelta="100%p"
android:duration="200" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.5"
android:duration="200" />
</set>
3、樣式style.xml
<!-- popupwin泡泡窗口,控制面板 -->
<style name="popWinAnimBot" parent="android:Animation">
<item name="@android:windowEnterAnimation">@anim/bottom_enter</item>
<item name="@android:windowExitAnimation">@anim/bottom_out</item>
</style>
4、java
/**
* 彈出菜單
* Created by xl on 2019/5/15.
*/
public class MenuPopupWin extends PopupWindow {
private Context m_context;
private TextView m_tvMenu1;
private TextView m_tvMenu2;
private View m_view;
private Window m_window;
public MenuPopupWin(Context context, final OnMenuItemClickListener listener) {
super(context);
this.m_context = context;
// 初始化view
m_view = LayoutInflater.from(context).inflate(R.layout.lay_menupopwin, null);
m_tvMenu1 = m_view.findViewById(R.id.text1);
m_tvMenu2 = m_view.findViewById(R.id.text2);
m_tvMenu1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
}
});
m_tvMenu2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.onItemClick(view, 1);
}
dismiss();
}
});
// 設置自定義PopupWindow的View
this.setContentView(m_view);
// 設置自定義PopupWindow彈出窗體的寬
this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
// 設置自定義PopupWindow彈出窗體的高
this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
// 設置自定義PopupWindow彈出窗體可點擊
this.setFocusable(true);
// 設置自定義PopupWindow彈出窗體動畫效果
this.setAnimationStyle(R.style.popWinAnimBot);
// 實例化一個ColorDrawable顏色爲半透明
ColorDrawable dw = new ColorDrawable(0xb0000000);
// 設置自定義PopupWindow彈出窗體的背景
this.setBackgroundDrawable(dw);
// popupwindow消失,恢復透明度
this.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
WindowManager.LayoutParams lp = m_window.getAttributes();
lp.alpha = 1f;
m_window.setAttributes(lp);
}
});
// mMenuView添加OnTouchListener監聽判斷獲取觸屏位置如果在選擇框外面則銷燬彈出框
m_view.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int height = m_view.findViewById(R.id.lay_spinnerdlg).getTop();
int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_UP) {
if (y < height) {
dismiss();
}
}
// setOutsideTouchable(true)則點擊PopupWindow之外的地方PopupWindow會消失
return true;
}
});
}
/**
* 顯示dlg,window用來設置背景變暗
*
* @param parent
*/
public void show(View parent, Window window) {
showAtLocation(parent, Gravity.BOTTOM, 0, 0);
m_window = window;
// 設置背景變暗
WindowManager.LayoutParams lp = window.getAttributes();
lp.alpha = 0.5f;
window.setAttributes(lp);
}
/**
* 定義接口
*/
public interface OnMenuItemClickListener {
void onItemClick(View view, int position);
}
}
5、activity調用
showMenuPopup(view);
/**
* 彈出menu菜單
*/
private void showMenuPopup(View view){
MenuPopupWin menu = new MenuPopupWin(this, new MenuPopupWin.OnMenuItemClickListener() {
@Override
public void onItemClick(View view, int position) {
}
});
menu.show(view, getWindow());
}
補充:
showAtLocation與showAsDropDown問題
showAsDropDown(View anchor, int xoff, int yoff)方法
這個方法沒有什麼好說的,在錨點anchor的正下方彈出popup,參數xoff和yoff分別是x軸和y軸的偏移量,偏移量是相對錨點anchor來說的,以anchor的左下角爲參考點;
但是,需要注意的是,Android 7.0版本之前,在指定位置彈出popupwindow用showAsDropDown(View anchor, int xoff, int yoff)毫無問題,但在android 7.0上,用showAsDropDown()就需要注意下面兩點了:
-
如果指定 PopupWindow 的高度爲 MATCH_PARENT,調用 showAsDropDown(View anchor) 時,在 7.0 之前,會在錨點 anchor 下邊緣到屏幕底部之間顯示 PopupWindow;而在 7.0、7.1 系統上的 PopupWindow 會佔據整個屏幕(除狀態欄之外)。
-
如果指定 PopupWindow 的高度爲自定義的值height,調用 showAsDropDown(View anchor)時, 如果 height > 錨點 anchor 下邊緣與屏幕底部的距離, 則還是會出現7.0、7.1上顯示異常的問題;否則,不會出現該問題。
所以在無法避免上述的兩個問題時,這時候就需要用showAtLocation()來處理可能出現的popup顯示異常問題。
showAtLocation(View parent, int gravity, int x, int y)
對showAtLocation()方法的很多解釋都是:“相對於父控件的位置,x和y爲偏移量。”這種解釋絕對是錯的離譜,而且很可笑,如果是這樣,那它和showAsDropDown()方法還有什麼異同,瞪大眼睛看看這個方法的參數,x不再是xoff,y也不再是yoff,從字面上來看也不是什麼所謂的偏移量吧,首先,這個方法是對於整個window的屏幕以座標來定位置的,不存在相對於某一個view,有相對也是相對整個屏幕,參數x和y是座標。
該方法的第一個參數是parent,類型是個View,這個參數名很讓人誤解,其實,並不是把PopupWindow放到這個parent裏,並不要求這個parent是一個ViewGroup。官方文檔對這個參數的解釋是“a parent view to get the token from”,這個parent的作用應該是調用其getWindowToken()方法獲取窗口的Token,所以,只要是該窗口上的控件就可以了。我個人開發中,一般是拿該parent在屏幕中的座標拿來作爲參考的,通過parent.getLocationInWindow(location)
獲得parent在屏幕上的座標,其中location是一個大小爲2的int數組。
第二個參數很讓人理解爲對齊方式,當然不是,而是通過設置gravity來設置座標原點:
- Gravity.TOP | Gravity.LEFT 以屏幕左上角爲座標原點
- Gravity.BOTTOM | Gravity.RIGHT 以屏幕右下角爲座標原點
- Gravity.LEFT 以屏幕左側,屏幕高度 1/2 處爲座標原點
以此類推,可根據實際情況是設置座標原點的位置。
設置了第二個參數,後面的參數x,y也就是相對於座標原點的位置了。
完!!!