회전 처리

이 항목에서는 Xamarin.Android에서 디바이스 방향 변경을 처리하는 방법에 대해 설명합니다. Android 리소스 시스템을 사용하여 특정 디바이스 방향에 대한 리소스를 자동으로 로드하는 방법과 방향 변경을 프로그래밍 방식으로 처리하는 방법에 대해 설명합니다.

개요

모바일 디바이스는 쉽게 회전하기 때문에 기본 제공 회전은 모바일 OS의 표준 기능입니다. Android는 사용자 인터페이스가 XML에서 선언적으로 만들어지든 프로그래밍 방식으로 코드에서 생성되든 관계없이 애플리케이션 내에서 회전을 처리하기 위한 정교한 프레임워크를 제공합니다. 회전된 디바이스에서 선언적 레이아웃 변경을 자동으로 처리하는 경우 애플리케이션은 Android 리소스 시스템과 긴밀한 통합을 활용할 수 있습니다. 프로그래밍 방식 레이아웃의 경우 변경 내용을 수동으로 처리해야 합니다. 이렇게 하면 런타임에 더 세밀하게 제어할 수 있지만 개발자에게 더 많은 작업이 필요합니다. 또한 애플리케이션은 활동 다시 시작을 옵트아웃하고 방향 변경을 수동으로 제어하도록 선택할 수 있습니다.

이 가이드에서는 다음 방향 항목을 살펴봅니다.

  • 선언적 레이아웃 회전 – Android 리소스 시스템을 사용하여 특정 방향에 대한 레이아웃과 그리기 개체를 모두 로드하는 방법을 포함하여 방향 인식 애플리케이션을 빌드하는 방법입니다.

  • 프로그래밍 방식 레이아웃 회전 – 프로그래밍 방식으로 컨트롤을 추가하는 방법과 방향 변경을 수동으로 처리하는 방법입니다.

레이아웃을 사용하여 선언적으로 회전 처리

명명 규칙을 따르는 폴더에 파일을 포함하면 방향이 변경되면 Android에서 자동으로 적절한 파일을 로드합니다. 여기에는 다음에 대한 지원이 포함됩니다.

  • 레이아웃 리소스 – 각 방향에 대해 확장되는 레이아웃 파일을 지정합니다.

  • 그리기 가능한 리소스 – 각 방향에 대해 로드되는 드로블 지정

레이아웃 리소스

기본적으로 리소스/레이아웃 폴더에 포함된 AXML(Android XML) 파일은 작업에 대한 보기를 렌더링하는 데 사용됩니다. 이 폴더의 리소스는 가로에 대해 특별히 추가 레이아웃 리소스가 제공되지 않는 경우 세로 방향과 가로 방향 모두에 사용됩니다. 기본 프로젝트 템플릿에서 만든 프로젝트 구조를 고려합니다.

Default project template structure

이 프로젝트는 Resources/layout 폴더에 단일 Main.axml 파일을 만듭니다. Activity의 OnCreate 메서드가 호출되면 Main.axml정의된 뷰가 확장됩니다. 이 뷰는 아래 XML에 표시된 대로 단추를 선언합니다.

<?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>

디바이스를 가로 방향으로 회전하는 경우 아래 스크린샷과 같이 활동의 OnCreate 메서드가 다시 호출되고 동일한 Main.axml 파일이 확장됩니다.

Same screen but in landscape orientation

방향별 레이아웃

레이아웃 폴더(기본값은 세로로 지정되고 명명된 폴더를 포함하여 명시적으로 레이아웃 포트로 명명layout-land될 수도 있음) 외에도 애플리케이션은 코드 변경 없이 가로에서 필요한 보기를 정의할 수 있습니다.

Main.axml 파일에 다음 XML이 포함되어 있다고 가정합니다.

<?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>

추가 Main.axml 파일이 포함된 layout-land라는 폴더가 프로젝트에 추가되면 가로에서 레이아웃을 확장하면 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 로드가 표시됩니다.

Portrait and landscape screenshots printing the portrait mode

그리기 가능한 리소스

회전하는 동안 Android는 레이아웃 리소스와 비슷하게 그리기 가능한 리소스를 처리합니다. 이 경우 시스템은 Resources/drawableResources/drawable-land 폴더에서 각각 드로블을 가져옵니다.

예를 들어 프로젝트에 resources/drawable 폴더에 Monkey.png 이미지가 포함되어 있다고 가정해 보겠습니다. 여기서 드로블은 다음과 같이 XML에서 ImageView 참조됩니다.

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

다른 버전의 Monkey.png Resources/drawable-land포함되어 있다고 가정해 보겠습니다. 레이아웃 파일과 마찬가지로 디바이스가 회전하면 아래와 같이 지정된 방향에 대한 그리기 가능 값이 변경됩니다.

Different version of Monkey.png shown in portrait and landscape modes

프로그래밍 방식으로 회전 처리

코드에서 레이아웃을 정의하는 경우도 있습니다. 이는 기술적 제한, 개발자 기본 설정 등을 비롯한 다양한 이유로 발생할 수 있습니다. 프로그래밍 방식으로 컨트롤을 추가할 때 애플리케이션은 XML 리소스를 사용할 때 자동으로 처리되는 디바이스 방향을 수동으로 고려해야 합니다.

코드에서 컨트롤 추가

프로그래밍 방식으로 컨트롤을 추가하려면 애플리케이션에서 다음 단계를 수행해야 합니다.

  • 레이아웃을 만듭니다.
  • 레이아웃 매개 변수를 설정합니다.
  • 컨트롤을 만듭니다.
  • 컨트롤 레이아웃 매개 변수를 설정합니다.
  • 레이아웃에 컨트롤을 추가합니다.
  • 레이아웃을 콘텐츠 보기로 설정합니다.

예를 들어 다음 코드와 같이 단일 TextView 컨트롤이 추가 RelativeLayout된 사용자 인터페이스를 고려해 보세요.

protected override void OnCreate (Bundle bundle)
{
  base.OnCreate (bundle);
                        
  // create a layout
  var rl = new RelativeLayout (this);

  // set layout parameters
  var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
  rl.LayoutParameters = layoutParams;
        
  // create TextView control
  var tv = 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 만들고 해당 LayoutParameters 속성을 설정합니다. 이 LayoutParams 클래스는 컨트롤을 재사용 가능한 방식으로 배치하는 방법을 캡슐화하는 Android의 방법입니다. 레이아웃의 인스턴스가 만들어지면 컨트롤을 만들고 추가할 수 있습니다. 컨트롤에는 LayoutParameters이 예제와 TextView 같은 컨트롤도 있습니다. TextView 만든 후에는 콘텐츠 뷰에 RelativeLayout 추가하고 콘텐츠 보기로 설정 RelativeLayout 하면 애플리케이션에서 다음과 같이 표시됩니다TextView.

Increment counter button shown in both portrait and landscape modes

코드에서 방향 검색

애플리케이션이 호출될 때 OnCreate 각 방향에 대해 다른 사용자 인터페이스를 로드하려고 하면(디바이스가 회전할 때마다 발생함) 방향을 검색한 다음 원하는 사용자 인터페이스 코드를 로드해야 합니다. Android에는 아래와 같이 속성을 통해 WindowManager.DefaultDisplay.Rotation 현재 디바이스 회전을 결정하는 데 사용할 수 있는 클래스가 WindowManager있습니다.

protected override void OnCreate (Bundle bundle)
{
  base.OnCreate (bundle);
                        
  // create a layout
  var rl = new RelativeLayout (this);

  // set layout parameters
  var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
  rl.LayoutParameters = layoutParams;
                        
  // get the initial orientation
  var surfaceOrientation = WindowManager.DefaultDisplay.Rotation;
  // create layout based upon orientation
  RelativeLayout.LayoutParams tvLayoutParams;
                
  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;
  }
                        
  // create TextView control
  var tv = new TextView (this);
  tv.LayoutParameters = tvLayoutParams;
  tv.Text = "Programmatic layout";
        
  // add TextView to the layout
  rl.AddView (tv);
        
  // set the layout as the content view
  SetContentView (rl);
}

이 코드는 화면 왼쪽 위에서 100픽셀의 위치를 설정 TextView 하며, 다음과 같이 가로로 회전할 때 자동으로 새 레이아웃에 애니메이션 효과를 줍니다.

View state is preserved across portrait and landscape modes

활동 다시 시작 방지

애플리케이션은 OnCreate모든 항목을 처리하는 것 외에도 다음과 같이 설정 ConfigurationChangesActivityAttribute 하여 방향이 변경될 때 활동이 다시 시작되지 않도록 할 수 있습니다.

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

이제 디바이스가 회전되면 활동이 다시 시작되지 않습니다. 이 경우 방향 변경을 수동으로 처리하기 위해 작업은 아래 작업의 새 구현과 같이 메서드를 재정 OnConfigurationChanged 의하고 전달된 개체에서 Configuration 방향을 결정할 수 있습니다.

[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
public class CodeLayoutActivity : Activity
{
  TextView _tv;
  RelativeLayout.LayoutParams _layoutParamsPortrait;
  RelativeLayout.LayoutParams _layoutParamsLandscape;
                
  protected override void OnCreate (Bundle bundle)
  {
    // create a layout
    // set layout parameters
    // get the initial orientation

    // create portrait 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 = new TextView (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 override void 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's 여기서 레이아웃 매개 변수는 가로 및 세로 모두에 대해 초기화됩니다. 클래스 변수는 방향이 변경될 때 활동이 다시 생성되지 않으므로 자체와 TextView 함께 매개 변수를 보유합니다. 코드는 여전히 in OnCreatesurfaceOrientartion 사용하여 에 대한 초기 레이아웃을 TextView설정합니다. 그런 다음 모든 OnConfigurationChanged 후속 레이아웃 변경 내용을 처리합니다.

애플리케이션을 실행할 때 Android는 디바이스 회전이 발생할 때 사용자 인터페이스 변경 내용을 로드하고 작업을 다시 시작하지 않습니다.

선언적 레이아웃에 대한 활동 다시 시작 방지

XML에서 레이아웃을 정의하는 경우 디바이스 회전으로 인한 작업 다시 시작을 방지할 수도 있습니다. 예를 들어 작업 다시 시작을 방지하고(성능상의 이유로) 다른 방향으로 새 리소스를 로드할 필요가 없는 경우 이 방법을 사용할 수 있습니다.

이렇게 하려면 프로그래밍 방식 레이아웃과 함께 사용하는 것과 동일한 절차를 따릅니다. 앞에서 했던 CodeLayoutActivity 것처럼 간단히 설정합니다.ConfigurationChangesActivityAttribute 방향 변경에 대해 실행해야 하는 모든 코드는 메서드에서 OnConfigurationChanged 다시 구현할 수 있습니다.

방향 변경 중 상태 유지 관리

회전을 선언적으로 처리하든 프로그래밍 방식으로 처리하든 모든 Android 애플리케이션은 디바이스 방향이 변경되면 상태를 관리하기 위한 동일한 기술을 구현해야 합니다. Android 디바이스가 회전될 때 시스템이 실행 중인 작업을 다시 시작하기 때문에 상태 관리가 중요합니다. Android는 특정 방향을 위해 특별히 설계된 레이아웃 및 그리기 개체와 같은 대체 리소스를 쉽게 로드할 수 있도록 이 작업을 수행합니다. 다시 시작하면 작업이 로컬 클래스 변수에 저장되었을 수 있는 일시적 상태가 손실됩니다. 따라서 활동이 상태에 의존하는 경우 애플리케이션 수준에서 상태를 유지해야 합니다. 애플리케이션은 방향 변경 내용 간에 유지하려는 애플리케이션 상태를 저장하고 복원하는 작업을 처리해야 합니다.

Android에서 상태를 유지하는 방법에 대한 자세한 내용은 활동 수명 주기 가이드를 참조하세요.

요약

이 문서에서는 Android의 기본 제공 기능을 사용하여 회전 작업을 하는 방법을 설명했습니다. 먼저 Android 리소스 시스템을 사용하여 방향 인식 애플리케이션을 만드는 방법을 설명했습니다. 그런 다음 코드에 컨트롤을 추가하는 방법과 방향 변경을 수동으로 처리하는 방법을 제시했습니다.