目录

效果展示

实现代码
布局文件window_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:background="@color/white"
android:orientation="vertical"
android:layout_width="@dimen/dp_120"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_menuOne"
android:text="菜单1"
android:padding="@dimen/dp_10"
android:gravity="center"
android:textSize="@dimen/sp_14"
android:textColor="@color/black"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View
android:background="@color/gray_e"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"/>
<TextView
android:id="@+id/tv_menuTwo"
android:text="菜单2"
android:padding="@dimen/dp_10"
android:gravity="center"
android:textSize="@dimen/sp_14"
android:textColor="@color/black"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View
android:background="@color/gray_e"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"/>
<TextView
android:id="@+id/tv_menuThree"
android:text="菜单3"
android:padding="@dimen/dp_10"
android:gravity="center"
android:textSize="@dimen/sp_14"
android:textColor="@color/black"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</FrameLayout>
Activity中
public class MenuActivity extends AppCompatActivity {
private TextView tvMenu;
private PopupWindow menuPopupWindow;
private RelativeLayout rlContainer;
private ValueAnimator menuAnimator;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
tvMenu = (TextView) findViewById(R.id.tv_menu);
rlContainer = (RelativeLayout) findViewById(R.id.rl_container);
tvMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showTimeFilterWindow();
}
});
}
private void showTimeFilterWindow() {
if (menuPopupWindow == null) {
View view = getLayoutInflater().inflate(R.layout.window_menu, null);
TextView tvMenuOne = (TextView) view.findViewById(R.id.tv_menuOne);
TextView tvMenuTwo = (TextView) view.findViewById(R.id.tv_menuTwo);
TextView tvMenuThree = (TextView) view.findViewById(R.id.tv_menuThree);
menuPopupWindow = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
menuPopupWindow.setOutsideTouchable(true);
}
if (menuPopupWindow.isShowing()) {
menuPopupWindow.dismiss();
} else {
if(menuAnimator == null){
menuAnimator = ValueAnimator.ofFloat(0.1f, 1);
menuAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
menuPopupWindow.getContentView().setScaleX(animatedValue);
menuPopupWindow.getContentView().setScaleY(animatedValue);
}
});
menuAnimator.setDuration(200);
}
menuAnimator.start();
int navigationBarHeightIfRoom = NavigationBarUtil.getNavigationBarHeightIfRoom(this);
menuPopupWindow.showAtLocation(rlContainer, Gravity.END | Gravity.BOTTOM, rlContainer.getWidth() - tvMenu.getLeft(),rlContainer.getHeight() - tvMenu.getTop() + navigationBarHeightIfRoom);
}
}
}
这里重点是菜单位置的计算,案例的菜单展示在按钮的左边,所以首先我们让菜单展示在按钮父容器的右下角,然后根据按钮的位置算出偏移,另外为了适配虚拟按键这里加了一个获取虚拟按键高度的工具类,参照的是这篇文章的代码:Android获取虚拟按键的高度(适配全面屏),代码如下:
public class NavigationBarUtil {
/**
* 获取虚拟按键的高度
* 1. 全面屏下
* 1.1 开启全面屏开关-返回0
* 1.2 关闭全面屏开关-执行非全面屏下处理方式
* 2. 非全面屏下
* 2.1 没有虚拟键-返回0
* 2.1 虚拟键隐藏-返回0
* 2.2 虚拟键存在且未隐藏-返回虚拟键实际高度
*/
public static int getNavigationBarHeightIfRoom(Context context) {
if(navigationGestureEnabled(context)){
return 0;
}
return getCurrentNavigationBarHeight(((Activity) context));
}
/**
* 全面屏(是否开启全面屏开关 0 关闭 1 开启)
*
* @param context
* @return
*/
public static boolean navigationGestureEnabled(Context context) {
int val = Settings.Global.getInt(context.getContentResolver(), getDeviceInfo(), 0);
return val != 0;
}
/**
* 获取设备信息(目前支持几大主流的全面屏手机,亲测华为、小米、oppo、魅族、vivo都可以)
*
* @return
*/
public static String getDeviceInfo() {
String brand = Build.BRAND;
if(TextUtils.isEmpty(brand)) return "navigationbar_is_min";
if (brand.equalsIgnoreCase("HUAWEI")) {
return "navigationbar_is_min";
} else if (brand.equalsIgnoreCase("XIAOMI")) {
return "force_fsg_nav_bar";
} else if (brand.equalsIgnoreCase("VIVO")) {
return "navigation_gesture_on";
} else if (brand.equalsIgnoreCase("OPPO")) {
return "navigation_gesture_on";
} else {
return "navigationbar_is_min";
}
}
/**
* 非全面屏下 虚拟键实际高度(隐藏后高度为0)
* @param activity
* @return
*/
public static int getCurrentNavigationBarHeight(Activity activity){
if(isNavigationBarShown(activity)){
return getNavigationBarHeight(activity);
} else{
return 0;
}
}
/**
* 非全面屏下 虚拟按键是否打开
* @param activity
* @return
*/
public static boolean isNavigationBarShown(Activity activity){
//虚拟键的view,为空或者不可见时是隐藏状态
View view = activity.findViewById(android.R.id.navigationBarBackground);
if(view == null){
return false;
}
int visible = view.getVisibility();
if(visible == View.GONE || visible == View.INVISIBLE){
return false ;
}else{
return true;
}
}
/**
* 非全面屏下 虚拟键高度(无论是否隐藏)
* @param context
* @return
*/
public static int getNavigationBarHeight(Context context){
int result = 0;
int resourceId = context.getResources().getIdentifier("navigation_bar_height","dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}
网友评论