安卓传感器横竖屏切换

工具方法 ScreenOrientationHelper.java

java 复制代码
package com.csdn.utils;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;

import com.csdn.BuildConfig;
import com.socks.library.KLog;

/**
 * 屏幕方向控制工具类:支持 Activity + Fragment,封装传感器监听和横竖屏切换逻辑
 */
public class ScreenOrientationHelper implements SensorEventListener {

    private static final String TAG = "ScreenOrientationHelper";
    private static final float ACCEL_THRESHOLD = 3.0f; // 加速度变化阈值(防抖)
    private static final long UPDATE_INTERVAL = 200; // 方向更新最小间隔(毫秒)

    private Context mContext; // 基础上下文(Activity/Fragment 均可)
    private Activity mHostActivity; // 实际控制屏幕方向的 Activity(Fragment 需通过 getActivity() 获取)
    private final SensorManager mSensorManager;
    private final Sensor mAccelerometer;

    private boolean mIsFullscreen = false; // 是否处于全屏状态
    private int mCurrentRotation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; // 当前屏幕方向
    private long mLastUpdateTime = 0; // 上次方向更新时间
    private boolean mIsSensorRegistered = false; // 传感器是否已注册
    private boolean mIsFragmentAttached = false; // 是否绑定了 Fragment

    // -------------------------- 构造方法(支持 Activity/Fragment) --------------------------
    /**
     * 构造方法(用于 Activity)
     * @param activity 关联的 Activity
     */
    public ScreenOrientationHelper(@NonNull Activity activity) {
        this.mContext = activity;
        this.mHostActivity = activity;
        this.mSensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
        this.mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    }

    /**
     * 构造方法(用于 Fragment)
     * @param fragment 关联的 Fragment
     */
    public ScreenOrientationHelper(@NonNull Fragment fragment) {
        this.mContext = fragment.getContext();
        this.mSensorManager = (SensorManager) fragment.getContext().getSystemService(Context.SENSOR_SERVICE);
        this.mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        attachFragment(fragment); // 绑定 Fragment 生命周期
    }

    // -------------------------- Fragment 生命周期绑定 --------------------------
    /**
     * 绑定 Fragment 并监听其生命周期(自动处理 Activity 引用和资源释放)
     */
    private void attachFragment(@NonNull Fragment fragment) {
        mIsFragmentAttached = true;
        // 获取 Fragment 所在的 Activity(确保在 onAttach 之后调用,这里通过 FragmentManager 监听)
        fragment.getParentFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
            @Override
            public void onFragmentActivityCreated(@NonNull FragmentManager fm, @NonNull Fragment f, Bundle savedInstanceState) {
                super.onFragmentActivityCreated(fm, f, savedInstanceState);
                mHostActivity = f.getActivity(); // Fragment 关联 Activity 后,获取 Activity 引用
                if (mHostActivity == null && BuildConfig.DEBUG) {
                    KLog.e(TAG, "Fragment 未关联 Activity,无法控制屏幕方向");
                }
            }

            @Override
            public void onFragmentDestroyed(@NonNull FragmentManager fm, @NonNull Fragment f) {
                super.onFragmentDestroyed(fm, f);
                release(); // Fragment 销毁时释放资源
                fm.unregisterFragmentLifecycleCallbacks(this); // 解除监听
            }
        }, false);
    }

    // -------------------------- 核心功能方法 --------------------------
    /**
     * 设置是否全屏(控制传感器是否生效)
     * @param isFullscreen true-全屏(启用传感器),false-非全屏(禁用传感器)
     */
    public void setFullscreen(boolean isFullscreen) {
        // 未获取到宿主 Activity 时,不执行任何操作
        if (mHostActivity == null) {
            if (BuildConfig.DEBUG) KLog.w(TAG, "宿主 Activity 为空,无法设置全屏状态");
            return;
        }

        this.mIsFullscreen = isFullscreen;
        if (isFullscreen) {
            registerSensorListener();
        } else {
            unregisterSensorListener();
            restorePortraitOrientation(); // 非全屏时恢复竖屏
        }
    }

    /**
     * 注册传感器监听
     */
    public void registerSensorListener() {
        if (mAccelerometer == null) {
            if (BuildConfig.DEBUG) KLog.e(TAG, "设备不支持加速度传感器");
            return;
        }
        if (mHostActivity == null) {
            if (BuildConfig.DEBUG) KLog.w(TAG, "宿主 Activity 为空,无法注册传感器");
            return;
        }
        if (!mIsSensorRegistered) {
            boolean isRegistered = mSensorManager.registerListener(
                    this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
            if (isRegistered) {
                mIsSensorRegistered = true;
                if (BuildConfig.DEBUG) KLog.d(TAG, "传感器注册成功");
            } else {
                if (BuildConfig.DEBUG) KLog.e(TAG, "传感器注册失败");
            }
        }
    }

    /**
     * 注销传感器监听
     */
    public void unregisterSensorListener() {
        if (mIsSensorRegistered) {
            mSensorManager.unregisterListener(this);
            mIsSensorRegistered = false;
            if (BuildConfig.DEBUG) KLog.d(TAG, "传感器注销成功");
        }
    }

    /**
     * 恢复竖屏方向
     */
    public void restorePortraitOrientation() {
        if (mHostActivity == null) return;

        if (mCurrentRotation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
            mHostActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
            mCurrentRotation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
            if (BuildConfig.DEBUG) KLog.d(TAG, "恢复竖屏成功");
        }
    }

    /**
     * 强制设置屏幕方向
     * @param orientation 屏幕方向(如 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
     */
    public void setScreenOrientation(int orientation) {
        if (mHostActivity == null) return;

        if (mCurrentRotation != orientation) {
            mHostActivity.setRequestedOrientation(orientation);
            mCurrentRotation = orientation;
            if (BuildConfig.DEBUG) KLog.d(TAG, "设置屏幕方向:" + getOrientationName(orientation));
        }
    }

    // -------------------------- 辅助方法 --------------------------
    /**
     * 获取当前屏幕方向名称(用于日志)
     */
    private String getOrientationName(int orientation) {
        switch (orientation) {
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                return "竖屏";
            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
                return "横屏(正常)";
            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
                return "横屏(反向)";
            default:
                return "未知方向";
        }
    }

    // -------------------------- 传感器回调 --------------------------
    @Override
    public void onSensorChanged(SensorEvent event) {
        // 非全屏、未注册传感器、无宿主 Activity 时直接返回
        if (!mIsFullscreen || !mIsSensorRegistered || mHostActivity == null) return;

        // 防抖处理:控制更新频率
        long currentTime = System.currentTimeMillis();
        if (currentTime - mLastUpdateTime < UPDATE_INTERVAL) return;
        mLastUpdateTime = currentTime;

        float x = event.values[0]; // x轴加速度(左右方向)
        float y = event.values[1]; // y轴加速度(上下方向)

        // 仅处理横屏方向切换(x轴主导且超过阈值)
        if (Math.abs(x) > Math.abs(y) && Math.abs(x) > ACCEL_THRESHOLD) {
            int newRotation = x < 0 ?
                    ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE : // 左横屏
                    ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; // 右横屏

            setScreenOrientation(newRotation);
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // 传感器精度变化时无需处理
    }

    // -------------------------- 资源释放 --------------------------
    /**
     * 释放资源(Activity/Fragment 销毁时调用)
     */
    public void release() {
        unregisterSensorListener();
        // 清空引用,避免内存泄漏
        mContext = null;
        mHostActivity = null;
        if (BuildConfig.DEBUG) KLog.d(TAG, "资源释放完成");
    }

    // -------------------------- Getter 方法 --------------------------
    public boolean isFullscreen() {
        return mIsFullscreen;
    }

    public int getCurrentRotation() {
        return mCurrentRotation;
    }

    public boolean isFragmentAttached() {
        return mIsFragmentAttached;
    }
}

Fragment 中使用

java 复制代码
public class VideoFragment extends Fragment {
    private ScreenOrientationHelper mOrientationHelper;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 直接传入 Fragment 构造
        mOrientationHelper = new ScreenOrientationHelper(this);
    }

    // 示例:全屏播放时启用传感器,退出全屏时禁用
    public void enterFullscreen() {
        mOrientationHelper.setFullscreen(true);
    }

    public void exitFullscreen() {
        mOrientationHelper.setFullscreen(false);
    }

    // (可选)如果需要手动释放,可在 onDestroy 补充(已通过生命周期回调自动处理)
    @Override
    public void onDestroy() {
        super.onDestroy();
        mOrientationHelper.release();
    }
}

Activity 中使用

java 复制代码
public class VideoActivity extends Activity {
    private ScreenOrientationHelper mOrientationHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mOrientationHelper = new ScreenOrientationHelper(this);
    }

    public void toggleFullscreen(boolean isFullscreen) {
        mOrientationHelper.setFullscreen(isFullscreen);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mOrientationHelper.release();
    }
}
相关推荐
思陌Ai算法定制3 分钟前
肿瘤MDT能用大模型角色扮演模拟吗?GPT-5五种提示框架决策一致性78%-87%
经验分享
CedarQR43 分钟前
云卓 G20 遥控器 Android 开发实录:从 USB 设备到读取摇杆/按键通道值
android·linux·无人机
i love you china1 小时前
问卷系统调查博客项目测试报告
笔记
疯狂打码的少年1 小时前
【数据结构】板块总结 + 下期预告(数据库技术)
数据结构·笔记·算法
zQ.ii1 小时前
WPF 触发器学习笔记
笔记·学习·wpf
pengyu1 小时前
【Kotlin 协程修仙录 · 炼虚境 · 初阶】 | 虚空造物:Channel 基础与协程间通信的管道艺术
android·前端·kotlin
小强闯江湖1 小时前
ViewCompose:让原生 Android View 进入声明式时代
android·开源·kotlin
乐橙开放平台2 小时前
老旧社区双通道笔记:周界 hoveringAlarm / aiPerArea + 电梯 aiNonVehDetect,一套 callback 做 24h 防护
笔记·物联网·音视频·智能家居
想要成为老金高手2 小时前
Kubernetes 实战笔记(一):Pod 管理与 kubectl 核心命令全解
笔记·容器·kubernetes
程序员麻辣烫2 小时前
反曲弓射箭笔记
笔记·生活