Android 屏幕方向相關,setRequestedOrientation,OrientationEventListener

1 Activity 在AndroidManifest.xml設置方向android:screenOrientation

正向拿着手機(平時正常拿手機)對應角度爲0度,此時屏幕爲爲豎屏,旋轉180度,對應角度180度,此時屏幕爲反向豎屏;
90時此時屏幕爲橫屏(用戶右側(正向)橫屏拿着手機),屏幕角度爲270,此時屏幕爲反向橫屏。
Android可以利用android:screenOrientation控制activity啓動時方向,取值可以爲:
在這裏插入圖片描述
常見屬性解釋:
unspecified,默認值,由系統決定,不同手機可能不一致,設置了這個屬性會根據手機本身的傳感器方向變化。
landscape,強制橫屏顯示
portrait,強制豎屏顯
“reverselandscape” 與正常的橫向方向相反顯示。
“reverseportrait” 與正常的縱向方向相反顯示。
behind,與前一個activity方向相同
“sensor” 根據設備傳感器方向設置屏幕方向,當用戶旋轉設備時,顯示的方向會改變。但默認情況下,有些設備不會在所有的四個方向上都旋轉,因此要允許在所有的四個方向上都能旋轉,就要使用fullsensor屬性值。
“fullsensor” 顯示的方向(4個方向)是由設備的方向傳感器來決定的,除了它允許屏幕有4個顯示方向之外,其他與設置爲“sensor”時情況類似。
sensorLandscape,橫屏旋轉,一般橫屏遊戲會這樣設置
sensorPortrait,豎屏旋轉
nosensor,旋轉設備時候,界面不會跟着旋轉。初始化界面方向由系統控制
user,用戶當前設置的方向

引用總結(https://www.jianshu.com/p/dd53094b580a 感謝作者):
ActivityInfo屬性值 含義
SCREEN_ORIENTATION_UNSET
SCREEN_ORIENTATION_UNSPECIFIED 默認值,系統根據方向感應自動選擇屏幕方向
SCREEN_ORIENTATION_LANDSCAPE 正向橫屏,顯示的寬比高長(鎖死爲橫屏方向,不再讓方向感應起作用)
SCREEN_ORIENTATION_PORTRAIT 正向豎屏,顯示的高比寬長(鎖死爲豎屏方向,不再讓方向感應起作用)
SCREEN_ORIENTATION_USER 用戶當前首選的方向
SCREEN_ORIENTATION_BEHIND 這個有點奇葩,它跟Activity堆棧中的下面一個Activity的方向一致
SCREEN_ORIENTATION_SENSOR 由設備的方向傳感器決定,如果用戶旋轉設備,這屏幕就會橫豎屏切換(這裏要注意了:這個屬性有些強悍。大家知道安卓手機上都有一個“屏幕旋轉”按鈕,有的也叫“鎖定屏幕”隨便什麼名字不管了,這個設置裏面的開關和Activity有密切關係。關閉它之後Activity界面就不能響應方向傳感器了,打開它纔會恢復正常,挺合理的一個功能,讓決定權放在用戶手中。可一旦你設置了這個屬性,無論用戶怎麼設置自己的手機上的“屏幕旋轉”按鈕,打開也好,關閉也好,Activity界面都會響應方向傳感器的,會隨着用戶手持手機的方向自動變化,這就讓用戶有點奇怪。)
SCREEN_ORIENTATION_NOSENSOR 忽略物理方向傳感器,這樣就不會隨着用戶旋轉設備而橫豎屏切換了(這裏有個坑:如果用戶橫屏拿着手機進行播放,界面也是橫屏的,一旦設置了這個屬性之後,手機界面會先變換到豎屏,然後纔會鎖死方向傳感器)
SCREEN_ORIENTATION_SENSOR_LANDSCAPE 橫屏,和上面它“爸爸”SENSOR一樣強悍,無視用戶手機設置的“屏幕旋轉”按鈕開關,直接根據方向傳感器來切換正反向橫屏,但是不會切換到豎屏
SCREEN_ORIENTATION_SENSOR_PORTRAIT 豎屏,和上面它“爸爸”SENSOR一樣強悍,無視用戶手機設置的“屏幕旋轉”按鈕開關,直接根據方向傳感器來切換正反向豎屏,不會切換到橫屏
SCREEN_ORIENTATION_REVERSE_LANDSCAPE 反向橫屏,橫屏分正向橫屏(靠左手方向橫屏)和反向橫屏(靠右手方向橫屏)
SCREEN_ORIENTATION_REVERSE_PORTRAIT 反向豎屏,就是和正常豎着拿手機相反,豎着掉了個個
SCREEN_ORIENTATION_FULL_SENSOR
SCREEN_ORIENTATION_USER_LANDSCAPE
SCREEN_ORIENTATION_USER_PORTRAIT
SCREEN_ORIENTATION_FULL_USER
SCREEN_ORIENTATION_LOCKED 鎖死用戶當前屏幕,方向傳感器不生效(這裏也有一個坑,低版本不生效,如vivo4.1,你如果使用這個屬性來鎖定屏幕,它會直接變回到豎屏再鎖死)

對應角度
android:screenOrientation="landscape"爲90度(橫屏)
android:screenOrientation="reverseLandscape"爲270度(反向橫屏)
android:screenOrientation="sensorLandscape"爲90度和270度根據G-sensor切換(橫屏切換)
android:screenOrientation="portrait"爲0度(豎屏)
android:screenOrientation="reversePortrait"爲180度(反向豎屏)
android:screenOrientation=“sensorPortrait”爲0度和180度之間切換(豎屏切換)

android:configChanges

上面的屬性一般需要配合android:configChanges使用,如果不設置android:configChanges,在activity頁面配置修改時,activity會被重新創建,配置了對應的android:configChanges,activity不會被重建,而是會調用Activity 的onConfigurationChanged()方法。
在這裏插入圖片描述
其中keyboard,keyboardHidden,orientation,screenSize,fontScale等都是常用屬性。
android:configChanges=“orientation|keyboard|mcc|mnc|locale|keyboardHidden|uiMode|fontScale|keyboardHidden|screenSize”

說明:
設置了android:screenOrientation=“portrait”,或者landscape等相關屬性(和setRequestedOrientation 設置效果相同),onConfigurationChanged屬性不會被調用。

設置爲sensor時,會跟隨傳感器方向改變activity方向,onConfigurationChanged被調用,
/**
* Overall orientation of the screen. May be one of
* {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.
*/
public int orientation;

注意:設置了sensor後不是設備的每個方向都支持,類似pixel 2XL 反向豎屏就不支持,需要fullSensor屬性才支持。

2 setRequestedOrientation 設置屏幕方向

 /**
     * Change the desired orientation of this activity.  If the activity
     * is currently in the foreground or otherwise impacting the screen
     * orientation, the screen will immediately be changed (possibly causing
     * the activity to be restarted). Otherwise, this will be used the next
     * time the activity is visible.
     *
     * @param requestedOrientation An orientation constant as used in
     * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
     */
    public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
        if (mParent == null) {
            try {
                ActivityManager.getService().setRequestedOrientation(
                        mToken, requestedOrientation);
            } catch (RemoteException e) {
                // Empty
            }
        } else {
            mParent.setRequestedOrientation(requestedOrientation);
        }
    }

**所有取值**
  /** @hide */
    @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
            SCREEN_ORIENTATION_UNSET,
            SCREEN_ORIENTATION_UNSPECIFIED,
            SCREEN_ORIENTATION_LANDSCAPE,
            SCREEN_ORIENTATION_PORTRAIT,
            SCREEN_ORIENTATION_USER,
            SCREEN_ORIENTATION_BEHIND,
            SCREEN_ORIENTATION_SENSOR,
            SCREEN_ORIENTATION_NOSENSOR,
            SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
            SCREEN_ORIENTATION_SENSOR_PORTRAIT,
            SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
            SCREEN_ORIENTATION_REVERSE_PORTRAIT,
            SCREEN_ORIENTATION_FULL_SENSOR,
            SCREEN_ORIENTATION_USER_LANDSCAPE,
            SCREEN_ORIENTATION_USER_PORTRAIT,
            SCREEN_ORIENTATION_FULL_USER,
            SCREEN_ORIENTATION_LOCKED
    })

使用:

 <activity android:name=".activity.Main16Activity"
            android:configChanges="orientation|keyboard|mcc|mnc|locale|keyboardHidden|uiMode|fontScale|keyboardHidden|screenSize"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


public class Main16Activity extends AppCompatActivity {

    private Button btn1;
    private Button btn2;
    private Button btn3;
    private Button btn4;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main16);
        btn1 = findViewById(R.id.btn_portrait);
        btn2 = findViewById(R.id.btn_revert_portrait);
        btn3 = findViewById(R.id.btn_landscape);
        btn4 = findViewById(R.id.btn_revert_landscape);
        btn1.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        });
        btn2.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        });
        btn3.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        });
        btn4.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        });
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        System.out.println("=========orientation=========="+newConfig.orientation);
    }
}

得到的結果可能每個人測試的有所不同,這時因爲手機有一個方向鎖定的功能,或者是自動旋轉,叫法可能不同,
小米:
在這裏插入圖片描述
pixel
在這裏插入圖片描述
上面小米和pixel手機的開關是相反的,我們根據小米手機的文字來說明,當選中方向鎖定時,此時方向會被鎖定。方向鎖定分兩種,手機橫屏時方向被鎖定,手機豎屏時方向被鎖定。方向被鎖定之後,再次使用setRequestedOrientation時,不同手機可能表現有所不同。

pixel未被鎖定時,setRequestedOrientation四個方向都可以設置,都有效。
pixel豎屏時被鎖定(0)和pixel橫屏時被鎖定(180)效果相同,setRequestedOrientation REVERSE_PORTRAIT 和Portrait 作用相同,其他有效。
在這裏插入圖片描述

在這裏插入圖片描述

小米8 未被鎖定時和豎屏鎖定時(0),setRequestedOrientation REVERSE_PORTRAIT 和Portrait 作用相同,其他有效。
小米8橫屏時被鎖定(180),setRequestedOrientation 橫屏 和反向橫屏作用相同,豎屏和反向豎屏作用相同。
小米8就無法直接從豎屏轉換到反向豎屏。

屏幕方向鎖定獲取:

Settings.System.getInt( context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION );
結果爲1時沒有被鎖定,也就是開啓了自動旋轉。

3 獲取屏幕旋轉角度OrientationEventListener

調用this.getWindowManager().getDefaultDisplay().getRotation();
該函數的返回值,有如下四種:
Surface.ROTATION_0,Surface.ROTATION_90,Surface.ROTATION_180,Surface.ROTATION_270 代表了屏幕的方向。

其中,Surface.ROTATION_0 表示的是手機豎屏方向向上,後面幾個以此爲基準依次以順時針90度遞增。
設置了screenOrientation爲sensor後,屏幕可以隨着手機方向改變而改變,但當設置了屏幕方向爲橫屏或者豎屏之後,屏幕就不會再響應屏幕方向的改變,因爲setRequestedOrientation和android:screenOrientation="portrait"效果相同,設置之後只能是橫屏或者豎屏。

系統提供了OrientationEventListener用來監聽屏幕旋轉的角度,原理是利用加速度傳感器。
使用方法:

public class Main16Activity extends AppCompatActivity {

    private Button btn1;
    private Button btn2;
    private Button btn3;
    private Button btn4;
    private Button btn5;
    private int mCurrentOrientation;

    OrientationEventListener mOrientationListener ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main16);
        btn1 = findViewById(R.id.btn_portrait);
        btn2 = findViewById(R.id.btn_revert_portrait);
        btn3 = findViewById(R.id.btn_landscape);
        btn4 = findViewById(R.id.btn_revert_landscape);
        btn5 = findViewById(R.id.btn_5);
        btn1.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        });
        btn2.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
        });
        btn3.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        });
        btn4.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
        });

        btn5.setOnClickListener(view -> {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
        });

        //簡單判斷和操作,詳細的可用方法需要結合方向鎖定,activity變化之前的方向
        mOrientationListener = new OrientationEventListener(this) {
            @Override
            public void onOrientationChanged(int degree) {
                //此處依據degress判斷橫豎屏,可以根據自己的需要進行判斷是橫屏還是豎屏。

               // System.out.println("=========degree=============="+degree);

                //手機平放時,檢測不到有效的角度
                if ( degree == OrientationEventListener.ORIENTATION_UNKNOWN ) {
                    return;
                }

                if ( degree >= 350 || degree <= 20 ) {
                    //0度,用戶豎直拿着手機
                    mCurrentOrientation = 0;
                    System.out.println("=========0=============="+degree);
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                } else if ( degree >= 70 && degree <= 110 ) {
                    //90度,用戶右側橫屏拿着手機
                    mCurrentOrientation = 90;
                    System.out.println("=========90=============="+degree);
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                } else if ( degree >= 160 && degree <= 200 ) {
                    //180度,用戶反向豎直拿着手機
                    mCurrentOrientation = 180;
                    System.out.println("=========180=============="+degree);
                    //很多手機不起作用,類似小米8
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                } else if ( degree >= 250 && degree <= 290 ) {
                    //270度,用戶左側橫屏拿着手機
                    mCurrentOrientation = 270;
                    System.out.println("=========270=============="+degree);
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                }else{
                    return;
                }

            }
        };

        if (mOrientationListener.canDetectOrientation()) {//判斷設備是否支持
            mOrientationListener.enable();
        } else {
            mOrientationListener.disable();//註銷
            //當前設備不支持手機旋轉!
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        System.out.println("=========orientation=========="+newConfig.orientation);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mOrientationListener != null){
            mOrientationListener.disable();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mOrientationListener != null){
            mOrientationListener.enable();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mOrientationListener != null){
            mOrientationListener.disable();
        }
    }

    private boolean isLock(){
        try {
            int flag = Settings.System.getInt( Main16Activity.this.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION );
            if (flag == 1)return false;
            return true;
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
            return false;
        }
    }
}

android 屏幕方向隨傳感器變化,並帶有切換大屏,小屏和鎖定屏幕方向

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章