實現一個 Activity 以**懸浮窗/對話框形式**覆蓋在另一個 Activity 之上,保持底層 Activity 可見(可選模態/非模態)。
<activity
android:name=".FloatActivity"
android:theme="@android:style/Theme.Dialog"
<!-- 或 Theme.Holo.Dialog, Theme.Material.Light.Dialog -->
android:excludeFromRecents="true"
/>
public class FloatActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_float);
// 設置寬高比例或固定值
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = (int)(dm.widthPixels * 0.8);
int height = (int)(dm.heightPixels * 0.6);
getWindow().setLayout(width, height);
// 可選:設置背景透明、圓角
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
}
}
// 在主 Activity 中
Intent intent = new Intent(this, FloatActivity.class);
startActivity(intent); // 像啟動普通 Activity 一樣
適用於**跨應用、桌面懸浮球、全局覆蓋**場景。
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
public class FloatWindowService extends Service {
private WindowManager wm;
private View floatView;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
floatView = LayoutInflater.from(this).inflate(R.layout.float_layout, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
// Android 8.0+ 必須用 TYPE_APPLICATION_OVERLAY
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
: WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.START | Gravity.TOP;
params.x = 100; params.y = 100;
wm.addView(floatView, params);
return START_STICKY;
}
@Override
public void onDestroy() {
if (floatView != null) wm.removeView(floatView);
super.onDestroy();
}
}
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
| 維度 | Dialog 主題 Activity | WindowManager 懸浮窗 |
|---|---|---|
| 實現難度 | 極低 | 高(權限、生命周期、觸摸分發) |
| 作用域 | 應用內 | 全局(桌面/其他App上) |
| 生命周期 | 標準 Activity | Service 管理,需手動控制 |
| 適用場景 | 對話框、底部彈窗、表單頁 | 懸浮球、錄屏控制、全局客服 |
| 權限需求 | 無 | SYSTEM_ALERT_WINDOW(高危) |
// 主題繼承
<style name="Theme.Translucent" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowCloseOnTouchOutside">false</item> <!-- 模態 -->
</style>
**應用內懸浮首選 `Theme.Dialog` Activity**,零權限、標準生命周期、易維護。**跨應用/桌面懸浮必須用 `WindowManager` + Service**,但需處理權限申請、版本適配、觸摸事件分發等複雜問題。根據業務場景選型。