Activity橫豎屏切換

默認情況下,當“屏幕方向”或“鍵盤顯示隱藏” 變化時都會銷燬當前Activity,創建新的Activity。

如果不希望重新創建Activity實例,可以按如下配置Activity:

<activity android:name=".MainActivity" android:configChanges="keyboardHidden|orientation">

注意Android3.2橫豎屏幕切換時以上配置configChanges要加上screenSize,要不還會調用onCreate

<activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|screenSize"/> 

上面的android:configChanges屬性指定了要捕獲“屏幕方向”和“鍵盤顯示隱藏”變化,

當捕獲到這些變化後會調用Activity的onConfigurationChanged()方法。


默認情況下(沒有配置android:configChanges屬性):

豎屏切橫屏,銷燬當前Activity之後,創建一個新Activity實例。

橫屏切豎屏,銷燬當前Activity之後,創建一個新Activity實例,新的Activity實例很快就被銷燬,接着又會創建一個新Activity實例。如果只希望創建一個實例,可以配置android:configChanges="orientation"


    <activity

        android:screenOrientation="portrait"   //豎屏

        android:screenOrientation="landscape"   //橫屏

    />    

上述修改也可以在Java代碼中通過類似如下代碼來設置

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)


android:screenOrientation屬性

他有以下幾個參數:


    "unspecified":默認值 由系統來判斷顯示方向.判定的策略是和設備相關的,所以不同的設備會有不同的顯示方向.

    "landscape":橫屏顯示(寬比高要長)

    "portrait":豎屏顯示(高比寬要長)

    "user":用戶當前首選的方向

    "behind":和該Activity下面的那個Activity的方向一致(在Activity堆棧中的)

    "sensor":有物理的感應器來決定。如果用戶旋轉設備這屏幕會橫豎屏切換。

    "nosensor":忽略物理感應器,這樣就不會隨着用戶旋轉設備而更改了("unspecified"設置除外)。



APP的橫豎屏切換手動觸發

    由上面描述可知,當android:screenOrientation爲默認值"unspecified"或"sensor"等時,就會有系統根據設備的旋轉情況來觸發橫豎屏的切換,那麼有沒有方法我們手動在程序中觸發橫豎屏的變換呢,顯然上面爲我們提供的setRequestedOrientation就是系統提供的一個入口,下面我們給出一個按鍵的方式來觸發的案列:

    

    public class MainActivity extends Activity implements OnClickListener {

        private Button mBtnLandscape;

        private Button mBtnPortrait;

        @Override

        protected void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);

            mBtnLandscape = (Button) findViewById(R.id.but_landscape);

            mBtnPortrait = (Button) findViewById(R.id.but_portrait);

            mBtnLandscape.setOnClickListener(this);

            mBtnPortrait.setOnClickListener(this);

        }

        @Override

        public void onClick(View v) {

            if(v == mBtnLandscape){

                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

            }else{

                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

            }

        }

        @Override

        public void onConfigurationChanged(Configuration newConfig) {

            super.onConfigurationChanged(newConfig);

            String message=newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE ? "屏幕設置爲:橫屏" : "屏幕設置爲:豎屏";

            Toast.makeText(this, message, Toast.LENGTH_LONG).show();

        }

    }

    

    需要注意的是,手動調用時,無視AndroidManifest中關於screenOrientation的設置;另外上述代碼中的onConfigurationChanged要被調用到也是需要條件的,在這裏,只給代碼,不做討論,後面再給出一個相關的補充說明。


重啓Activity的橫豎屏切換


    在上面的案列中,缺省狀態下,Activity每次橫豎屏切換(包括用setRequestedOrientation調用)都會重新調用一輪onPause-> onStop-> onDestory-> onCreate->onStart->onResume操作,從而銷燬原來的Activity對象,創建新的Activity對象,這是因爲通常情況下軟件在橫豎屏之間切換,界面的高寬會發生轉換,從而可能會要求不同的佈局。具體的佈局切換可以通過如下兩種方法來實現:

    

    1)在res目錄下建立layout-land和layout-port目錄,相應的layout文件名不變,比如都爲main.xml。layout-land是橫屏的layout,layout-port是豎屏的layout,其他的不用管,橫豎屏切換時模擬器自動尋找main.xml文件。

    

    2)假如佈局資源是不一樣又不按照如上設置,則需要通過java代碼來判斷當前是橫屏還是豎屏然後來加載相應的xml佈局文件(比如mainP爲豎屏mainL爲橫屏)。因爲當屏幕變爲橫屏的時候,系統會重新呼叫當前Activity的onCreate方法,你可以把以下方法放在你的onCreate中來檢查當前的方向,然後可以讓你的setContentView來載入不同的layout xml。

    

    @Override

    

    protected void onCreate(Bundle icicle) {

     super.onCreate(icicle);

     int mCurrentOrientation = getResources().getConfiguration().orientation;

     if ( mCurrentOrientation == Configuration.ORIENTATION_PORTRAIT ) {

         Log.i("info", "portrait"); // 豎屏

         setContentView(R.layout.mainP);

     } else if ( mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE ) {

         Log.i("info", "landscape"); // 橫屏

         setContentView(R.layout.mainL);

     }

     init();//初始化,賦值等操作

     findViews();//獲得控件

     setListensers();//設置控件的各種監聽方法

    }


    上面只是對佈局切換做了描述,實際上由於重啓Activity在未加處理的情況下必然導致數據的丟失和重新獲取,這樣用戶體驗會非常差。爲此就要在切換前對數據進行保存,切換重啓後對數據進行恢復,具體操作的步驟如下:

    

    重寫Activity.onRetainNonConfigurationInstance(),用戶橫豎屏切換前保存數據

    

    @Override 

    public Object onRetainNonConfigurationInstance() { 

        final MyDataObject data = collectMyLoadedData(); 

        return data; 

    }

    

    在onCreate()函數中調用getLastNonConfigurationInstance(),獲取onRetainNonConfigurationInstance()保存的數據

    

    @Override 

    

    public void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        setContentView(R.layout.main); 

     

        final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance(); 

        if (data == null) { 

            data = loadMyData(); 

        } 

        ... 

    }



非重啓Activity的橫豎屏切換


    雖然重啓Activity爲我們提供了保存數據和讀取數據的方式,但是如此一來程序會顯得有些繁瑣,所以有時候程序員往往就不想讓Activity重啓,Android也爲我們提供瞭解決方案,就是通過onConfigurationChanged攔截橫豎屏變換,從而進行必要的重新佈局和切換操作。操作步驟如下:

    首先,manifest中爲相應的Activity設置android:configChanges屬性,從而讓Activity不延續上述的重建流程,具體如下:

    Andorid 3.2以前的SDK可以使用如下配置

    android:configChanges="orientation|keyboardHidden"

    而Adnroid 3.2以後的SDK必須添加一個screenSize屬性,具體如下

    android:configChanges="keyboardHidden|orientation|screenSize"

    或者

    android:configChanges="orientation|screenSize"

    

    其次,在Activity或View的onConfigurationChanged(Configuration newConfig)函數中獲取當前橫豎屏參數。至於其調用順序跟touch事件的傳遞順序相似,不過他沒有消費事件的概念,會順次調用到每一個onConfigurationChanged函數。下面是重寫Activity的例子:

    

    //佈局分別在layout-land和layout-port目錄中的同名main.xml時

    

    @Override

    public void onConfigurationChanged (Configuration newConfig){

        super.onConfigurationChanged(newConfig);

        setContentView(R.layout.main);

        //注意,這裏刪除了init(),否則又初始化了,狀態就丟失

        findViews();

        setListensers();

    }

    

    //佈局爲不按照layout-land和layout-port目錄,而自定義名字時

    @Override

    public void onConfigurationChanged (Configuration newConfig){

        super.onConfigurationChanged(newConfig);

        int mCurrentOrientation = getResources().getConfiguration().orientation;

        if ( mCurrentOrientation == Configuration.ORIENTATION_PORTRAIT ) {

            setContentView(R.layout.mainP);

            //注意,這裏刪除了init(),否則又初始化了,狀態就丟失

            findViews();

            setListensers();

        } else if ( mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE ) {

            setContentView(R.layout.mainL);

            //注意,這裏刪除了init(),否則又初始化了,狀態就丟失

            findViews();

            setListensers();

        }

    }

    

    當然有時候連佈局都不用更改的話,就可以直接對原有控件進行調用操作了,比如:

    public class MainActivity extends Activity {

        private TextView textView;

        @Override

        public void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.main);

            Log.i("--Main--", "onCreate");

            textView=(TextView)findViewById(R.id.tv_id);

        }

    

        @Override

        public void onConfigurationChanged(Configuration newConfig) {

            super.onConfigurationChanged(newConfig);

            Log.i("--Main--", "onConfigurationChanged");

            if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){

                textView.setText("當前屏幕爲橫屏");

            }else{

                textView.setText("當前屏幕爲豎屏");

            }

        }   

    }

    

    需要注意的是,onConfigurationChanged函數中只能獲得橫豎屏切換後的參數,在該函數中獲取不到新的Layout和控件的尺寸位置信息,如果要處理尺寸和位置信息,必須通過消息異步或者延時調用,下面是一個App在橫豎屏切換時需要重新設置popupWindow位置的代碼:

    

    @Override

    protected void onConfigurationChanged(Configuration newConfig) {

        super.onConfigurationChanged(newConfig);

        //View中不用創建Handler,可直接調用post操作

        //new Handler().postDelayed(new Runnable() {

        //    @Override

        //    public void run() {

        //        updatePopup();    

        //    }

        //}, 500);


        postDelayed(new Runnable() {    

            @Override

            public void run() {

                updatePopup();      //

            }

        }, 500);//如果不在post中,而是直接調用,那麼彈出位置就會有問題

    }

    

    雖然上面沒有看到對佈局的顯式調用進行重新佈局,照理控件的對象沒有被銷燬,但是控件在橫豎屏切換時應該是需要進行重新layout和measure,然後再進行重繪的,否則不會引發彈出框位置的變化,至於如何調用重新layout、measure和Draw操作,在這裏就不多展開了。


自適應橫豎屏

如果想讓它啓動的時候是橫屏的話就橫屏表示,縱屏的話就縱屏表示,然後手機切換橫豎屏就不能用了該怎麼解決呢?


首先:在Mainfest.xml中追加

android:screenOrientation="sensor"android:configChanges="orientation|keyboardHidden"這兩個屬性。


第二步:取得屏幕的長和寬,進行比較設置橫豎屏的變量。


    Display display = getWindowManager().getDefaultDisplay();  

    int width = display.getWidth();  

    int height = display.getHeight();  

    if (width > height) {  

        orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;  

    } else {  

        orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;  

    }  


第三步:在onConfigurationChanged()函數中追加this.setRequestedOrientation(orientation)就行了


    @Override  

    public void onConfigurationChanged(Configuration newConfig) {  

        super.onConfigurationChanged(newConfig);  

        this.setRequestedOrientation(mOrientation);  

    }  


但是這樣的話你切到別的畫面的時候再回到原畫面,它就仍然是橫的或者是縱的。怎麼讓它從別的屏幕回來後,又重新橫豎屏佈局呢?


只要在OnResume()中在設定下就行了。但是這個只支持橫豎屏只有一個layout的。橫豎屏分別對應layout的還不知道該怎麼解決。


    @Override  

    protected void onResume() {  

        mOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;  

        this.setRequestedOrientation(mOrientation);  

        Display display = getWindowManager().getDefaultDisplay();  

        int width = display.getWidth();  

        int height = display.getHeight();  

        if (width > height) {  

            mOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;  

        } else {  

            mOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;  

        }  

          

        super.onResume();  

    }  


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