Xamarin Getting Started翻译系列三--旋转处理

旋转处理

开发中对设备旋转的处理、注意事项、职责

 

概述

由于移动设备经常旋转,移动OS将旋转内置为标准的特性。

作为成熟的移动OS,Android为应用程序处理旋转提供了精致的框架,无论使用XML声明用户界面或使用代码创建用户界面。当旋转设备,在声明布局中,应用程序可以受益于框架对Android资源系统的紧密整合;而对于程序创建的布局,必须手动处理旋转,这样可以在程序运行时实现更好的控制,但开发者需要做更多工作。应用程序也可以选择不使用Activity的这种机制,重启并手动控制方向变化。

本文阐述如下主题:

l  声明式布局的旋转—如何使用Android资源系统创建方向敏感的应用程序,包括如何加载布局和针对特定方向的绘制。

l  程序创建式布局的旋转:如何编程加入控件,以及如何手动处理方向变化。

处理声明式布局的旋转

在遵守特定命名规范目录下添加文件,Android方向变化的时候会自动加载相应文件。包括:

l  布局资源—指定针对各个方向的布局文件

l  绘制资源—指定各个方向加载的绘制资源(drawable)

布局资源

默认情况下,Resources/layout目录下的Android XML(AXML)文件是用于渲染Activity视图的。如果没有指定横向布局资源,这个目录下的资源同时用于竖向和横向布局。对于由默认项目模版创建的项目结构:


项目在Resources/layout目录下只创建了一个Main.axml文件。在Activity的OnCreate方法中调用时,使用Main.axml定义视图,按如下方式声明一个按钮:

<?xml version="1.0" encoding="utf-8"?>

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"

android:layout_width="fill_parent"android:layout_height="fill_parent">

<Button android:id="@+id/myButton"android:layout_width="fill_parent"android:layout_height="wrap_content"android:text="@string/hello"/>

</LinearLayout>

 

如果设备横向旋转,Activity的OnCreate方法会再次被调用,并再次加载同一个Main.axml,截图如下:


特定方向布局

 

在额外的布局目录(默认是纵向也可以显示的指定名称为layout-port,现在添加一个名为layout-land目录),当横向时应用程序无需代码干预就可以自动加载所需视图。

Main.axml定义如下:

<?xml version="1.0" encoding="utf-8"?>

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"

android:layout_height="fill_parent">

 

<TextView

 

android:text="This is portrait"android:layout_height="wrap_content" android:layout_width="fill_parent" />

</RelativeLayout>

 

如果项目中在layout-land目录中加入额外的Main.axml文件,当横向的时候,Android将重新加载这个Main.axml。在横向版本的Main.axml文件中加入如下代码(为了简化,这个XML与默认纵向版本相似,加入显示不同字符串的TextView):


 

<?xml version="1.0" encoding="utf-8"?>

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"

android:layout_height="fill_parent">

 

<TextView

 

android:text="This is landscape"android:layout_height="wrap_content" android:layout_width="fill_parent" />

</RelativeLayout>

 

为了测试新XML加载,运行代码并将设备从纵向旋转为横向,如下所示:


绘制资源(Drawable Resources)

 

在旋转的时候,Android以与布局资源相似的方式对待绘制资源。本例中,系统分别从Resources/drawable和Resources/drawable-land目录中获取资源。

例如,项目在Resources/drawable目录中包含一个叫做Mokey.png的图片,在XML文件中ImageView按如下方式引用drawable对象:


 

 

<ImageView android:layout_height="wrap_content"android:layout_width="wrap_content"android:src="@drawable/monkey"android:layout_centerVertical="true" android:layout_centerHorizontal="true" />

进一步假设Resources/drawable-land目录下的Monkey.png是不同版本的。与布局文件相同,当设备旋转,drawable对象也会按方向变化,如下图:


代码处理旋转

 

有时我们会在代码中定义布局。这种选择有多种原因,如技术限制,开发者偏好等。当我们使用代码添加控件,应用程序必须手动处理设备方向问题,如果使用XML资源将自动处理。

在代码中添加控件

 

要在代码中添加控件,应用程序需要执行如下步骤:

l  创建布局

l  设置布局参数

l  创建控件

l  设置控件布局参数

l  将控件添加到布局

l  设置布局为内容视图

 

例如,假设用户界面只有一个TextView控件,添加到RelativeLayout中,代码如下:

protectedoverride void OnCreate (Bundle bundle)

{

base.OnCreate(bundle);

//create a layout

varrl = new RelativeLayout (this);

//set layout parameters

varlayoutParams = new RelativeLayout.LayoutParams

(ViewGroup.LayoutParams.FillParent,ViewGroup.LayoutParams.FillParent);

rl.LayoutParameters= layoutParams;

//create TextView control

vartv = new TextView (this);

//set TextView's LayoutParameters tv.LayoutParameters = layoutParams; tv.Text ="Programmatic layout";

//add TextView to the layout rl.AddView (tv);

//set the layout as the content view

SetContentView (rl);

}

代码创建一个RelativeLayout类实例,并设置其LayoutParamters属性。LayoutParams类以Android特有的方式封装可重用的控件定位方法。布局实例创建后,就可以向其中加入控件了。控件也具有LayoutParameters属性,例如本例中的TextView。TextView创建后,加入到RelativeLayout,并设置RelativeLayout为内容视图,应用程序将显示TextView,见下图:


在代码中检测方向

 

如果应用程序在OnCreate方法中尝试根据方向加载不同的用户界面(会在每次设备旋转后发生),就必须检测方向,然后加载期望的用户界面。Android提供了一个叫做WindowManager的类,可通过WindowManager.DefaultDisplay.Rotation属性检测设备旋转方向。如下所示:

protectedoverride void OnCreate (Bundle bundle)

{

base.OnCreate(bundle);

// create alayout

 

var rl = newRelativeLayout (this);

// set layoutparameters

var layoutParams= new RelativeLayout.LayoutParams

(ViewGroup.LayoutParams.FillParent,ViewGroup.LayoutParams.FillParent);

rl.LayoutParameters= layoutParams;

// get theinitial orientation

var surfaceOrientation = WindowManager.DefaultDisplay.Rotation;

// create layoutbased upon orientation

RelativeLayout.LayoutParamstvLayoutParams;

if (surfaceOrientation== SurfaceOrientation.Rotation0 || surfaceOrientation

==SurfaceOrientation.Rotation180) {

tvLayoutParams =new RelativeLayout.LayoutParams

(ViewGroup.LayoutParams.FillParent,ViewGroup.LayoutParams.WrapContent);

} else {

tvLayoutParams =new RelativeLayout.LayoutParams

(ViewGroup.LayoutParams.FillParent,ViewGroup.LayoutParams.WrapContent);

tvLayoutParams.LeftMargin= 100;

tvLayoutParams.TopMargin= 100;

}

// createTextView control

vartv = new TextView (this); tv.LayoutParameters = tvLayoutParams; tv.Text ="Programmatic layout";

// add TextViewto the layout rl.AddView (tv);

// set thelayout as the content view

SetContentView(rl);

}

这段代码在旋转为横向时,设置TextView位于左上角100像素,切换到新布局时自动具有动画效果,如下所示:


防止Activity重启

 

上面小节中在Oncreate中处理设备旋转。应用程序也可以防止Activity在方向变化的时候重启(执行OnCreate),可按如下方式在ActivityAttribute设置ConfigurationChanges

[Activity (Label = "CodeLayoutActivity",ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation |Android.Content.PM.ConfigChanges.ScreenSize)]

现在当设备旋转,Activity不会重启。这时需要手动处理方向变化,Activity需要重写OnConfigurationChanged方法并根据传入参数Configuration检测方向,新实现的Activity类如下:

 

[Activity(Label = "CodeLayoutActivity",ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation|Android.Content.PM.ConfigChanges.ScreenSize)]

 

public classCodeLayoutActivity : Activity

 

{

 

TextView _tv;

 

RelativeLayout.LayoutParams_layoutParamsPortrait; RelativeLayout.LayoutParams _layoutParamsLandscape;

 

 

protectedoverride void OnCreate (Bundle bundle)

 

{

 

// create alayout

 

// set layoutparameters

 

// get theinitial orientation

 

 

// createportrait and landscape layout for the TextView

 

_layoutParamsPortrait= new RelativeLayout.LayoutParams

 

(ViewGroup.LayoutParams.FillParent,ViewGroup.LayoutParams.WrapContent);

 

 

_layoutParamsLandscape= new RelativeLayout.LayoutParams

 

(ViewGroup.LayoutParams.FillParent,ViewGroup.LayoutParams.WrapContent);

 

_layoutParamsLandscape.LeftMargin= 100;

 

_layoutParamsLandscape.TopMargin= 100;

 

 

_tv = newTextView (this);

 

 

if(surfaceOrientation == SurfaceOrientation.Rotation0 ||

 

surfaceOrientation== SurfaceOrientation.Rotation180) {

 

_tv.LayoutParameters= _layoutParamsPortrait;

 

} else {

 

_tv.LayoutParameters= _layoutParamsLandscape;

 

}

 

 

_tv.Text ="Programmatic layout";

 

rl.AddView(_tv); SetContentView (rl);

}

 

 

public overridevoid OnConfigurationChanged(Android.Content.Res.Configuration newConfig)

 

{

 

base.OnConfigurationChanged(newConfig);

 

 

if(newConfig.Orientation == Android.Content.Res.Orientation.Portrait) {

 

_tv.LayoutParameters= _layoutParamsPortrait;

 

_tv.Text ="Changed to portrait";

 

} else if (newConfig.Orientation ==Android.Content.Res.Orientation.Landscape) {

_tv.LayoutParameters= _layoutParamsLandscape;

 

_tv.Text= "Changed to landscape";

 

}

 

}

 

}

这里同时初始化TextView的横向和纵向布局参数。类变量的参数以及TextView本身,都不会在方向变化的时候重新创建。在OnCreate方法中还是使用surfaceOrientartion来设置TextView的初始布局。然后OnConfigurationChanged处理接下来的布局变化。

当运行应用程序,Android加载用户界面,设备方向变化时也不会重启Activity。

 

声明式布局防止Activity重启

如果我们使用XML定义布局,也可以避免设备旋转时Activity重启。例如,我们可以使用这种方法避免Activity重启(可能为了效率),对不同方向不必加载新资源。

为了实现这个目标,做与代码布局一样的过程。简单的在ActivityAttribute中设置ConfigurationChanges,同前面的CodeLayoutActivity范例。同样也需要在OnConfigurationChanged方法中处理方向变化的代码。

 

方向变化时维护状态

无论是声明式或编程式处理旋转,设备方向变化时Android应用程序需要同样的技术实现状态管理。管理状态是很重要的,因为Android设备旋转的时候系统重启了运行中的Activity。Android可以方便的加载不同的资源,并可以为不同方向设计不同的布局和drawable资源。当重启时,Activity会丢失存储在类成员中的临时状态。因此,如果Activity 是依赖于状态的,就必须从应用程序级持久化他的状态。应用程序如果要支持方向变化就必须处理保存和加载应用程序状态。

关于Android持久化状态的更多信息,见Activity Lifecycle

汇总

本文描述如何使用Android内置旋转支持。首先,讲述如何使用Android资源系统创建方向敏感应用程序。然后讲述如何在代码中添加控件,并手动处理方向变化。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值