Android—橫豎屏切換小結

Android橫豎屏切換小結

(老樣子,圖片啥的詳細文檔,可以下載後觀看 http://files.cnblogs.com/franksunny/635350788930000000.pdf

Android手機或平板都會存在橫豎屏切換的功能,通常是由物理重力感應觸發的,但是有時候也不盡然,通常在設置裏面我們可以對手機的橫豎屏切換進行關閉,操作界面如下

 

只需要點擊下“屏幕旋轉”按鈕就可以關閉橫豎屏切換了。

一、禁止APP內橫豎屏切換

上述設置更改的是整個手機的橫豎屏切換,當手機沒有關閉橫豎屏切換功能時,系統一旦觸發橫豎屏切換,缺省狀態下,當前活動的App的界面就會進行橫豎屏切換,由於橫豎屏的界面尺寸等參數不同,很多軟件在設計和開發中爲了避免橫豎屏切換時引發不必要的麻煩,通常需要讓App禁止掉橫豎屏的切換,這就需要通過在AndroidManifest.xml中設置activity中的android:screenOrientation屬性值來實現。

該android:screenOrientation屬性,他有以下幾個參數:

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

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

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

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

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

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

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

比如下列設置

android:screenOrientation="portrait"

則無論手機如何變動,擁有這個屬性的activity都將是豎屏顯示。

android:screenOrientation="landscape",爲橫屏顯示。

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

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)

二、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) {

                 // TODO Auto-generated method stub

                 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,其他的不用管,橫豎屏切換時程序自己會調用Activity的onCreate方法,從而根據當前橫豎屏情況自動加載響應的佈局。

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 ) {

     // If current screen is portrait

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

     setContentView(R.layout.mainP);

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

     //If current screen is 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"

關於configChanges的詳細描述,後面有個簡單補充章節,這裏不做過多展開。

其次,在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 ) {

        // If current screen is portrait

        setContentView(R.layout.mainP);

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

        findViews();

        setListensers();

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

        //If current screen is 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操作,在這裏就不多展開了。

五、對於AndroidManifest.xml設置的補充

經過上面代碼演示,我們可以看到具體實現涉及到了Manifest工程配置裏面具體Activity的screenOrientation和configChanges兩個參數,這兩個參數screenOrientation的優先級是高於configChanges,即假如screenOrientation設置爲固定橫豎屏時,那麼configChanges參數無論怎麼設都沒有辦法引發橫豎屏切換,除非在代碼中手動調用setRequestedOrientation函數進行修改。

screenOrientation屬性在前面已經講過了,而關於configChanges屬性設置有如下選項:

描述

mcc

IMSI移動臺的國家代碼(MCC)發生變化——一個SIM被探測到並且更新MCC

mnc

IMSI移動臺的網絡代碼(MNC)發生變化——一個SIM被探測到並且更新MNC

locale

區域發生變化——用戶選擇了一個文本需要顯示的新語言

touchscreen

觸摸屏發生變化。(這個通常不會發生。)

keyboard

鍵盤類型發生變化——例如:用戶插入了外接鍵盤。

keyboardHidden

鍵盤的可訪問性發生變化——例如:用戶發現了硬件鍵盤。

navigation

導航類型(軌跡球或dpad)發生變化。(通常不會發生。)

screenLayout

屏幕布局發生變化——這個會導致顯示不同的Activity。

fontScale

字體縮放因子發生變化——用戶選擇了新的字體大小。

uiMode

當UI模式發生改變的時候——當用戶放置設備到桌子或/汽車或夜間模式改變的時候可以引起UI模式變化。閱讀UiModeManager。在API級別8時引入。

orientation

屏幕方向發生變化——用戶旋轉了屏幕。注意:如果應用程序的目標API級別是13或更高(通過屬性minSdkVersion和屬性targetSdkVersion聲明),你也需要聲明配置項screenSize,因爲這將在設備選擇肖像和屏幕方向時發生改變。

screenSize

當前可用屏幕大小發生變化。這代表一個當前可用大小的變化,和當前的比率相關,因此當用戶選擇不同的畫面和圖像,會發生變化。然而,如果你的程序目標API級別是12或更低,你的Activity總是會自己處理這個配置變化(這個變化不會引起Activity的重啓,甚至在Android 3.2或更新的設備上)。在API級別13里加入的。

smallestScreenSize

物理屏幕大小的變化。不管方向的變化,僅僅在實際物理屏幕打包變化的時候,如:外接顯示器。這個配置項的變化引起在smallestWidth configuration裏的變化。然而,如果你的程序目標API級別是12或更低,你的Activity將自己處理這個變化(這個變化不會引起Activity的重啓,甚至在Android 3.2或更新的設備上)在API級別13里加入的。

layoutDirection

佈局方向變化。例如書寫方式從左向右(LTR)轉換爲從右向左(RTL)

 

從上述這個表我們可以看到除了橫豎屏,包括語言、網絡、鍵盤和外設等變化都可以被onConfigurationChanged函數監控到,具體的內容和釋義還是查看官方英文文檔吧,詳見如下鏈接

http://developer.android.com/guide/topics/manifest/activity-element.html

中文翻譯可以查閱 http://wiki.eoe.cn/page/Activity.html

結合網上的整理,小結跟這幾配置相關的情景:

1、不設置Activity的android:configChanges時,切屏會重新調用各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次(我在三星4.0設備上發現切橫屏和豎屏都是執行一次,而並非這裏說的有執行兩次的情況,不知道是否以前版本手機會這樣?);

2、設置Activity的android:configChanges="orientation"時,切屏還是會重新調用各個生命週期,切橫、豎屏時只會執行一次;

3、設置Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新調用各個生命週期,只會執行onConfigurationChanged方法。

注:上述描述是在Android3.2以前,如果缺少了keyboardHidden選項,不能防止Activity的銷燬重啓,也就不能執行onConfigurationChanged方法了。在3.2之後,必須加上screenSize屬性纔可以屏蔽調用Activity的生命週期(我在一些設備上親測可以不需要keyboardHidden,只要screenSize就可以了,但是保險起見還是繼續保留keyboardHidden吧)。

六、對於setRequestedOrientation函數的補充說明

在上述(二)對於手動觸發橫豎屏切換的時候,我們用到了setRequestedOrientation,那時只是簡單做了下演示,後來發現還是需要做下補充說明的:

首先在非重啓Activity模式下

手動調用setRequestedOrientation之後,假如會引發橫豎屏切換(即請求的橫豎屏要求與當前的橫豎屏情況不一致,就會引發切換),那麼會立即調用onConfigurationChanged函數;假如不會引發橫豎屏切換(請求前後一致),那麼也就不會調用到onConfigurationChanged函數。

這個手動調用setRequestedOrientation的地方也可以在Activity中的任何地方,即也可以在onConfigurationChanged中調用,但是一旦指定爲橫屏或豎屏完成這個變換之後,後面不論屏幕如何進行怎麼翻轉變化,都不會再觸發橫豎屏切換了,也即等同於在manifest中設置了android:screenOrientation屬性爲橫屏或豎屏。如果要恢復爲響應橫豎屏隨物理傳感器設備變換,那麼就需要手動調用類似如下代碼進行恢復:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

其次在重啓Activity模式下

手動調用setRequestedOrientation發出橫豎屏設定請求之後,假如需要進行橫豎屏切換(即請求前後橫豎屏狀態不一致),則會對Activity進行銷燬並重啓;假如不需要需要進行橫豎屏切換,則Activity維持現狀不變;

手動調用setRequestedOrientation一次,完成變換之後,也跟上面非重啓一樣,相當於在manifest中設置了android:screenOrientation屬性爲橫屏或豎屏。要想恢復也需要重新調用類似上面非重啓的調用。

在這樣一個原理下,就有了對如下一種需求的解決方案:

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

網上給出了一個例子代碼,這裏就不做摘抄了,有興趣可以試一下,然後對比一下人家的實現方式,具體見如下鏈接

http://blog.csdn.net/yimo29/article/details/6030445

 

另外再給出幾個我做整理時參考的帖子,覺得對我幫助很大,分別如下

Android橫屏豎屏切換的問題(一個總結帖,還是不錯的)

http://blog.sina.com.cn/s/blog_77c632410101790w.html

解決Android手機屏幕橫豎屏切換(一個真實測試過的小結)

http://www.cnblogs.com/zhangkai281/archive/2011/07/06/2099277.html

Android 處理橫豎屏切換事件

http://ipjmc.iteye.com/blog/1265991

轉載自:http://www.cnblogs.com/franksunny/p/3714442.html

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