Android横竖屏切换小结

Android横竖屏切换小结

(老样子,图片啥的详细文档,能够下载后观看 http://files.cnblogs.com/franksunny/635350788930000000.pdfhtml

Android手机或平板都会存在横竖屏切换的功能,一般是由物理重力感应触发的,可是有时候也不尽然,一般在设置里面咱们能够对手机的横竖屏切换进行关闭,操做界面以下java

 

只须要点击下“屏幕旋转”按钮就能够关闭横竖屏切换了。android

1、禁止APP内横竖屏切换

上述设置更改的是整个手机的横竖屏切换,当手机没有关闭横竖屏切换功能时,系统一旦触发横竖屏切换,缺省状态下,当前活动的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)

2、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要被调用到也是须要条件的,在这里,只给代码,不作讨论,后面再给出一个相关的补充说明。

3、重启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(); 

    } 

    ... 

}

4、非重启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操做,在这里就很少展开了。

5、对于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

结合网上的整理,小结跟这几配置相关的情景:

一、不设置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吧)。

6、对于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

相关文章
相关标签/搜索