安卓基礎(懸浮窗分級菜單和彈窗)

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.pswp.cn/pingmian/79258.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/79258.shtml
英文地址,請注明出處:http://en.pswp.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…