(老樣子,圖片啥的詳細文檔,能夠下載後觀看 http://files.cnblogs.com/franksunny/635350788930000000.pdf)html
Android手機或平板都會存在橫豎屏切換的功能,一般是由物理重力感應觸發的,可是有時候也不盡然,一般在設置裏面咱們能夠對手機的橫豎屏切換進行關閉,操做界面以下java
只須要點擊下「屏幕旋轉」按鈕就能夠關閉橫豎屏切換了。android
上述設置更改的是整個手機的橫豎屏切換,當手機沒有關閉橫豎屏切換功能時,系統一旦觸發橫豎屏切換,缺省狀態下,當前活動的App的界面就會進行橫豎屏切換,因爲橫豎屏的界面尺寸等參數不一樣,不少軟件在設計和開發中爲了不橫豎屏切換時引起沒必要要的麻煩,一般須要讓App禁止掉橫豎屏的切換,這就須要經過在AndroidManifest.xml中設置activity中的android:screenOrientation屬性值來實現。程序員
該android:screenOrientation屬性,他有如下幾個參數:網絡
"unspecified":默認值 由系統來判斷顯示方向.斷定的策略是和設備相關的,因此不一樣的設備會有不一樣的顯示方向.異步
"landscape":橫屏顯示(寬比高要長)ide
"portrait":豎屏顯示(高比寬要長)函數
"user":用戶當前首選的方向佈局
"behind":和該Activity下面的那個Activity的方向一致(在Activity堆棧中的)post
"sensor":有物理的感應器來決定。若是用戶旋轉設備這屏幕會橫豎屏切換。
"nosensor":忽略物理感應器,這樣就不會隨着用戶旋轉設備而更改了("unspecified"設置除外)。
好比下列設置
android:screenOrientation="portrait"
則不管手機如何變更,擁有這個屬性的activity都將是豎屏顯示。
android:screenOrientation="landscape",爲橫屏顯示。
上述修改也能夠在Java代碼中經過相似以下代碼來設置
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
由上面描述可知,當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每次橫豎屏切換(包括用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重啓,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操做,在這裏就很少展開了。
通過上面代碼演示,咱們能夠看到具體實現涉及到了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
結合網上的整理,小結跟這幾配置相關的情景:
一、不設置Activity的android:configChanges時,切屏會從新調用各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次(我在三星4.0設備上發現切橫屏和豎屏都是執行一次,而並不是這裏說的有執行兩次的狀況,不知道是否之前版本手機會這樣?);
二、設置Activity的android:configChanges="orientation"時,切屏仍是會從新調用各個生命週期,切橫、豎屏時只會執行一次;
三、設置Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會從新調用各個生命週期,只會執行onConfigurationChanged方法。
注:上述描述是在Android3.2之前,若是缺乏了keyboardHidden選項,不能防止Activity的銷燬重啓,也就不能執行onConfigurationChanged方法了。在3.2以後,必須加上screenSize屬性才能夠屏蔽調用Activity的生命週期(我在一些設備上親測能夠不須要keyboardHidden,只要screenSize就能夠了,可是保險起見仍是繼續保留keyboardHidden吧)。
在上述(二)對於手動觸發橫豎屏切換的時候,咱們用到了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 處理橫豎屏切換事件