Урок 76. Tab - вкладки. Общий обзор
В этом уроке:
- создаем экран с вкладками
- используем иконку в названии вкладки
- используем обработчик перехода между вкладками
Вкладки помогают логически разделить содержимое экрана. Вместо того, чтобы бегать по разным экранам, вы можете сделать вкладки и переключаться между ними. В этом уроке создадим приложение с вкладками и посмотрим их основные возможности.
Создадим проект:
Project name: P0761_Tab
Build Target: Android 2.3.3
Application name: Tab
Package name: ru.startandroid.develop.p0761tab
Create Activity: MainActivity
Пропишем тексты в strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Tab</string>
<string name="text_tab1">Это первая вкладка</string>
<string name="text_tab2">Это вторая вкладка</string>
<string name="text_tab3">Это третья вкладка</string>
<string name="text_tab_header">Свой заголовок</string>
</resources>
Удалим все дефолтное с экрана main.xml и добавим туда компонент TabHost из вкладки Composite:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TabHost
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TabWidget>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/tab1"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
<LinearLayout
android:id="@+id/tab2"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
<LinearLayout
android:id="@+id/tab3"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</FrameLayout>
</LinearLayout>
</TabHost>
</LinearLayout>
Компонент добавился и притащил с собой еще кучу всего. Давайте смотреть. TabHost – корневой элемент вкладок. В нем вертикальный LinearLayout, в котором расположены TabWidget и FrameLayout. TabWidget будет отображать заголовки вкладок, а FrameLayout – содержимое вкладок. В этом FrameLayout мы размещаем все View-компоненты, которые хотим отображать на вкладках. Позже мы (в коде) сообщим вкладке, какой именно компонент она должна показать (явно укажем id), вкладка выберет из этой общей кучи нужный ей компонент и отобразит его, как свое содержимое.
По дефолту во FrameLayout созданы три LinearLayout – они могут быть использованы, как контейнеры для содержимого вкладок. Т.е. вы их заполняете компонентами, как вам необходимо, а потом в коде просто указываете id нужного LinearLayout-а и он со всем содержимым отобразится на вкладке.
Нам сейчас не нужны LInearLayout, мы не будем делать вкладки с сложным содержимым, разместим во FrameLayout просто несколько TextView.
В итоге main.xml получился такой:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TabHost
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TabWidget>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvTab1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_tab1">
</TextView>
<TextView
android:id="@+id/tvTab2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_tab2">
</TextView>
<TextView
android:id="@+id/tvTab3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_tab3">
</TextView>
</FrameLayout>
</LinearLayout>
</TabHost>
</LinearLayout>
Создадим еще один layout-файл - tab_header.xml:
<?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"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_tab_header">
</TextView>
</LinearLayout>
Этот layout мы используем как свой экран для заголовка вкладки. Тут просто TextView.
Создайте в папке res папку drawable, если ее нет. В ней создайте файл tab_icon_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:drawable/star_on" android:state_selected="true"></item>
<item android:drawable="@android:drawable/star_off"></item>
</selector>
Подробно об этом можно почитать тут. Этот xml-файл мы укажем как картинку для заголовка вкладки. И когда система будет прорисовывать заголовок вкладки, она обратится к этому файлу, чтобы понять какую картинку ей отображать. Этот код будет возвращать стандартную Android картинку star_on, если вкладка выбрана (state_selected="true"). Иначе вернет star_off. Далее увидим это в приложении, и станет понятней.
Кодим MainActivity.java:
package ru.startandroid.develop.p0761tab;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TabHost;
import android.widget.TabHost.OnTabChangeListener;
import android.widget.Toast;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost);
// инициализация
tabHost.setup();
TabHost.TabSpec tabSpec;
// создаем вкладку и указываем тег
tabSpec = tabHost.newTabSpec("tag1");
// название вкладки
tabSpec.setIndicator("Вкладка 1");
// указываем id компонента из FrameLayout, он и станет содержимым
tabSpec.setContent(R.id.tvTab1);
// добавляем в корневой элемент
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec("tag2");
// указываем название и картинку
// в нашем случае вместо картинки идет xml-файл,
// который определяет картинку по состоянию вкладки
tabSpec.setIndicator("Вкладка 2", getResources().getDrawable(R.drawable.tab_icon_selector));
tabSpec.setContent(R.id.tvTab2);
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec("tag3");
// создаем View из layout-файла
View v = getLayoutInflater().inflate(R.layout.tab_header, null);
// и устанавливаем его, как заголовок
tabSpec.setIndicator(v);
tabSpec.setContent(R.id.tvTab3);
tabHost.addTab(tabSpec);
// вторая вкладка будет выбрана по умолчанию
tabHost.setCurrentTabByTag("tag2");
// обработчик переключения вкладок
tabHost.setOnTabChangedListener(new OnTabChangeListener() {
public void onTabChanged(String tabId) {
Toast.makeText(getBaseContext(), "tabId = " + tabId, Toast.LENGTH_SHORT).show();
}
});
}
}
Находим компонент TabHost. Обратите внимание, используется андроидный id. Он был таким по умолчанию при добавлении компонента в main.xml. В принципе, в нашем случае, этот id можно сменить на свой. Далее вызываем обязательный метод setup. Это первичная инициализация. В этом методе TabHost находит в себе TabWidget и FrameLayout. Вот их id в main.xml менять нельзя. Иначе TabHost будет ругаться, что не может их найти.
Далее создаем три вкладки. Для создания используется метод newTabSpec, на вход он берет тэг. Тэг – это просто некий строковый идентификатор вкладки. Позже увидим, где он используется. Для первой вкладки задаем название методом setIndicator. В метод setContent передаем id компонента (из FrameLayout), который мы хотели бы видеть в качестве содержимого вкладки. В нашем случае это TextView. Метод addTab присоединяет готовую вкладку к TabHost.
Вторая вкладка создается аналогично, только используем другую реализацию метода setIndicator. Заголовок вкладки может содержать не только текст, но и картинку. И здесь мы это используем – передаем в метод текст и xml вместо картинки. Тот самый xml, который определяет картинку по состоянию вкладки. Разумеется, если вам нужна статичная картинка, вы можете указать ее и не использовать xml вообще.
При создании третьей вкладки используем еще одну реализацию метода setIndicator, которая берет на вход View и его ставит как заголовок. Используем тут наш layout-файл tab_header.
Вкладки созданы. Устанавливаем (setCurrentTabByTag) вторую в качестве выбранной по умолчанию. И пропишем (setOnTabChangedListener) для TabHost обработчик, который срабатывает при переключении вкладок. Будем выводить сообщение с тэгом вкладки.
Все сохраним и запустим приложение.
Выбрана вторая вкладка, т.к. мы это определили методом setCurrentTabByTag. Ее содержимое – это TextView с id = tvTab2, как мы и указывали в коде в методе setContent при создании вкладки.
У третьей вкладки заголовок соответствует содержимому tab_header, т.к. мы использовали setIndicator, который принимает на вход View.
Выберем первую вкладку.
Сработал обработчик и появилось сообщение с тэгом выбранной вкладки. Содержимое первой вкладки – это TextView с id = tvTab1 из FrameLayout.
Обратите внимание, что сменилась картинка на заголовке второй вкладки. Это обеспечил selector из res/drawable/tab_icon_selector. В зависимости от состояния вкладки он выдает разные картинки.
На следующем уроке:
- используем Activity в качестве содержимого вкладки
- используем TabActivity