Android强制横屏
目录
Android强制横屏
Android强制横屏实现方案
原理是在其他应用上方创建一个超级小的layout来带动它横屏
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"
tools:ignore="ForegroundServicesPolicy" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Intop">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".OverlayService"
android:enabled="true"
android:exported="false" />
<receiver
android:name=".StopServiceReceiver"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
MainActivity.java
package com.acplt.intop;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.Toast;
import android.Manifest;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE_OVERLAY_PERMISSION = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 一启动就检查并请求权限
checkOverlayPermission();
}
@Override
protected void onResume() {
super.onResume();
// 从设置页面返回时,再次检查
if (Settings.canDrawOverlays(this)) {
startOverlayService();
finish(); // 权限到手,启动服务后直接关闭Activity
}
}
private void checkOverlayPermission() {
if (Settings.canDrawOverlays(this)) {
// 已经有权限,直接启动服务
startOverlayService();
finish();
} else {
// 没有权限,引导用户去开启
requestOverlayPermission();
}
}
private void requestOverlayPermission() {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION);
}
private void startOverlayService() {
// 启动服务
Intent serviceIntent = new Intent(this, OverlayService.class);
ContextCompat.startForegroundService(this, serviceIntent);
Toast.makeText(this, "强制横屏已开启", Toast.LENGTH_SHORT).show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_OVERLAY_PERMISSION) {
// 不管用户是否授权,onResume里都会再次检查,所以这里不需要额外处理
}
}
}
OverlayService.java
package com.acplt.intop;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
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.View;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
public class OverlayService extends Service {
private View overlayView;
private WindowManager windowManager;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@SuppressLint("ForegroundServiceType")
@Override
public void onCreate() {
super.onCreate();
createOverlayView();
startForeground(1, createNotification()); // 必须将服务设为前台服务
}
private void createOverlayView() {
// 1. 获取 WindowManager
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// 2. 设置 WindowManager.LayoutParams 参数
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
1, // 宽度:1像素,几乎看不见
1, // 高度:1像素,几乎看不见
// 系统窗口类型,关键!
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
// 标志:不可聚焦、不可触摸、保持在锁屏之上
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
PixelFormat.TRANSLUCENT // 透明
);
// 3. 设置窗口位置和重力
params.gravity = Gravity.START | Gravity.TOP; // 左上角
// 4. !!!最关键的参数:强制此窗口为横屏!!!
params.screenOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
// 5. 创建一个极小的View
overlayView = LayoutInflater.from(this).inflate(R.layout.overlay_layout, null);
// 6. 将View添加到WindowManager
try {
windowManager.addView(overlayView, params);
} catch (Exception e) {
Toast.makeText(this, "无法创建悬浮窗,请确保权限已开启", Toast.LENGTH_LONG).show();
stopSelf();
}
}
private Notification createNotification() {
// 创建停止服务的Intent
Intent stopIntent = new Intent(this, StopServiceReceiver.class);
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(
this,
0,
stopIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// 创建一个前台服务通知
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 创建通知渠道(Android 8.0+要求)
NotificationChannel channel = new NotificationChannel(
"landscape_channel",
"强制横屏服务",
NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "landscape_channel")
.setContentTitle("强制横屏服务运行中")
.setContentText("点击停止服务")
.setSmallIcon(R.mipmap.ic_launcher) // 确保有这个资源
.setPriority(NotificationCompat.PRIORITY_LOW)
.setContentIntent(stopPendingIntent) // 设置点击事件
.setAutoCancel(true); // 点击后自动取消通知
return builder.build();
}
@Override
public void onDestroy() {
super.onDestroy();
// 服务被销毁时,移除悬浮窗
if (overlayView != null && windowManager != null) {
windowManager.removeView(overlayView);
}
Toast.makeText(this, "强制横屏已关闭", Toast.LENGTH_SHORT).show();
}
}
StopServiceReceiver.java
package com.acplt.intop;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class StopServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 停止 OverlayService
Intent serviceIntent = new Intent(context, OverlayService.class);
context.stopService(serviceIntent);
// 可选:显示提示消息
Toast.makeText(context, "强制横屏已关闭", Toast.LENGTH_SHORT).show();
}
}
overlay_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="1dp"
android:layout_height="1dp"
android:orientation="vertical">
<!-- 这个布局基本是空的,只是作为一个占位符 -->
</LinearLayout>