安卓基础(悬浮窗分级菜单和弹窗)

initializeViews()

初始化

把全部的按钮都弄出来

        // 主菜单按钮ImageButton mainButton = floatingMenuView.findViewById(R.id.main_button);// 二级菜单按钮subButtons = new ImageButton[3];subButtons[0] = floatingMenuView.findViewById(R.id.sub_button_1);subButtons[1] = floatingMenuView.findViewById(R.id.sub_button_2);subButtons[2] = floatingMenuView.findViewById(R.id.sub_button_3);// 三级菜单按钮初始化thirdLevelButtons = new ImageButton[3][3];// 第一组三级按钮 (从子按钮1展开)thirdLevelButtons[0][0] = floatingMenuView.findViewById(R.id.third_level_1_1);thirdLevelButtons[0][1] = floatingMenuView.findViewById(R.id.third_level_1_2);thirdLevelButtons[0][2] = floatingMenuView.findViewById(R.id.third_level_1_3);// 第二组三级按钮 (从子按钮2展开)thirdLevelButtons[1][0] = floatingMenuView.findViewById(R.id.third_level_2_1);thirdLevelButtons[1][1] = floatingMenuView.findViewById(R.id.third_level_2_2);thirdLevelButtons[1][2] = floatingMenuView.findViewById(R.id.third_level_2_3);// 第三组三级按钮 (从子按钮3展开)thirdLevelButtons[2][0] = floatingMenuView.findViewById(R.id.third_level_3_1);thirdLevelButtons[2][1] = floatingMenuView.findViewById(R.id.third_level_3_2);thirdLevelButtons[2][2] = floatingMenuView.findViewById(R.id.third_level_3_3);

然后就用button.setVisibility(View.GONE);把全部二级菜单全部隐藏起来

for循环也把三级菜单隐藏起来

// 初始隐藏所有二级和三级菜单for (ImageButton button : subButtons) {button.setVisibility(View.GONE);}for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {thirdLevelButtons[i][j].setVisibility(View.GONE);}}

所有的按钮都设置一下点击监听一下

// 设置主按钮点击事件mainButton.setOnClickListener(v -> toggleMainMenu());// 设置各个二级按钮的点击事件subButtons[0].setOnClickListener(v -> toggleThirdLevelMenu(0));subButtons[1].setOnClickListener(v -> toggleThirdLevelMenu(1));subButtons[2].setOnClickListener(v -> toggleThirdLevelMenu(2));// 设置三级按钮点击事件for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {final int groupIndex = i;final int buttonIndex = j;thirdLevelButtons[i][j].setOnClickListener(v -> {// 处理三级按钮点击Toast.makeText(this, "点击了按钮组" + (groupIndex + 1) + "的第" + (buttonIndex + 1) + "个按钮", Toast.LENGTH_SHORT).show();});}}

在设置一下滑动监听

        setupDragListener(mainButton);

123

├── toggleMainMenu()
│   ├── 展开/收起二级菜单
│   └── 控制动画
│
├── toggleThirdLevelMenu()
│   ├── 展开/收起三级菜单
│   └── 控制动画
│
├── hideThirdLevelMenu()
│   └── 隐藏指定组的三级菜单
│
└── onDestroy()└── 移除悬浮窗视图

123

完整代码

FloatingMenuService.java

package com.example.testtest;import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageButton;
import android.widget.Toast;public class FloatingMenuService extends Service {private WindowManager windowManager;private View floatingMenuView;private WindowManager.LayoutParams params;private boolean isMenuExpanded = false;private ImageButton[] subButtons;private ImageButton[][] thirdLevelButtons;private boolean[] isThirdLevelExpanded = {false, false, false};private int initialX, initialY;private float initialTouchX, initialTouchY;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();// 初始化WindowManagerwindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);// 加载悬浮窗布局floatingMenuView = LayoutInflater.from(this).inflate(R.layout.floating_menu, null);// 设置窗口参数params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,getLayoutType(),WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSLUCENT);params.gravity = Gravity.TOP | Gravity.START;params.x = 0;params.y = 100;// 添加视图到窗口windowManager.addView(floatingMenuView, params);// 初始化视图initializeViews();}private int getLayoutType() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {return WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {return WindowManager.LayoutParams.TYPE_PHONE;}}private void initializeViews() {// 主菜单按钮ImageButton mainButton = floatingMenuView.findViewById(R.id.main_button);// 二级菜单按钮subButtons = new ImageButton[3];subButtons[0] = floatingMenuView.findViewById(R.id.sub_button_1);subButtons[1] = floatingMenuView.findViewById(R.id.sub_button_2);subButtons[2] = floatingMenuView.findViewById(R.id.sub_button_3);// 三级菜单按钮初始化thirdLevelButtons = new ImageButton[3][3];// 第一组三级按钮 (从子按钮1展开)thirdLevelButtons[0][0] = floatingMenuView.findViewById(R.id.third_level_1_1);thirdLevelButtons[0][1] = floatingMenuView.findViewById(R.id.third_level_1_2);thirdLevelButtons[0][2] = floatingMenuView.findViewById(R.id.third_level_1_3);// 第二组三级按钮 (从子按钮2展开)thirdLevelButtons[1][0] = floatingMenuView.findViewById(R.id.third_level_2_1);thirdLevelButtons[1][1] = floatingMenuView.findViewById(R.id.third_level_2_2);thirdLevelButtons[1][2] = floatingMenuView.findViewById(R.id.third_level_2_3);// 第三组三级按钮 (从子按钮3展开)thirdLevelButtons[2][0] = floatingMenuView.findViewById(R.id.third_level_3_1);thirdLevelButtons[2][1] = floatingMenuView.findViewById(R.id.third_level_3_2);thirdLevelButtons[2][2] = floatingMenuView.findViewById(R.id.third_level_3_3);// 初始隐藏所有二级和三级菜单for (ImageButton button : subButtons) {button.setVisibility(View.GONE);}for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {thirdLevelButtons[i][j].setVisibility(View.GONE);}}// 设置主按钮点击事件mainButton.setOnClickListener(v -> toggleMainMenu());// 设置各个二级按钮的点击事件subButtons[0].setOnClickListener(v -> toggleThirdLevelMenu(0));subButtons[1].setOnClickListener(v -> toggleThirdLevelMenu(1));subButtons[2].setOnClickListener(v -> toggleThirdLevelMenu(2));// 设置三级按钮点击事件for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {final int groupIndex = i;final int buttonIndex = j;thirdLevelButtons[i][j].setOnClickListener(v -> {// 处理三级按钮点击Toast.makeText(this, "点击了按钮组" + (groupIndex + 1) + "的第" + (buttonIndex + 1) + "个按钮", Toast.LENGTH_SHORT).show();});}}// 设置拖动事件setupDragListener(mainButton);}private void toggleMainMenu() {isMenuExpanded = !isMenuExpanded;// 如果要收起主菜单,也要收起所有三级菜单if (!isMenuExpanded) {// 收起所有三级菜单for (int i = 0; i < isThirdLevelExpanded.length; i++) {if (isThirdLevelExpanded[i]) {hideThirdLevelMenu(i, true);}}}if (isMenuExpanded) {// 展开菜单for (int i = 0; i < subButtons.length; i++) {ImageButton button = subButtons[i];button.setVisibility(View.VISIBLE);// 加载动画Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in);animation.setStartOffset(i * 100); // 设置延迟,实现顺序展开效果button.startAnimation(animation);}} else {// 收起菜单for (int i = 0; i < subButtons.length; i++) {ImageButton button = subButtons[i];// 加载动画Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);animation.setStartOffset((subButtons.length - 1 - i) * 100); // 设置延迟,实现顺序收起效果button.startAnimation(animation);// 设置动画结束监听器final int index = i;animation.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {subButtons[index].setVisibility(View.GONE);}@Overridepublic void onAnimationRepeat(Animation animation) {}});button.startAnimation(animation);}}}private void toggleThirdLevelMenu(int groupIndex) {isThirdLevelExpanded[groupIndex] = !isThirdLevelExpanded[groupIndex];// 收起其他三级菜单for (int i = 0; i < isThirdLevelExpanded.length; i++) {if (i != groupIndex && isThirdLevelExpanded[i]) {hideThirdLevelMenu(i, false);isThirdLevelExpanded[i] = false;}}if (isThirdLevelExpanded[groupIndex]) {// 展开对应的三级菜单for (int i = 0; i < 3; i++) {ImageButton button = thirdLevelButtons[groupIndex][i];button.setVisibility(View.VISIBLE);// 加载动画Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in);animation.setStartOffset(i * 70); // 设置较短的延迟,三级菜单展开稍快button.startAnimation(animation);}} else {// 收起对应的三级菜单hideThirdLevelMenu(groupIndex, false);}}private void hideThirdLevelMenu(int groupIndex, boolean immediately) {for (int i = 0; i < 3; i++) {ImageButton button = thirdLevelButtons[groupIndex][i];if (immediately) {button.clearAnimation();button.setVisibility(View.GONE);continue;}// 加载动画Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);animation.setStartOffset((3 - 1 - i) * 70); // 设置较短的延迟button.startAnimation(animation);// 设置动画结束监听器final int index = i;animation.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {thirdLevelButtons[groupIndex][index].setVisibility(View.GONE);}@Overridepublic void onAnimationRepeat(Animation animation) {}});button.startAnimation(animation);}}private void setupDragListener(View view) {view.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 记录初始位置initialX = params.x;initialY = params.y;// 记录触摸点位置initialTouchX = event.getRawX();initialTouchY = event.getRawY();return true;case MotionEvent.ACTION_MOVE:// 计算移动距离params.x = initialX + (int) (event.getRawX() - initialTouchX);params.y = initialY + (int) (event.getRawY() - initialTouchY);// 更新窗口位置windowManager.updateViewLayout(floatingMenuView, params);return true;case MotionEvent.ACTION_UP:// 如果移动距离很小,则视为点击int xDiff = (int) (event.getRawX() - initialTouchX);int yDiff = (int) (event.getRawY() - initialTouchY);if (Math.abs(xDiff) < 5 && Math.abs(yDiff) < 5) {v.performClick();}return true;}return false;}});}@Overridepublic void onDestroy() {super.onDestroy();if (floatingMenuView != null && windowManager != null) {windowManager.removeView(floatingMenuView);}}
} 

MainActivity.java

package com.example.testtest;import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private static final int OVERLAY_PERMISSION_CODE = 100;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button startButton = findViewById(R.id.start_floating_button);startButton.setOnClickListener(v -> checkOverlayPermission());}private void checkOverlayPermission() {// 检查是否已有悬浮窗权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {// 如果没有权限,请求权限Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));// Android 12之前使用startActivityForResultif (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {startActivityForResult(intent, OVERLAY_PERMISSION_CODE);} else {// Android 12及以上使用ActivityResultLaunchertry {overlayPermissionLauncher.launch(intent);} catch (Exception e) {Toast.makeText(this, "打开权限设置页面失败", Toast.LENGTH_SHORT).show();}}} else {// 已有权限,启动悬浮窗服务startFloatingMenuService();}}// 用于Android 12及以上版本的权限请求结果处理private final ActivityResultLauncher<Intent> overlayPermissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),result -> {// 检查用户是否授予了权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {startFloatingMenuService();} else {Toast.makeText(this, "需要悬浮窗权限才能使用此功能", Toast.LENGTH_SHORT).show();}});@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);// 处理Android 12之前的权限请求结果if (requestCode == OVERLAY_PERMISSION_CODE) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {startFloatingMenuService();} else {Toast.makeText(this, "需要悬浮窗权限才能使用此功能", Toast.LENGTH_SHORT).show();}}}private void startFloatingMenuService() {Intent serviceIntent = new Intent(MainActivity.this, FloatingMenuService.class);startService(serviceIntent);// 可选:启动服务后可以关闭Activity// finish();}
} 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:padding="16dp"tools:context=".MainActivity"><TextViewandroid:id="@+id/title_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_marginTop="50dp"android:text="悬浮窗菜单示例"android:textSize="24sp"android:textStyle="bold" /><TextViewandroid:id="@+id/description_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/title_text"android:layout_marginTop="20dp"android:gravity="center"android:text="点击下方按钮启动带有菜单功能的悬浮窗"android:textSize="16sp" /><Buttonandroid:id="@+id/start_floating_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:padding="12dp"android:text="启动悬浮窗"android:textSize="18sp" /></RelativeLayout> 

floating_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="16dp"><!-- 三级按钮组1 (显示在子按钮1下方) --><ImageButtonandroid:id="@+id/third_level_1_3"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_1"android:layout_marginTop="8dp"android:layout_alignEnd="@+id/sub_button_1"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_1_3"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_1" /><ImageButtonandroid:id="@+id/third_level_1_2"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_1"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_toStartOf="@+id/third_level_1_3"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_1_2"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_2" /><ImageButtonandroid:id="@+id/third_level_1_1"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_1"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_toStartOf="@+id/third_level_1_2"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_1_1"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_3" /><!-- 三级按钮组2 (显示在子按钮2下方) --><ImageButtonandroid:id="@+id/third_level_2_3"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_2"android:layout_marginTop="8dp"android:layout_alignEnd="@+id/sub_button_2"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_2_3"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_1" /><ImageButtonandroid:id="@+id/third_level_2_2"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_2"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_toStartOf="@+id/third_level_2_3"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_2_2"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_2" /><ImageButtonandroid:id="@+id/third_level_2_1"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_2"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_toStartOf="@+id/third_level_2_2"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_2_1"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_3" /><!-- 三级按钮组3 (显示在子按钮3下方) --><ImageButtonandroid:id="@+id/third_level_3_3"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_3"android:layout_marginTop="8dp"android:layout_alignEnd="@+id/sub_button_3"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_3_3"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_1" /><ImageButtonandroid:id="@+id/third_level_3_2"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_3"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_toStartOf="@+id/third_level_3_3"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_3_2"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_2" /><ImageButtonandroid:id="@+id/third_level_3_1"android:layout_width="40dp"android:layout_height="40dp"android:layout_below="@+id/sub_button_3"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:layout_toStartOf="@+id/third_level_3_2"android:background="@drawable/circle_third_button_bg"android:contentDescription="@string/third_level_3_1"android:padding="8dp"android:visibility="gone"android:src="@drawable/ic_button_3" /><!-- 子按钮3 --><ImageButtonandroid:id="@+id/sub_button_3"android:layout_width="48dp"android:layout_height="48dp"android:layout_marginEnd="16dp"android:layout_toStartOf="@+id/sub_button_2"android:background="@drawable/circle_button_bg"android:contentDescription="@string/sub_button_3"android:padding="8dp"android:src="@drawable/ic_button_3" /><!-- 子按钮2 --><ImageButtonandroid:id="@+id/sub_button_2"android:layout_width="48dp"android:layout_height="48dp"android:layout_marginEnd="16dp"android:layout_toStartOf="@+id/sub_button_1"android:background="@drawable/circle_button_bg"android:contentDescription="@string/sub_button_2"android:padding="8dp"android:src="@drawable/ic_button_2" /><!-- 子按钮1 --><ImageButtonandroid:id="@+id/sub_button_1"android:layout_width="48dp"android:layout_height="48dp"android:layout_marginEnd="16dp"android:layout_toStartOf="@+id/main_button"android:background="@drawable/circle_button_bg"android:contentDescription="@string/sub_button_1"android:padding="8dp"android:src="@drawable/ic_button_1" /><!-- 主按钮 --><ImageButtonandroid:id="@+id/main_button"android:layout_width="56dp"android:layout_height="56dp"android:layout_alignParentEnd="true"android:background="@drawable/circle_main_button_bg"android:contentDescription="@string/main_button"android:padding="8dp"android:src="@drawable/ic_menu" /></RelativeLayout> 

123


弹窗

1. 创建一个基本弹窗
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示").setMessage("确认删除吗?").setPositiveButton("确定", (dialog, which) -> {// 确定按钮点击逻辑Toast.makeText(this, "已删除", Toast.LENGTH_SHORT).show();}).setNegativeButton("取消", null).show();
2. 自定义布局弹窗
AlertDialog.Builder builder = new AlertDialog.Builder(this);// 加载自定义布局
View customView = LayoutInflater.from(this).inflate(R.layout.custom_dialog, null);
builder.setView(customView);AlertDialog dialog = builder.create();
dialog.show();// 绑定自定义布局中的组件
Button btnSubmit = customView.findViewById(R.id.btn_submit);
btnSubmit.setOnClickListener(v -> {dialog.dismiss(); // 关闭弹窗
});

容器

  • 场景​​:当需要在一个弹窗中同时包含​​输入框、按钮、标题​​等多个组件时,容器可以将这些元素组织成一个整体。
  • ​优势​​:模块化的布局更易扩展和维护,例如后续新增一个按钮只需添加到容器中
// 创建容器并添加多个子视图
LinearLayout container = new LinearLayout(context);
container.setOrientation(LinearLayout.VERTICAL);
container.setPadding(50, 30, 50, 10);TextView title = new TextView(context);
title.setText("用户信息");
container.addView(title);EditText input = new EditText(context);
container.addView(input);Button submitBtn = new Button(context);
submitBtn.setText("提交");
container.addView(submitBtn);

设置监听

// 创建容器并添加多个子视图
LinearLayout container = new LinearLayout(context);
container.setOrientation(LinearLayout.VERTICAL);
container.setPadding(50, 30, 50, 10); // 注意:建议使用 dp 单位(见下文注意事项)TextView title = new TextView(context);
title.setText("用户信息");
container.addView(title);EditText input = new EditText(context);
container.addView(input);Button submitBtn = new Button(context);
submitBtn.setText("提交");// 设置点击监听
submitBtn.setOnClickListener(v -> {String text = input.getText().toString();if (!text.isEmpty()) {Toast.makeText(context, "提交内容:" + text, Toast.LENGTH_SHORT).show();} else {Toast.makeText(context, "请输入内容", Toast.LENGTH_SHORT).show();}
});container.addView(submitBtn);


改进后的MainActivity.java

package com.example.testtest;import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private static final int OVERLAY_PERMISSION_CODE = 100;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button startButton = findViewById(R.id.start_floating_button);startButton.setOnClickListener(v -> checkOverlayPermission());}private void checkOverlayPermission() {// 检查是否已有悬浮窗权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {// 如果没有权限,请求权限Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));// Android 12之前使用startActivityForResultif (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {startActivityForResult(intent, OVERLAY_PERMISSION_CODE);} else {// Android 12及以上使用ActivityResultLaunchertry {overlayPermissionLauncher.launch(intent);} catch (Exception e) {Toast.makeText(this, "打开权限设置页面失败", Toast.LENGTH_SHORT).show();}}} else {// 已有权限,启动悬浮窗服务startFloatingMenuService();}}// 用于Android 12及以上版本的权限请求结果处理private final ActivityResultLauncher<Intent> overlayPermissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),result -> {// 检查用户是否授予了权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {startFloatingMenuService();} else {Toast.makeText(this, "需要悬浮窗权限才能使用此功能", Toast.LENGTH_SHORT).show();}});@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);// 处理Android 12之前的权限请求结果if (requestCode == OVERLAY_PERMISSION_CODE) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {startFloatingMenuService();} else {Toast.makeText(this, "需要悬浮窗权限才能使用此功能", Toast.LENGTH_SHORT).show();}}}private void startFloatingMenuService() {Intent serviceIntent = new Intent(MainActivity.this, FloatingMenuService.class);startService(serviceIntent);// 可选:启动服务后可以关闭Activity// finish();}
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

冯·诺依曼体系:现代计算机的底层逻辑与百年传承

在智能手机流畅运行复杂游戏、超级计算机模拟气候变化的今天&#xff0c;很少有人会想到&#xff0c;驱动这些神奇机器运转的核心架构&#xff0c;依然遵循着70多年前提出的设计理念。这就是由匈牙利裔美国科学家约翰冯诺依曼&#xff08;John von Neumann&#xff09;奠定的冯…

【云备份】服务端工具类实现

1.文件实用工具类设计 不管是客户端还是服务端&#xff0c;文件的传输备份都涉及到文件的读写&#xff0c;包括数据管理信息的持久化也是如此&#xff0c;因此首先设 计封装文件操作类&#xff0c;这个类封装完毕之后&#xff0c;则在任意模块中对文件进行操作时都将变的简单化…

CGI 协议是否会具体到通讯报文?

CGI&#xff08;Common Gateway Interface&#xff09;不涉及具体的网络通讯报文格式&#xff0c;它定义的是 Web服务器与外部程序之间的数据交互方式&#xff0c;而不是像HTTP或FastCGI那样的二进制协议。下面分几个方面详细说明&#xff1a; 1. CGI 的交互方式&#xff08;非…

【Mytais系列】Type模块:类型转换

MyBatis 的 类型系统&#xff08;Type System&#xff09; 是框架处理 Java 类型与数据库类型之间映射的核心模块&#xff0c;它通过 类型处理器&#xff08;TypeHandler&#xff09;、类型别名&#xff08;TypeAlias&#xff09; 和 类型转换器 等机制&#xff0c;实现了数据库…

新华三H3CNE网络工程师认证—动态NAT

静态NAT严格地一对一进行地址映射&#xff0c;这就导致即便内网主机长时间离线或者不发送数据时&#xff0c;与之对应的共有地址也处于使用状态。为了避免地址浪费&#xff0c;动态NAT提出了地址池的概念&#xff1a;所有可用的共用地址组成地址池。 当内部主机访问外部网络时临…

华为OD机试真题 Java 实现【水库蓄水问题】

前言 博主刷的华为机考题&#xff0c;代码仅供参考&#xff0c;因为没有后台数据&#xff0c;可能有没考虑到的情况 如果感觉对你有帮助&#xff0c;请点点关注点点赞吧&#xff0c;谢谢你&#xff01; 题目描述 思路 1. 其实就是找一个最大的水坑&#xff0c;两个…

【Linux】Petalinux驱动开发基础

基于Petalinux做Linux驱动开发。 部分图片和经验来源于网络,若有侵权麻烦联系我删除,主要是做笔记的时候忘记写来源了,做完笔记很久才写博客。 专栏目录:记录自己的嵌入式学习之路-CSDN博客 目录 1 一个完整的Linux系统(针对Zynq) 1.1 PS部分 1.2 PL部分(若…

JAVA刷题记录: 递归,搜索与回溯

专题一 递归 面试题 08.06. 汉诺塔问题 - 力扣&#xff08;LeetCode&#xff09; class Solution {public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {dfs(A, B, C, A.size());}public void dfs(List<Integer> a, List<In…

YOLOv11改进:利用RT-DETR主干网络PPHGNetV2助力轻量化目标检测

这里写自定义目录标题 YOLOv11改进&#xff1a;利用RT-DETR主干网络PPHGNetV2助力轻量化目标检测1. 介绍2. 引言3. 技术背景3.1 YOLOv11概述3.2 RT-DETR与PPHGNetV23.3 相关工作 4. 应用使用场景5. 详细代码实现5.1 环境准备5.2 PPHGNetV2主干网络实现5.3 YOLOv11与PPHGNetV2集…

WPF之Button控件详解

文章目录 1. 引言2. Button控件基础Button类定义 3. Button控件的核心属性3.1 Content属性3.2 IsDefault属性3.3 IsCancel属性3.4 其他常用属性 4. 按钮样式与模板自定义4.1 简单样式设置4.2 使用Style对象4.3 触发器使用4.4 使用ControlTemplate完全自定义4.5 按钮视觉状态 5.…

【Java】2025 年 Java 学习路线:从入门到精通

文章目录 一、Java基础阶段(4-8周)1. 开发环境搭建2. 核心语法基础3. 面向对象编程(OOP)4. 核心类库二、Java进阶阶段(6-10周)1. JVM深度理解2. 并发编程3. 新特性掌握4. 设计模式三、开发框架与中间件(8-12周)1. Spring生态2. 持久层框架3. 常用中间件四、项目实战阶段…

虚幻引擎入门笔记

【虚幻5】UE5新手入门尝试 虚幻引擎的基础设置 1.验证-当文件误删的时候&#xff0c;对其进行验证&#xff0c;可以恢复。 2.虚幻引擎极其强大&#xff0c;可以实现多种复合技能&#xff0c;所在创建项目页面可以看见不只是创建游戏的项目 3.更改虚幻引擎默认的缓存地址。有些…

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】1.1 数据库核心概念与PostgreSQL技术优势

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 深度解析PostgreSQL核心架构与技术优势&#xff1a;从数据库原理到实战场景1.1 数据库核心概念与PostgreSQL技术优势1.1.1 关系型数据库核心架构解析1.1.1.1 数据库系统的底…

详解SLAM中的李群和李代数(上)

1 概述 最近阅读高翔大神的《视觉SLAM十四讲》这本书&#xff0c;感觉整本书写的非常的平实&#xff0c;用非常接地气的语言毫无保留的介绍了视觉SLAM的相关知识&#xff0c;非常值得一读。不过&#xff0c;在第4章出现的李群和李代数的相关概念就有点令人难以费解了。其实这段…

libevent库详解:高性能异步IO的利器

目录 一、libevent 简介 主要特点&#xff1a; 二、事件模型原理 1. event_base 2. event 3. evconnlistener&#xff08;TCP监听器&#xff09; 4. bufferevent 简化流程如下&#xff1a; 三、libevent 使用示例 1. 创建事件主循环 2. 创建监听器&#xff08;TCP&a…

从 “零” 做个开源音乐软件“SteadyBeat”吧!<1> 准备

换换脑子&#xff0c;做个音乐软件&#xff0c;根据调性、和弦走向&#xff08;情感&#xff09;、节拍、速度等需求&#xff0c;结合AI和一众工具&#xff0c;自动生成伴奏、Solo等&#xff0c;有点像库乐队&#xff01;自己平时也用得着&#xff0c;暂时取名叫《SteadyBeat》…

npm error code CERT_HAS_EXPIRED

npm error code CERT_HAS_EXPIRED 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发者社区主理人 擅长.net、C、python开发&#xff0c; 如果遇到技术问题&#xff0c;即可私…

数字世界的“私人车道“:网络切片如何用Python搭建专属通信高速路?

数字世界的"私人车道"&#xff1a;网络切片如何用Python搭建专属通信高速路&#xff1f; 2024年6月&#xff0c;中国移动宣布在浙江某智能工厂完成全球首个"5G工业网络切片"规模商用——这条为生产线定制的"数字专属车道"&#xff0c;将设备控制…

VSCode Verilog编辑仿真环境搭建

VSCode Verilog环境搭建 下载Iverilog安装Iverilog验证安装VS Code安装插件 下载Iverilog 官网下载Iverilog 安装Iverilog 一定要勾选这两项 建议勾选这两项 验证安装 运行Windows PowerShell输入命令&#xff1a;iverilog输入命令&#xff1a;Get-Command gtkwave …

C++ - 数据容器之 list(创建与初始化、元素访问、容量判断、元素遍历、添加元素、删除元素)

一、创建与初始化 引入 <list> 并使用 std 命名空间 #include <list>using namespace std;创建一个空 list list<int> my_list;创建一个包含 5 个元素&#xff0c;每个元素初始化为 0 的 list list<int> my_list(5);创建一个包含 5 个元素&#xf…