Android开发横竖屏切换

平时在使用手机过程中遇到过手机屏幕横竖切换的现象,当时感觉很简单,不过在具体的实际开发中还是遇到了点麻烦,不过在查阅了官网和浏览了网页后,顺利把问题解决了,先把成果分享给大家。

横竖屏切换大体可以分为如下几种:

一、禁止APP内横竖屏切换的现象。

我们的手机系统内置了横竖屏切换的功能,当我们没有关闭此功能时,系统一旦触发横竖屏切换,在缺省值的情况下,当前活动的APP界面就会进行相应的横竖屏切换效果,但是当我们对此功能进行了相应的设置,屏幕只能显示规定的效果了。设置很简单,只需要在项目清单AndroidManifest.xml对需要设置的Activity对象设置android:screenOrientation属性值就可以。很多软件在设计和开发中为了避免横竖屏切换时引发不必要的麻烦,通常需要让App禁止掉横竖屏的切换,就是这样实现的。

如果设置成android:screenOrientation="portrait",屏幕只能竖屏显示,无法通过手机的旋转改变显示效果,同理,如果设置成android:screenOrientation="landscape",屏幕也只能横屏显示,也无法通过手机的旋转改变显示效果。

android:screenOrientation属性还有如下几个参数:

"unspecified":默认值,由系统来判断显示方向。判定的策略是和设备相关的,所以不同的设备会有不同的显示方向。

"user":用户当前首选的方向。

"behind":和该Activity下面的那个Activity的方向一致(在Activity堆栈中的)。

"sensor":由物理的感应器来决定的,如果用户旋转设备屏幕就会进行横竖屏切换。

"nosensor":忽略物理感应器,这样就不会随着用户旋转设备而更改了("unspecified"设置除外)。

二、手动触发APP的横竖屏切换。

当android:screenOrientation设置成"unspecified"或"sensor"等时,就会由系统根据设备的旋转情况来触发横竖屏的切换,如果想手动切换,系统为我们提供了setRequestedOrientation接口,大家可以在Activity布局界面上放置两个Button按钮,其中一个按钮的点击事件里实现横屏效果,就可以设置成setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);其中一个按钮的点击事件里实现竖屏效果,就可以设置成setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);代码很简单,在此就不给大家展示了。

三、通过重启Activity实现横竖屏的切换效果。

在缺省状态下,Activity每次横竖屏切换(包括用setRequestedOrientation调用)都会重新调用一轮onPause-> onStop-> onDestory-> onCreate->onStart->onResume操作,从而销毁原来的Activity对象,创建新的Activity对象,这是因为通常情况下软件在横竖屏之间切换,界面的高宽会发生转换,从而可能会要求不同的布局。具体的布局切换可以通过如下两种方法来实现:

1.在res目录下通过给layout文件使用限定符,根据资源适配原则调用相应的布局资源(类似于drawable),横竖屏切换时程序自己会调用Activity的onCreate方法,从而根据当前横竖屏情况自动加载响应的布局。

2.假如布局资源是不一样又不按照如上设置,则需要通过java代码来判断当前是横屏还是竖屏然后来加载相应的xml布局文件(比如mainP为竖屏mainL为横屏)。因为当屏幕变为横屏的时候,系统会重新呼叫当前Activity的onCreate方法,你可以把以下方法放在你的onCreate中来检查当前的方向,然后可以让你的setContentView来载入不同的layout xml。

@Override

protected void onCreate(Bundle savedInstanceState) {

 super.onCreate(savedInstanceState);

//获取当前屏幕的方向

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

 if ( mCurrentOrientation == Configuration.ORIENTATION_PORTRAIT ) {

     // 如果屏幕为竖屏时

     setContentView(R.layout.mainP);

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

     //如果屏幕为横屏时

     setContentView(R.layout.mainL);

 }

//初始化,赋值等操作

 init();

//设置控件的各种监听方法

 setListensers();

}

上面只是对布局切换做了描述,实际上由于重启Activity在未加处理的情况下必然导致数据的丢失和重新获取,这样用户体验会非常差。为此就要在切换前对数据进行保存,切换重启后对数据进行恢复,具体操作的步骤如下:

重写Activity.onRetainNonConfigurationInstance(),用户横竖屏切换前保存数据。在onCreate()函数中调用getLastNonConfigurationInstance(),获取onRetainNonConfigurationInstance()保存的数据。

四、不通过重启Activity实现横竖屏的切换效果。

通过onConfigurationChanged拦截横竖屏变换,从而进行必要的重新布局和切换操作。操作步骤如下:

首先,在项目清单文件AndroidManifest中为相应的Activity设置android:configChanges属性,从而让Activity不延续上述的重建流程,具体如下:

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

android:configChanges="orientation|keyboardHidden"

而Adnroid 3.2以后的SDK必须添加一个screenSize属性,具体如下

android:configChanges="keyboardHidden|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(),否则又初始化了,状态就丢失

    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(),否则又初始化了,状态就丢失

        setListensers();

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

        //当屏幕为横屏时

        setContentView(R.layout.mainL);

        //注意,这里删除了init(),否则又初始化了,状态就丢失

        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设置的补充

经过上面代码演示,我们可以看到具体实现涉及到了AndroidManifest工程配置里面具体Activity的screenOrientation和configChanges两个参数,这两个参数screenOrientation的优先级是高于configChanges,即假如screenOrientation设置为固定横竖屏时,那么configChanges参数无论怎么设置都没有办法引发横竖屏切换,除非在代码中手动调用setRequestedOrientation函数进行修改。关于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)






你可能感兴趣的:(Android开发)