Глава 24.Стили и включения Листинг 24.1 (продолжение) setContentView(R.layout.activity_remote_control); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_remote_control, menu); returntrue; } @Override protected Fragment createFragment() { return new RemoteControlFragment(); } @Override public void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); } } Откройте файл AndroidManifest.xml и ограничьте эту активность портретной ориен- тацией. Листинг 24.2. Ограничьтесь портретной ориентацией (AndroidManifest.xml) android:label="@string/app_name" android:screenOrientation="portrait">
Создание RemoteControlFragment На панели Package Explorer переименуйте файл activity_remote_control.xml в fragment_re- mote_control.xml . Для начала мы создадим трехкнопочный пульт, чтобы упростить код. Замените содержимое fragment_remote_control.xml разметкой XML из листинга 24.3. Листинг 24.3. Исходный трехкнопочный макет (layout/fragment_remote_control.xml) xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".RemoteControlActivity"> android:layout_width="wrap_content" android:layout_height="wrap_content"
Создание проекта RemoteControl 397 android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="@string/hello_world" />
android:id="@+id/fragment_remote_control_tableLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:stretchColumns="*" > android:id="@+id/fragment_remote_control_selectedTextView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" android:gravity="center" android:text="0" android:textSize="50dp" /> android:id="@+id/fragment_remote_control_workingTextView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_margin="15dp" android:background="#555555" android:gravity="center" android:text="0" android:textColor="#cccccc" android:textSize="20dp" /> Конструкция android:stretchColumns="*" гарантирует, что все столбцы будут иметь одинаковую ширину. Кроме того, для определения размера текста мы используем единицы dp вместо sp . Это означает, что текст будет иметь одинаковый размер не- зависимо от настроек пользователя. Наконец, создайте новый класс с именем RemoteControlFragment . Назначьте его суперклассом android.support.v4.app.Fragment . В файле RemoteControlFragment. java переопределите метод onCreateView(…) с назначением слушателей для кнопок.
398 Глава 24. Стили и включения Листинг 24.4. Создание класса RemoteControlFragment (RemoteControlFragment.java) public class RemoteControlFragment extends Fragment { private TextView mSelectedTextView; private TextView mWorkingTextView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_remote_control, parent, false); mSelectedTextView = (TextView)v .findViewById(R.id.fragment_remote_control_selectedTextView); mWorkingTextView = (TextView)v .findViewById(R.id.fragment_remote_control_workingTextView); View.OnClickListener numberButtonListener = new View.OnClickListener() { public void onClick(View v) { TextView textView = (TextView)v; String working = mWorkingTextView.getText().toString(); String text = textView.getText().toString(); if (working.equals("0")) { mWorkingTextView.setText(text); } else { mWorkingTextView.setText(working + text); } } }; Button zeroButton = (Button)v .findViewById(R.id.fragment_remote_control_zeroButton); zeroButton.setOnClickListener(numberButtonListener); Button oneButton = (Button)v .findViewById(R.id.fragment_remote_control_oneButton); oneButton.setOnClickListener(numberButtonListener); Button enterButton = (Button) v .findViewById(R.id.fragment_remote_control_enterButton); enterButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { CharSequence working = mWorkingTextView.getText(); if (working.length() > 0) mSelectedTextView.setText(working); mWorkingTextView.setText("0"); } }); return v; } } Хотя мы используем три кнопки, нужны только два слушателя щелчков. Дело в том, что для обоих цифровых кнопок может использоваться один слушатель. При щелчке на цифровой кнопке вы либо добавляете новую цифру в существую- щем текстовом представлении, либо заменяете его цифрой, на которой был сделан
Стили и компактность разметки 399 щелчок — в зависимости от того, является ли текущим введенным текстом 0. Затем при нажатии кнопки Enter текст рабочей области перемещается в область выбора и стирается. Запустите приложение RemoteControl. Вы увидите простое трехкнопочное прило- жение с двоичным вводом. Да если разобраться, кому нужно больше двух кнопок? Рис. 24.3. Пульт с двоичным вводом Стили и компактность разметки Теперь, когда у нас имеется работоспособный проект, взгляните на разметку макета. Все кнопки выглядят одинаково. Пока это не создает проблем, но если вы захотите добавить к кнопке атрибут, работу придется повторить в трех местах. А что будет, когда кнопок станет двенадцать? К счастью, Android поддерживает стили пользовательского интерфейса, которые позволяют избавиться от подобных повторений. Ресурсы стилей похожи на стили CSS. Каждый стиль определяет набор пар XML «атрибут-значение». Стили орга- низуются в иерархию: потомок обладает теми же атрибутами и значениями, что и родитель, но может переопределять их или добавлять новые значения. Стили, как и строковые ресурсы, определяются в теге файла XML из папки res/values . Имя файла, как и для строковых ресурсов, несущественно, но по общепринятой схеме стили размещаются в файле styles.xml
400 Глава 24. Стили и включения Мастер проектов Android уже создал за вас файл styles.xml . (Обратите внимание: в этом файле уже определена тема приложения RemoteControl — стандартная для Android; тема определяет внешний вид кнопок, фоны и другие распространенные элементы.) Мы переместим атрибуты, общие для всех кнопок, из конкретных кнопок в новый стиль RemoteButton . Включите в файл стилей определение, приведенное ниже. Листинг 24.5. Исходные определения стилей RemoteControl (values/styles.xml)
0dp match_parent
Каждый стиль определяется элементом
Взмах волшебной палочки — и все кнопки моментально изменяются! Рис. 24.5. Все кнопки выглядят одинаково! Для любознательных: include и merge Ранее в этой главе мы использовали тег include для многократного включения стро- ки кнопок RemoteButton (в один макет). Принцип работы тега достаточно очевиден: Этот элемент включает содержимое файла разметки с идентификатором ресурса @layout/some_partial_layout Тег include может использоваться как для устранения дублирования разметки в одном макете, как это было сделано ранее в этой главе, так и для устранения дуб- лирования между макетами. Если некоторый блок разметки встречается в двух
Упражнение. Наследование стилей 405 и более макетах, вы можете создать его в форме макета и включать там, где он ну- жен. Если в будущем этот блок потребуется изменить, все изменения достаточно внести в одном месте. У тегов include есть пара особенностей, о которых необходимо знать. Во-первых, включаемый макет проходит обычный процесс поиска ресурсов в зависимости от текущей конфигурации устройства; соответственно в нем можно использовать конфигурационные квалификаторы, как и в любом другом макете. Во-вторых, можно переопределять атрибут android:id и любые атрибуты android:layout_* корневого элемента включаемого макета, указывая их в самом теге include . Это позволяет использовать один макет несколько раз, указывая разные атрибуты при каждом включении. Элемент merge работает в сочетании с элементом include . Он может использоваться как корневой элемент включаемого макета вместо реального виджета. Когда один макет включает другой макет с корневым элементом merge , потомки элемента merge включаются напрямую — они становятся потомками элемента include , а элемент merge удаляется. Механизм включения работает проще, чем кажется из описания. Если оно вам кажется запутанным, просто запомните, что элемент merge отбрасывается. Он присутствует только по одной причине: XML требует, чтобы корневой элемент присутствовал только в одном экземпляре. Упражнение. Наследование стилей Кнопки Delete и Enter скромно располагаются у нижнего края экрана, а в их оформ- лении используется такой же стиль, как у цифровых кнопок. Кнопкам «действий» нужен другой, более броский стиль. Создайте новый стиль кнопки, производный от стиля RemoteButton , который уста- навливает атрибут полужирного текста. Затем включите использование нового стиля для первой и третьей кнопки в нижней строке. Создать стиль, производный от другого стиля, несложно. Это можно сделать двумя способами. Первый — присвоить атрибуту parent вашего стиля имя того стиля, от которого он должен наследовать. Другой, более простой способ — снабдить имя стиля префиксом из родительского стиля и точки (например, ParentStyleName. MyStyleName ).
Графические объекты В предыдущей главе мы быстро построили интерфейс пульта дистанционного управления, используя некоторые нетривиальные приемы создания макетов. Пульт выглядит неплохо, хотя и немного банально, потому что все кнопки в нем имеют ти- пичный для Android внешний вид и поведение. В этой главе мы воспользуемся двумя новыми инструментами, чтобы придать кнопкам совершенно особое оформление. Рис. 25.1. Переработанная версия RemoteControl Оба эти инструмента относятся к категории графических объектов (drawables). В An- droid графическим объектом называется все, что предназначено для прорисовки на 25
Графические объекты XML 407 экране, будь то абстрактная фигура, класс, производный от Drawable , или растровое изображение. Нам уже знаком один вид графических объектов: BitmapDrawable , представляющий растровое изображение. В этой главе мы рассмотрим несколько других видов графических объектов: списки состояний, геометрические фигуры, списки слоев и 9-зонные графические объекты. Поскольку первые три вида обычно определяются в файлах XML, мы объединим их в более широкую категорию графических объектов XML. Графические объекты XML Прежде чем браться за графические объекты XML, проведем небольшой экспери- мент: используем атрибут android:background для изменения цвета фона кнопок. Листинг 25.1. Попытка изменения цвета кнопки (values/styles.xml)
Результат должен выглядеть примерно так. Рис. 25.2. Что произошло?