В этом уроке:

- создаем экран с вкладками
- используем иконку в названии вкладки
- используем обработчик перехода между вкладками

 

Вкладки помогают логически разделить содержимое экрана. Вместо того, чтобы бегать по разным экранам, вы можете сделать вкладки и переключаться между ними. В этом уроке создадим приложение с вкладками и посмотрим их основные возможности.

 

Создадим проект:

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


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language

Автор сайта

Дмитрий Виноградов

Подробнее можно посмотреть или почитать.

Никакие другие люди не имеют к этому сайту никакого отношения и просто занимаются плагиатом.

Социальные сети

 

В канале я публикую ссылки на интересные и полезные статьи по Android

В чате можно обсудить вопросы и проблемы, возникающие при разработке



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal