Androidのセンサーを使って端末の向き(縦横)を判定する

spirit level

photo credit: Heartlover1717 via photopin cc

 

Android端末のセンサーを使って端末の向きを判定する処理を考える機会があったので記事にしてみました。

通常アプリで画面の回転をハンドリングするにはActivity#onConfigurationChange()などを使用すると思います。しかし、このコールバックは画面の向きをscreenOrientationなどで固定している場合には呼ばれません(画面の向きが変化ないので呼ばれないのは当然ですよね)。

そのため、画面の表示上は縦や横に固定にしたままの状態で端末の向き(画面の向きではない)を判定したい場合、独自に判定処理を実装する必要があります。

 

今回私が行った手順は以下のとおりです。

  1. センサーを使用して端末の傾きを取得する方法を調べる
  2. Android標準の画面回転の動作から縦横判定に使用する閾値を調べる
  3. (本題)センサーで取得した値と閾値から端末の向きを判定するロジックを作る

 

1.Androidのセンサーを使用して端末の傾きを取得する方法を調べる

センサーを使用して端末の傾きを取得する方法は多くのサイトで解説されていますが、TechBooster:センサを使ってAndroid端末の傾きを知るのサンプルコードを参考にすることにしました。

このサンプルを用いることで以下の値をリアルタイムに画面に表示することが可能になります。

  • azimuth:端末を垂直に立てた状態から縦軸を中心に回転させた際の角度
  • pitch:端末を垂直に立てた状態から前後に倒した際の角度
  • roll:端末を垂直に立てた状態から時計のように回転させた際の角度

 

2.Android標準の画面回転の動作から判定に使用する閾値を調べる

上で作成したサンプルアプリを起動しながら端末を傾けると以下の事がわかりました。

  • 端末を垂直に立てた状態(roll=0度)から時計回りに回転させて、rollが60度になった時点で画面が横に切り替わる。
  • 画面が真横の状態(roll=90度)から反時計回りに回転させて、rollが30度になった時点で画面が縦に切り替わる。

 

つまり、縦→横または横→縦に60度以上回転させると画面が切り替わる(45度で切り替えるのではなく60度にすることで過度な切り替えが行われないようになっている)。

 

3.(本題)センサーで取得した値と閾値をから端末の向きを判定するロジックを作る

作成したアプリの実行結果は以下のようになります。
アプリ画面を横固定となっており、その状態で端末を縦にすると表示の向きは横のまま判定結果が縦になっていることがわかります。

センサーの値が更新されるのに合わせて画面上の値もリアルタイムに更新されます。

SensorSampleResult

 

上記アプリのActivityのコードをgistにアップしました。

https://gist.github.com/smile0520/1425ed7cb064162fa20e

 

処理の概要をいかに示します。

  • 前回判定した時の端末の向きと現在の角度を元に現在の端末の向きを求める。
  • 端末を反時計回りに回した場合は値がマイナスとなるが、符号が反転しているだけなので角度の絶対値を使用して処理を共通化。
  • 90度を超えるる場合も縦横判定を行う。その際、角度の絶対値をマイナス90し、向きを反転させることで90度以下の場合と処理を共通化。

以下にコードの抜粋を示します。

int roll = (int) (orientationValues[2] * RAD2DEG);

// 以下、縦横判定処理
int absRoll = Math.abs(roll);
if (mPreOrientation == -1) {
    mPreOrientation = absRoll < VERTICAL_TO_HORIZONTAL_DEGREE ?
            ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL;
} else if (absRoll < 90) {
    mPreOrientation = getOrientation(mPreOrientation, roll);
} else {
    // プラマイ90度を超える場合は90度未満に置き換えて向きを反転させる。
    int plusMinus = roll >= 0 ? 1 : -1;
    roll = (absRoll - 90) * plusMinus;
    int preOrientation = invertOrientation(mPreOrientation);
    mPreOrientation = invertOrientation(getOrientation(preOrientation, roll));
}

// 表示の更新
orientationText.setText(mPreOrientation == ORIENTATION_VERTICAL ? "縦" : "横");

共通で使用するメソッド。

/**
 * 与えられた端末の向きを反転させます。
 *
 * @param orientation 端末の向き({@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL} or -1)
 * @return 反転した端末の向き({@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL} or -1)
 */
private int invertOrientation(int orientation) {
    if (orientation == ORIENTATION_HORIZONTAL) {
        return ORIENTATION_VERTICAL;
    } else if (orientation == ORIENTATION_VERTICAL) {
        return ORIENTATION_HORIZONTAL;
    } else {
        return -1;
    }
}

/**
 * 一つ前の端末の向きと現在の傾きを元に、現在の端末の向きを求めます
 *
 * @param preOrientation 一つ前の端末の向き({@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL} or -1)
 * @param roll           傾き(90度未満)
 * @return 現在の端末の向き({@link #ORIENTATION_HORIZONTAL} or {@link #ORIENTATION_VERTICAL} or -1)
 */
private int getOrientation(int preOrientation, int roll) {
    int absRoll = Math.abs(roll);
    if (preOrientation == ORIENTATION_VERTICAL) {
        return (absRoll < VERTICAL_TO_HORIZONTAL_DEGREE) ?
                ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL;
    } else if (preOrientation == ORIENTATION_HORIZONTAL) {
        return (absRoll < HORIZONTAL_TO_VERTICAL_DEGREE) ?
                ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL;
    } else {
        return -1;
    }
}

以上です。

  1. トラックバックはまだありません。

コメントを残す