安卓传感器横竖屏切换

工具方法 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();
    }
}
相关推荐
崎岖Qiu2 分钟前
【设计模式笔记19】:建造者模式
java·笔记·设计模式·建造者模式
Java猿_7 小时前
Spring Boot 集成 Sa-Token 实现登录认证与 RBAC 权限控制(实战)
android·spring boot·后端
大布布将军8 小时前
⚡️ 深入数据之海:SQL 基础与 ORM 的应用
前端·数据库·经验分享·sql·程序人生·面试·改行学it
锦瑟弦音8 小时前
微信小游戏分包(cocos自带分包)
笔记·游戏
找方案9 小时前
我的 all-in-rag 学习笔记:文本分块 ——RAG 系统的 “信息切菜术“
人工智能·笔记·all-in-rag
HXR_plume9 小时前
【Web信息处理与应用课程笔记1】网页排序(上)
笔记
源代码•宸9 小时前
goframe框架签到系统项目(BITFIELD 命令详解、Redis Key 设计、goframe 框架教程、安装MySQL)
开发语言·数据库·经验分享·redis·后端·mysql·golang
qcwl669 小时前
操作系统 真象还原 学习笔记#13
笔记·学习
m0_689618289 小时前
30 分钟打印!多材料3D打印软机器人内置驱动 + 自主避障
笔记·学习·机器人
Rousson10 小时前
硬件学习笔记--93 静电防护方案(电阻、磁珠、电感、TVS等)
笔记·单片机·学习