IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 手机加速度传感器在Android横竖屏切换中的应用 -> 正文阅读

[移动开发]手机加速度传感器在Android横竖屏切换中的应用

如何判断手机的横竖屏

这个问题依赖与手机上的传感器
一般获取手机倾斜角度,用加速度计和磁力计去判断
如果只是判断横竖屏 可以只用加速度计
加速度计普及度高

使用加速度计实现

参考了AOSP的通用实现

public class AccelerometerListener {

  // Device orientation
  public static final int ORIENTATION_UNKNOWN = 0;
  public static final int ORIENTATION_VERTICAL = 1;
  public static final int ORIENTATION_HORIZONTAL = 2;
  private static final String TAG = "AccelerometerListener";
  private static final boolean DEBUG = true;
  private static final boolean VDEBUG = false;
  private static final int ORIENTATION_CHANGED = 1234;
  private static final int VERTICAL_DEBOUNCE = 100;
  private static final int HORIZONTAL_DEBOUNCE = 500;
  private static final double VERTICAL_ANGLE = 50.0;
  private SensorManager sensorManager;
  private Sensor sensor;
  // mOrientation is the orientation value most recently reported to the client.
  private int orientation;
  // mPendingOrientation is the latest orientation computed based on the sensor value.
  // This is sent to the client after a rebounce delay, at which point it is copied to
  // mOrientation.
  private int pendingOrientation;
  private OrientationListener listener;
  Handler handler =
      new Handler() {
        @Override
        public void handleMessage(Message msg) {
          switch (msg.what) {
            case ORIENTATION_CHANGED:
              synchronized (this) {
                orientation = pendingOrientation;
                if (DEBUG) {
                  LogUtil.d(
                      TAG,
                      "orientation: "
                          + (orientation == ORIENTATION_HORIZONTAL
                              ? "horizontal"
                              : (orientation == ORIENTATION_VERTICAL ? "vertical" : "unknown")));
                }
                if (listener != null) {
                  listener.orientationChanged(orientation);
                }
              }
              break;
          }
        }
      };
  SensorEventListener sensorListener =
      new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
          onSensorEvent(event.values[0], event.values[1], event.values[2]);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
          // ignore
        }
      };

  public AccelerometerListener(Context context) {
    sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
    sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  }

  public void setListener(OrientationListener listener) {
    this.listener = listener;
  }

  public void enable(boolean enable) {
    if (DEBUG) {
      LogUtil.d(TAG, "enable(" + enable + ")");
    }
    synchronized (this) {
      if (enable) {
        orientation = ORIENTATION_UNKNOWN;
        pendingOrientation = ORIENTATION_UNKNOWN;
        sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
      } else {
        sensorManager.unregisterListener(sensorListener);
        handler.removeMessages(ORIENTATION_CHANGED);
      }
    }
  }

  private void setOrientation(int orientation) {
    synchronized (this) {
      if (pendingOrientation == orientation) {
        // Pending orientation has not changed, so do nothing.
        return;
      }

      // Cancel any pending messages.
      // We will either start a new timer or cancel alltogether
      // if the orientation has not changed.
      handler.removeMessages(ORIENTATION_CHANGED);

      if (this.orientation != orientation) {
        // Set timer to send an event if the orientation has changed since its
        // previously reported value.
        pendingOrientation = orientation;
        final Message m = handler.obtainMessage(ORIENTATION_CHANGED);
        // set delay to our debounce timeout
        int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE : HORIZONTAL_DEBOUNCE);
        handler.sendMessageDelayed(m, delay);
      } else {
        // no message is pending
        pendingOrientation = ORIENTATION_UNKNOWN;
      }
    }
  }

  private void onSensorEvent(double x, double y, double z) {
    if (VDEBUG) {
      LogUtil.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
    }

    // If some values are exactly zero, then likely the sensor is not powered up yet.
    // ignore these events to avoid false horizontal positives.
    if (x == 0.0 || y == 0.0 || z == 0.0) {
      return;
    }

    // magnitude of the acceleration vector projected onto XY plane
    final double xy = Math.hypot(x, y);
    // compute the vertical angle
    double angle = Math.atan2(xy, z);
    // convert to degrees
    angle = angle * 180.0 / Math.PI;
    final int orientation =
        (angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);
    if (VDEBUG) {
      LogUtil.d(TAG, "angle: " + angle + " orientation: " + orientation);
    }
    setOrientation(orientation);
  }

  public interface OrientationListener {

    void orientationChanged(int orientation);
  }
}

接口使用


public class ProximitySensor
    implements AccelerometerListener.OrientationListener, InCallStateListener, AudioModeListener {

  private static final String TAG = ProximitySensor.class.getSimpleName();

  private final PowerManager powerManager;
  private final PowerManager.WakeLock proximityWakeLock;
  private final AudioModeProvider audioModeProvider;
  private final AccelerometerListener accelerometerListener;
  private final ProximityDisplayListener displayListener;
  private int orientation = AccelerometerListener.ORIENTATION_UNKNOWN;
  private boolean uiShowing = false;
  private boolean isPhoneOffhook = false;
  private boolean dialpadVisible;
  private boolean isAttemptingVideoCall;
  private boolean isVideoCall;
  private boolean isRttCall;

  public ProximitySensor(
      @NonNull Context context,
      @NonNull AudioModeProvider audioModeProvider,
      @NonNull AccelerometerListener accelerometerListener) {
    Trace.beginSection("ProximitySensor.Constructor");
    powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
      proximityWakeLock =
          powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
    } else {
      LogUtil.i("ProximitySensor.constructor", "Device does not support proximity wake lock.");
      proximityWakeLock = null;
    }
    this.accelerometerListener = accelerometerListener;
    this.accelerometerListener.setListener(this);

    displayListener =
        new ProximityDisplayListener(
            (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE));
    displayListener.register();

    this.audioModeProvider = audioModeProvider;
    this.audioModeProvider.addListener(this);
    Trace.endSection();
  }

  public void tearDown() {
    audioModeProvider.removeListener(this);

    accelerometerListener.enable(false);
    displayListener.unregister();

    turnOffProximitySensor(true);
  }

  /** Called to identify when the device is laid down flat. */
  @Override
  public void orientationChanged(int orientation) {
    this.orientation = orientation;
    updateProximitySensorMode();
  }

  /** Called to keep track of the overall UI state. */
  @Override
  public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
    // We ignore incoming state because we do not want to enable proximity
    // sensor during incoming call screen. We check hasLiveCall() because a disconnected call
    // can also put the in-call screen in the INCALL state.
    boolean hasOngoingCall = InCallState.INCALL == newState && callList.hasLiveCall();
    boolean isOffhook =
        InCallState.PENDING_OUTGOING == newState
            || InCallState.OUTGOING == newState
            || hasOngoingCall;

    DialerCall activeCall = callList.getActiveCall();
    boolean isVideoCall = activeCall != null && activeCall.isVideoCall();
    boolean isRttCall = activeCall != null && activeCall.isActiveRttCall();

    if (isOffhook != isPhoneOffhook
        || this.isVideoCall != isVideoCall
        || this.isRttCall != isRttCall) {
      isPhoneOffhook = isOffhook;
      this.isVideoCall = isVideoCall;
      this.isRttCall = isRttCall;

      orientation = AccelerometerListener.ORIENTATION_UNKNOWN;
      accelerometerListener.enable(isPhoneOffhook);

      updateProximitySensorMode();
    }
  }

  @Override
  public void onAudioStateChanged(CallAudioState audioState) {
    updateProximitySensorMode();
  }

  public void onDialpadVisible(boolean visible) {
    dialpadVisible = visible;
    updateProximitySensorMode();
  }

  public void setIsAttemptingVideoCall(boolean isAttemptingVideoCall) {
    LogUtil.i(
        "ProximitySensor.setIsAttemptingVideoCall",
        "isAttemptingVideoCall: %b",
        isAttemptingVideoCall);
    this.isAttemptingVideoCall = isAttemptingVideoCall;
    updateProximitySensorMode();
  }
  /** Used to save when the UI goes in and out of the foreground. */
  public void onInCallShowing(boolean showing) {
    if (showing) {
      uiShowing = true;

      // We only consider the UI not showing for instances where another app took the foreground.
      // If we stopped showing because the screen is off, we still consider that showing.
    } else if (powerManager.isScreenOn()) {
      uiShowing = false;
    }
    updateProximitySensorMode();
  }

  void onDisplayStateChanged(boolean isDisplayOn) {
    LogUtil.i("ProximitySensor.onDisplayStateChanged", "isDisplayOn: %b", isDisplayOn);
    accelerometerListener.enable(isDisplayOn);
  }

  /**
   * TODO: There is no way to determine if a screen is off due to proximity or if it is legitimately
   * off, but if ever we can do that in the future, it would be useful here. Until then, this
   * function will simply return true of the screen is off. TODO: Investigate whether this can be
   * replaced with the ProximityDisplayListener.
   */
  public boolean isScreenReallyOff() {
    return !powerManager.isScreenOn();
  }

  private void turnOnProximitySensor() {
    if (proximityWakeLock != null) {
      if (!proximityWakeLock.isHeld()) {
        LogUtil.i("ProximitySensor.turnOnProximitySensor", "acquiring wake lock");
        proximityWakeLock.acquire();
      } else {
        LogUtil.i("ProximitySensor.turnOnProximitySensor", "wake lock already acquired");
      }
    }
  }

  private void turnOffProximitySensor(boolean screenOnImmediately) {
    if (proximityWakeLock != null) {
      if (proximityWakeLock.isHeld()) {
        LogUtil.i("ProximitySensor.turnOffProximitySensor", "releasing wake lock");
        int flags = (screenOnImmediately ? 0 : PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);
        proximityWakeLock.release(flags);
      } else {
        LogUtil.i("ProximitySensor.turnOffProximitySensor", "wake lock already released");
      }
    }
  }

  /**
   * Updates the wake lock used to control proximity sensor behavior, based on the current state of
   * the phone.
   *
   * <p>On devices that have a proximity sensor, to avoid false touches during a call, we hold a
   * PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock whenever the phone is off hook. (When held, that wake
   * lock causes the screen to turn off automatically when the sensor detects an object close to the
   * screen.)
   *
   * <p>This method is a no-op for devices that don't have a proximity sensor.
   *
   * <p>Proximity wake lock will be released if any of the following conditions are true: the audio
   * is routed through bluetooth, a wired headset, or the speaker; the user requested, received a
   * request for, or is in a video call; or the phone is horizontal while in a call.
   */
  private synchronized void updateProximitySensorMode() {
    Trace.beginSection("ProximitySensor.updateProximitySensorMode");
    final int audioRoute = audioModeProvider.getAudioState().getRoute();

    boolean screenOnImmediately =
        (CallAudioState.ROUTE_WIRED_HEADSET == audioRoute
            || CallAudioState.ROUTE_SPEAKER == audioRoute
            || CallAudioState.ROUTE_BLUETOOTH == audioRoute
            || isAttemptingVideoCall
            || isVideoCall
            || isRttCall);

    // We do not keep the screen off when the user is outside in-call screen and we are
    // horizontal, but we do not force it on when we become horizontal until the
    // proximity sensor goes negative.
    final boolean horizontal = (orientation == AccelerometerListener.ORIENTATION_HORIZONTAL);
    screenOnImmediately |= !uiShowing && horizontal;

    // We do not keep the screen off when dialpad is visible, we are horizontal, and
    // the in-call screen is being shown.
    // At that moment we're pretty sure users want to use it, instead of letting the
    // proximity sensor turn off the screen by their hands.
    screenOnImmediately |= dialpadVisible && horizontal;

    LogUtil.i(
        "ProximitySensor.updateProximitySensorMode",
        "screenOnImmediately: %b, dialPadVisible: %b, "
            + "offHook: %b, horizontal: %b, uiShowing: %b, audioRoute: %s",
        screenOnImmediately,
        dialpadVisible,
        isPhoneOffhook,
        orientation == AccelerometerListener.ORIENTATION_HORIZONTAL,
        uiShowing,
        CallAudioState.audioRouteToString(audioRoute));

    if (isPhoneOffhook && !screenOnImmediately) {
      LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning on proximity sensor");
      // Phone is in use!  Arrange for the screen to turn off
      // automatically when the sensor detects a close object.
      turnOnProximitySensor();
    } else {
      LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning off proximity sensor");
      // Phone is either idle, or ringing.  We don't want any special proximity sensor
      // behavior in either case.
      turnOffProximitySensor(screenOnImmediately);
    }
    Trace.endSection();
  }

  /**
   * Implementation of a {@link DisplayListener} that maintains a binary state: Screen on vs screen
   * off. Used by the proximity sensor manager to decide whether or not it needs to listen to
   * accelerometer events.
   */
  public class ProximityDisplayListener implements DisplayListener {

    private DisplayManager displayManager;
    private boolean isDisplayOn = true;

    ProximityDisplayListener(DisplayManager displayManager) {
      this.displayManager = displayManager;
    }

    void register() {
      displayManager.registerDisplayListener(this, null);
    }

    void unregister() {
      displayManager.unregisterDisplayListener(this);
    }

    @Override
    public void onDisplayRemoved(int displayId) {}

    @Override
    public void onDisplayChanged(int displayId) {
      if (displayId == Display.DEFAULT_DISPLAY) {
        final Display display = displayManager.getDisplay(displayId);

        final boolean isDisplayOn = display.getState() != Display.STATE_OFF;
        // For call purposes, we assume that as long as the screen is not truly off, it is
        // considered on, even if it is in an unknown or low power idle state.
        if (isDisplayOn != this.isDisplayOn) {
          this.isDisplayOn = isDisplayOn;
          onDisplayStateChanged(this.isDisplayOn);
        }
      }
    }

    @Override
    public void onDisplayAdded(int displayId) {}
  }
}

使用加速度计和磁力计获取手机倾斜角度

public class MainActivity extends Activity {
  
    private SensorManager mSensorManager;
  
    private Sensor accelerometer; // 加速度传感器
    private Sensor magnetic; // 地磁场传感器
  
    private TextView azimuthAngle;
  
    private float[] accelerometerValues = new float[3];
    private float[] magneticFieldValues = new float[3];
  
    private static final String TAG = "---MainActivity";
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 实例化传感器管理者
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        // 初始化加速度传感器
        accelerometer = mSensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        // 初始化地磁场传感器
        magnetic = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
  
        azimuthAngle = (TextView) findViewById(R.id.azimuth_angle_value);
        calculateOrientation();
    }
  
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        // 注册监听
        mSensorManager.registerListener(new MySensorEventListener(),
                accelerometer, Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(new MySensorEventListener(), magnetic,
                Sensor.TYPE_MAGNETIC_FIELD);
        super.onResume();
    }
  
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        // 解除注册
        mSensorManager.unregisterListener(new MySensorEventListener());
        super.onPause();
    }
  
    // 计算方向
    private void calculateOrientation() {
        float[] values = new float[3];
        float[] R = new float[9];
        SensorManager.getRotationMatrix(R, null, accelerometerValues,
                magneticFieldValues);
        SensorManager.getOrientation(R, values);
        values[0] = (float) Math.toDegrees(values[0]);
  
        Log.i(TAG, values[0] + "");
        if (values[0] >= -5 && values[0] < 5) {
            azimuthAngle.setText("正北");
        } else if (values[0] >= 5 && values[0] < 85) {
            // Log.i(TAG, "东北");
            azimuthAngle.setText("东北");
        } else if (values[0] >= 85 && values[0] <= 95) {
            // Log.i(TAG, "正东");
            azimuthAngle.setText("正东");
        } else if (values[0] >= 95 && values[0] < 175) {
            // Log.i(TAG, "东南");
            azimuthAngle.setText("东南");
        } else if ((values[0] >= 175 && values[0] <= 180)
                || (values[0]) >= -180 && values[0] < -175) {
            // Log.i(TAG, "正南");
            azimuthAngle.setText("正南");
        } else if (values[0] >= -175 && values[0] < -95) {
            // Log.i(TAG, "西南");
            azimuthAngle.setText("西南");
        } else if (values[0] >= -95 && values[0] < -85) {
            // Log.i(TAG, "正西");
            azimuthAngle.setText("正西");
        } else if (values[0] >= -85 && values[0] < -5) {
            // Log.i(TAG, "西北");
            azimuthAngle.setText("西北");
        }
    }
  
    class MySensorEventListener implements SensorEventListener {
        @Override
        public void onSensorChanged(SensorEvent event) {
            // TODO Auto-generated method stub
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                accelerometerValues = event.values;
            }
            if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
                magneticFieldValues = event.values;
            }
            calculateOrientation();
        }
  
        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
            // TODO Auto-generated method stub
  
        }
  
    }
  
}

加速度传感器在Android横竖屏切换中的应用

  • 通过监听加速度传感器,实时获得X、Y、Z三个方向的加速度值;当4(XX + YY)>=Z*Z时,开始计算设备在X与Y平面上的旋转角度;最后根据旋转角度计算出设备的横竖屏状态。
/**
* 在onResume中,向SensorManager注册监听加速度传感器
*/
@Override
protected void onResume() {
    super.onResume();
    orientationListener = new OrientationListener(this);
    orientationListener.enable();
}

public void enable() {
    if (mSensor == null) {
        Log.w(TAG, "Cannot detect sensors. Not enabled");
        return;
    }
    if (mEnabled == false) {
        if (localLOGV) Log.d(TAG, "OrientationEventListener enabled");
        mSensorManager.registerListener(mSensorEventListener, mSensor, mRate);
        mEnabled = true;
    }
}
``
```java
class SensorEventListenerImpl implements SensorEventListener {
    private static final int _DATA_X = 0;
    private static final int _DATA_Y = 1;
    private static final int _DATA_Z = 2;

    /**
    ** 通过X、Y、Z三个方向的加速度值的变化,计算出设备旋转的角度。
    **/
    public void onSensorChanged(SensorEvent event) {
        float[] values = event.values;
        int orientation = ORIENTATION_UNKNOWN;
        float X = -values[_DATA_X];
        float Y = -values[_DATA_Y];
        float Z = -values[_DATA_Z];        
        float magnitude = X*X + Y*Y;
        // Don't trust the angle if the magnitude is small compared to the y value
        if (magnitude * 4 >= Z*Z) {
            float OneEightyOverPi = 57.29577957855f;
            float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
            orientation = 90 - (int)Math.round(angle);
            // normalize to 0 - 359 range
            while (orientation >= 360) {
                orientation -= 360;
            } 
            while (orientation < 0) {
                orientation += 360;
            }
        }
        if (mOldListener != null) {
            mOldListener.onSensorChanged(Sensor.TYPE_ACCELEROMETER, event.values);
        }
        if (orientation != mOrientation) {
            mOrientation = orientation;
            onOrientationChanged(orientation);
        }
    }
}
public class OrientationListener extends OrientationEventListener {
    private int degree = 0;//旋转角度
    private int mOrientation = 0;//2—横屏 1-竖屏,0-未知
    public OrientationListener(Context context) {
        super(context);
    }
    /**
    * 根据旋转的角度,得出设备横竖屏状态
    **/
    @Override
    public void onOrientationChanged(int orientation) {
        degree = orientation;
        if (degree > 0 && degree < 45) {
            mOrientation = 1;
        } else if (degree > 45 && degree < 135) {
            mOrientation = 2;
        } else if (degree > 135 && degree < 225) {
            mOrientation = 1;
        } else if (degree > 225 && degree < 315) {
            mOrientation = 2;
        } else if (degree > 315 && degree < 360) {
            mOrientation = 1;
        } 
        if (mOrientation == 2) {
            Log.i("OrientationListener ", "横屏");
        } else if (mOrientation == 1) {
            Log.i("OrientationListener ", "竖屏");
        }
    }
}

特别说明:Math.atan2(-Y, X) 是计算从原点(0,0)到(x,y)点的线段与x轴正方向之间的平面角度(弧度值),
float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi得到的是原点(0,0)到(x,y)点的线段与x轴正方向之间的角度,90 - (int)Math.round(angle)为设备旋转的角度。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-29 11:46:00  更:2021-07-29 11:46:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 13:20:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码