Ориентация - Север!

Вступление
Узнать ориентацию программно
Установить ориентацию программно
Запрет на создание новой активности

Вступление

Если направить телефон на Север, то по умолчанию включится известная песня Лолиты (Шутка).

Когда создавались первые портативные устройства вроде КПК и смартфоны, то за основу бралась настольная операционная система и допиливалась под мобильное устройство. Лишние фукнции удалялись, а некоторые функции добавлялись. Но при этом как-то совсем упустили из виду, что в отличие от громоздких мониторов и экранов ноутбуков, карманные устройства можно вращать в руках. Поэтому первые телефоны не умели менять ориентацию экрана. Некоторые программисты самостоятельно стали создавать программы, которые умели переключаться в разные режимы. Затем эту возможность стали включать в настройки аппарата. А сравнительно недавно аппараты научились самостоятельно определять ориентацию экрана.

Всего существует два режима - портретный и альбомный. На большинстве телефонов используется по умолчанию портретный режим (как на паспорте). Альбомный режим знаком нам по обычным мониторам.

Как переключаться между режимами

Рассмотрим следующий случай. Предположим, у нас в приложении имеется одно текстовое поле и шесть кнопок. Вроде все нормально.

Но стоит нам повернуть устройство на 90 градусов (для эмулятора нужно нажать комбинацию клавиш Ctrl+F11), как сразу обнаруживаются проблемы. Пятая кнопка видна частично, а шестая вообще оказалась за пределами видимости. Непорядок!

Чтобы избежать такой проблемы необходимо как-то по другому скомпоновать кнопки. Например, расположить их не подряд друг за другом, а разбить на пары. Для этой цели воспользуемся контейнером TableLayout (Таблица). С её помощью мы можем разбить кнопки на две колонки и поместить их в три ряда.

Для этой операции нам понадобится сделать несколько важных шагов. Сначала нужно создать новую подпапкку в папке res. Для этого выделяем папку res, вызываем из него контекстное меню и последовательно выбираем команды New | Folder. В диалоговом окне даем название новой папке layout-land (это важно!). По суффиксу -land система понимает, что речь идет о новом режиме. Теперь нам осталось создать в созданной папке новый XML-файл main.xml. Для этого вызываем контекстное меню у папки layout-land и выбираем команды New | Other.... В открывшемся окне раскрываем папку Android и выбираем пункт Android XML file. Далее присваиваем ему имя main.xml. Именно здесь мы будем создавать новую компоновку приложения, которая будет выводиться при смене режима на альбомный. Скопируем весь код из первого файла main.xml, который находится в папке layout и модифицируем его следующим образом.

Показать код (щелкните мышкой)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <LinearLayout
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_gravity="center"
android:paddingLeft="20dip"
android:paddingRight="20dip">
 <EditText android:layout_height="wrap_content" android:id="@+id/editText1" android:layout_width="match_parent"></EditText>

<TableLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:stretchColumns="*">
<TableRow>
<Button android:id="@+id/button1" 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent" 
    android:text="@string/button1"></Button>
<Button android:id="@+id/button2" 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent" 
    android:text="@string/button2"></Button>
</TableRow>
<TableRow>
<Button 
android:id="@+id/button3" 
   android:layout_height="wrap_content" 
   android:layout_width="fill_parent" 
   android:text="@string/button3"></Button>
<Button 
    android:id="@+id/button4" 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent" 
    android:text="@string/button4"></Button>
</TableRow>
<TableRow>
<Button 
android:id="@+id/button5" 
   android:layout_height="wrap_content" 
   android:layout_width="fill_parent" 
   android:text="@string/button5"></Button>
<Button 
    android:id="@+id/button6" 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent" 
    android:text="@string/button6"></Button>
</TableRow>
</TableLayout>
 </LinearLayout>
</LinearLayout>

Запускаем приложение и проверяем. Отлично, теперь видны все кнопки. Поздравляю, вы гений!

Узнать ориентацию программно

Чтобы из кода узнать текущую ориентацию, можно создать функцию:


private String getScreenOrientation(){    
	if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
	    return "Портретная ориентация";
	else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
		return "Альбомная ориентация";
	else
		return "";
}

Вызовите данную функцию из нужного места, например, при щелчке кнопки и узнайте текущую ориентацию. В примере использовались две распространнённые системные константы для ориентации. Есть еще константа ORIENTATION_SQUARE (квадратный экран). Но я таких телефонов не встречал.

Установить ориентацию программно

Если вы большой оригинал и хотите запустить приложение в стиле "вид сбоку", то можете сделать это программно. Разместите код в методе onCreate():


import android.content.pm.ActivityInfo;

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

Учтите, что котам не очень удобно будет пользоваться вашим приложением.

Альбомный режим

Таким образом, вы можете запретить приложению менять ориентацию, если добавите нужный код в onCreate.


setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //для ландшафтного режима
// или
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //для портретного режима

Запрет на создание новой активности

Кстати, на примере программной установки ориентации можно увидеть интересный эффект, о котором нужно помнить. Предположим у нас есть кнопка, позволяющая менять ориентацию. Заодно будем менять текст на кнопке, чтобы операция соответствовала надписи.


public class OrientationActivity extends Activity {

	Button btn;
	static final String ORIENTATION_PORTRAIT = "Портретный режим";
	static final String ORIENTATION_LANDSCAPE = "Альбомный режим";

	// определяем изменение ориентации экрана
	boolean mState = false;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		btn = (Button) findViewById(R.id.button1);

		// set default text display
		btn.setText(ORIENTATION_LANDSCAPE);
	}

	public void onClick(View v) {
		// state FALSE: переключаемся на LANDSCAPE
		if (!mState) {
			setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
			btn.setText(ORIENTATION_PORTRAIT);
		}
		// state TRUE: переключаемся PORTRAIT
		else {
			setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
			btn.setText(ORIENTATION_LANDSCAPE);
		}
		// обновляем state на противоположное значение
		mState = !mState;
	}
}

Теперь посмотрите, что у нас получилось. Запустите проект и нажмите на кнопку. Ориентация экрана поменялась, однако текст на кнопке остался прежним, хотя по нашей задумке он должен измениться.

Ориентация

Теперь нажмём на кнопку ещё раз. Надпись изменится, но ориентация не сменится. И только повторный щелчок повернёт экран в обратную сторону.

По умолчанию, при смене ориентации Android уничтожает и пересоздает активность из кода, что подразумевает повторный вызов метода onCreate(). Поэтому при повороте активность устанавливала текст, определенный в onCreate. В большинстве случаев это не мешает программе. Но если приложение воспроизводит видео, то при смене ориентации вызов onCreate может привести к повторному началу воспроизведения (если так написан пример).

Чтобы активность не пересоздавалась, добавьте в манифест строчку для нужной активности:


android:configChanges="keyboardHidden|orientation|screenSize"
Во многих примерах, которые я видел, используется пара keyboardHidden|orientation, но, похоже, в Android 4 этого недостаточно и нужно добавить еще одии атрибут screenSize

В этом случае система вызовет метод onConfigurationChanged(Configuration) и полагается на вас (смотрите документацию).

Также можно применить другой способ без использования файла манифеста. Нужно вызвать метод onRetainNonConfigurationInstance() для сохранения данных между вызовами onDestroy и onCreate. Когда программа начинает исполнение снова, вы используете getLastNonConfigurationInstance() в новом экземпляре активности, чтобы восстановить эту информацию. Можно сохранять всё, включая ссылки на текущее намерение и выполняющие процессы.