【Android】Popup menu:弹出式菜单

PopupMenu,弹出菜单,一个模态形式展示的弹出风格的菜单,绑在在某个View上,一般出现在被绑定的View的下方(如果下方有空间)。

注意:弹出菜单是在API 11和更高版本上才有效的。

核心步骤:

(1)通过PopupMenu的构造函数实例化一个PopupMenu对象,需要传递一个当前上下文对象以及绑定的View。

(2)调用PopupMenu.setOnMenuItemClickListener()设置一个PopupMenu选项的选中事件。

(3)使用MenuInflater.inflate()方法加载一个XML文件到PopupMenu.getMenu()中。

(4)在需要的时候调用PopupMenu.show()方法显示。

需求

点击右上角的"...",下拉出来"重启"和"关闭",点击"重启"或者"关闭",弹出一个弹窗确认是否需要重启或者关闭设备;再点击确定,就会重启或者关机该设备;点击取消,什么都不做,继续保持再主launcher界面。

AndroidMainfest.xml

添加重启和关机的权限

复制代码
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.SHUTDOWN" />

res/menu/main_menu.xml

创建main_menu.xml文件,menu中包含item(重启和关机);

复制代码
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_restart"
        android:title="重启"
        android:icon="@drawable/ic_restart"
        app:showAsAction="never"/>

    <item
        android:id="@+id/action_shutdown"
        android:title="关机"
        android:icon="@drawable/ic_power_off"
        app:showAsAction="never"/>
</menu>

重启图标:drawable/ic_restart.xml

复制代码
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="?attr/colorControlNormal">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
</vector>

tips:去掉android:tint="?attr/colorControlNormal"这个属性可以让图标变成白色,也就是android:fillColor="@android:color/white"这个属性可以生效。

关机图标:drawable/ic_power_off.xml

复制代码
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="?attr/colorControlNormal">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M13,3h-2v10h2V3zM16.59,6.59L18,8c3.87,3.87 3.9,10.24 0.1,14.14 -3.79,3.9 -10.23,3.87 -14.12,-0.08C-0.1,18.1 -0.07,11.66 3.82,7.77l1.41,-1.41L4.2,5.2C-0.18,9.58 -0.16,17.03 4.3,21.4c4.47,4.36 11.91,4.35 16.37,-0.02 4.46,-4.38 4.47,-11.83 0.02,-16.2L16.59,6.59z"/>
</vector>

tips:去掉android:tint="?attr/colorControlNormal"这个属性可以让图标变成白色,也就是android:fillColor="@android:color/white"这个属性可以生效。

三个点图标:drawable/ic_more_vert.xml

复制代码
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>

activity_main.xml

此文件中指出三个点图标的位置

复制代码
<LinearLayout
...
...
<ImageView
            android:id="@+id/menuButton"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginStart="10dp"
            android:src="@drawable/ic_more_vert"
            android:padding="8dp"/>
</LinearLayout>	

android:src="@drawable/ic_more_vert"是三个点的图标,将三个点的图标显示在LinearLayout中

MainActivity.java

  1. 在OnCreate方法中绑定三个点图标的监听事件

    复制代码
     protected void onCreate(Bundle savedInstanceState){
     	super.onCreate(savedInstanceState);
     	setContentView(R.layout.activity_main);
     	setupClickListeners();
     }
  2. 监听

    复制代码
    findViewById(R.id.menuButton).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               showPopupMenu(view);
           }
       });

R.id.menuButton是activity_main.xml中设置的id,setOnClickListener监听点击事件,当点击时触发showPopupMenu(view)

showPopupMenu方法

复制代码
 private void showPopupMenu(View view) {
       // View当前PopupMenu显示的相对View的位置
       PopupMenu popupMenu = new PopupMenu(this, view);
       // menu布局
       popupMenu.getMenuInflater().inflate(R.menu.main_menu, popupMenu.getMenu());
       // menu的item点击事件
       popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
           @Override
           public boolean onMenuItemClick(MenuItem item) {

               if (item.getItemId() == R.id.action_restart) {
                   showConfirmationDialog("重启设备", "您确定要重启设备吗?", item.getItemId());
               } else if (item.getItemId() == R.id.action_shutdown) {
                   showConfirmationDialog("关闭设备", "您确定要关闭设备吗?", item.getItemId());
               }

               return false;

           }
       });
//        // PopupMenu关闭事件
//        popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
//            @Override
//            public void onDismiss(PopupMenu menu) {
//                Toast.makeText(getApplicationContext(), "关闭PopupMenu", Toast.LENGTH_SHORT).show();
//            }
//        });

       popupMenu.show();
   }

当点击重启或者关机按钮时触发事件

复制代码
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {

            if (item.getItemId() == R.id.action_restart) {
                showConfirmationDialog("重启设备", "您确定要重启设备吗?", item.getItemId());
            } else if (item.getItemId() == R.id.action_shutdown) {
                showConfirmationDialog("关闭设备", "您确定要关闭设备吗?", item.getItemId());
            }

            return false;

        }
    });

showConfirmationDialog

复制代码
private void showConfirmationDialog(String title, String message, int itemId) {
    new AlertDialog.Builder(this)
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton("确定", (dialog, which) -> {
                dialog.cancel();
                if (R.id.action_restart == itemId) {
                    restartDevice();
                } else {
                    shutdownDevice();
                }
            })
            .setNegativeButton("取消", (dialog, which) -> {
            })
            .setIcon(R.id.action_restart == itemId ? R.drawable.ic_restart : R.drawable.ic_power_off)
            .show();
}

restartDevice

复制代码
private void restartDevice() {
    try {
        // 需要系统应用签名
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        pm.reboot(null);
    } catch (Exception e) {
        Toast.makeText(this, "重启失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
    }
}

shutdownDevice

复制代码
private void shutdownDevice() {
    try {
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        Method shutdownMethod = pm.getClass().getMethod("shutdown", boolean.class,
                String.class, boolean.class);
        shutdownMethod.invoke(pm, false, null, false); // 执行关机
    } catch (Exception e) {
        Toast.makeText(this, "关机失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
    }
}

最终效果

点击三个点按钮

点击重启或者关机

点击取消回到主界面;

点击确定重启或者关机执行操作。

注意:普通权限可以执行重启操作,但是不能执行关机操作,关机操作需要系统签名获取系统权限。

签名总结

由于Android studio出现些许问题,未能实践成功,但具体步骤如下:

  1. AndroidManifest.xml

    添加shareuid属性

  2. build中选择如下选项

  3. APK

  4. 首次创建选Create new...

  5. 创建一个.jks文件

  6. 点击next

  7. 选择release版本

  8. 点击create生产签名的apk

  9. 其他总结:

    链接:https://blog.csdn.net/Sunxiaolin2016/article/details/100891402

命令:jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore path/to/your/keystore.keystore your_app.apk your_alias_name

path/to/your/keystore.keystore 这个是本地的签名文件路径

相关推荐
maki0772 分钟前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
千里马学框架37 分钟前
音频焦点学习之AudioFocusRequest.Builder类剖析
android·面试·智能手机·车载系统·音视频·安卓framework开发·audio
fundroid4 小时前
掌握 Compose 性能优化三步法
android·android jetpack
TeleostNaCl5 小时前
如何在 IDEA 中使用 Proguard 自动混淆 Gradle 编译的Java 项目
android·java·经验分享·kotlin·gradle·intellij-idea
旷野说6 小时前
Android Studio Narwhal 3 特性
android·ide·android studio
maki07712 小时前
VR大空间资料 01 —— 常用VR框架对比
android·ue5·游戏引擎·vr·虚幻·pico
xhBruce16 小时前
InputReader与InputDispatcher关系 - android-15.0.0_r23
android·ims
领创工作室16 小时前
安卓设备分区作用详解-测试机红米K40
android·java·linux
hello_ludy16 小时前
Android 中的 mk 和 bp 文件编译说明
android·编译
maki07719 小时前
VR大空间资料 03 —— VRGK使用体验和源码分析
android·vr·虚幻·源码分析·oculus·htc vive·vrgk