安卓传感器横竖屏切换

工具方法 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();
    }
}
相关推荐
2301_7717172114 分钟前
解决mysql报错:1406, Data too long for column
android·数据库·mysql
dvjr cloi39 分钟前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
OBiO20133 小时前
Cell | 突破AAV载体容量限制!路中华/姜玉武/刘太安团队开发AAVLINK系统实现大基因递送
笔记
老花眼猫3 小时前
编制椭圆旋转绘图函数
c语言·经验分享·青少年编程·课程设计
随遇丿而安3 小时前
第2周:`EditText` 不只是输入框,它是 Android 输入体验的第一道门
android
我命由我123453 小时前
Kotlin 开发 - lateinit 关键字
android·java·开发语言·kotlin·android studio·android-studio·android runtime
智者知已应修善业3 小时前
【51单片机2个按键控制流水灯运行与暂停】2023-9-6
c++·经验分享·笔记·算法·51单片机
一起搞IT吧4 小时前
Android性能系列专题理论之十:systrace/perfetto相关指标知识点细节含义总结
android·嵌入式硬件·智能手机·性能优化
sakiko_4 小时前
UIKit学习笔记5-使用UITableView制作聊天页面
笔记·学习·swift·uikit
Alice-YUE5 小时前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript