Библиотека поддержки Android Design Support Library облегчает разработку, обеспечивая обратную совместимость множества компонентов в материальном стиле, вплоть до Android 2.1. В ней представлены такие компоненты, как navigation drawer, floating action button, snackbar, tabs, floating labels и animation frameworks. В этой статье мы узнаем, как реализовать вкладки в андроид приложении в стиле Material Design.
Прежде чем идти дальше, ознакомьтесь с руководящими принципами реализации вкладок в приложениях.
Создаем приложение в стиле Material Design
Создадим новый проект с применением материальной темы.
В Android Studio в меню выберите File ⇒ New Project и заполните все детали, необходимые для создания нового проекта. При выборе шаблона, выберите Empty Activity и продолжите.
Откройте файл сборки модуля app build.gradle и добавьте в секцию dependensies библиотеку поддержки дизайна com.android.support:design:23.0.1:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:design:23.0.1' }
Откройте файл colors.xml в папке res ⇒ values и добавьте цвета:
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#125688</color> <color name="colorPrimaryDark">#125688</color> <color name="textColorPrimary">#FFFFFF</color> <color name="windowBackground">#FFFFFF</color> <color name="navigationBarColor">#000000</color> <color name="colorAccent">#c8e8ff</color> </resources>
Добавьте размеры в файл dimens.xml в папке res ⇒ values:
dimens.xml <resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> <dimen name="tab_max_width">264dp</dimen> <dimen name="tab_padding_bottom">16dp</dimen> <dimen name="tab_label">14sp</dimen> <dimen name="custom_tab_layout_height">72dp</dimen> </resources>
Откройте файл styles.xml в папке res ⇒ values и добавьте стили. Стили, определенные в этом styles.xml являются общими для всех версий android:
<resources> <style name="MyMaterialTheme" parent="MyMaterialTheme.Base"> </style> <style name="MyMaterialTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="windowNoTitle">true</item> <item name="windowActionBar">false</item> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>
Теперь внутри папки res, создайте папку с именем values-v21. В ней создайте еще один файл styles.xml и пропишите в нем стили для версии Android 5.0:
<resources> <style name="MyMaterialTheme" parent="MyMaterialTheme.Base"> <item name="android:windowContentTransitions">true</item> <item name="android:windowAllowEnterTransitionOverlap">true</item> <item name="android:windowAllowReturnTransitionOverlap">true</item> <item name="android:windowSharedElementEnterTransition">@android:transition/move</item> <item name="android:windowSharedElementExitTransition">@android:transition/move</item> </style> </resources>
И наконец, откройте файл AndroidManifest.xml и примените к приложению нашу настроенную тему, изменив значение атрибута android:theme:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="info.androidhive.materialtabs" > <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/MyMaterialTheme" > <activity android:name=".activity.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Запустите приложение и убедитесь, что оно отображается в материальном стиле . Если вы видите изменения цвета панели уведомлений, это означает, что успешно применяется тема материального дизайна.
Теперь давайте начнем добавление вкладок. Но до этого мы создадим несколько фрагментов для тестирования. Все эти фрагменты будут содержать очень простой пользовательский интерфейс с только одним TextView.
В основном пакете создайте фрагмент с именем OneFragment.java и добавьте такой код:
import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import info.androidhive.materialtabs.R; public class OneFragment extends Fragment{ public OneFragment() { // Required empty public constructor } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_one, container, false); } }
Откройте файл макета fragment_one.xml в папке res ⇒ layout и внесите изменения:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="info.androidhive.materialtabs.fragments.OneFragment"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/one" android:textSize="40dp" android:textStyle="bold" android:layout_centerInParent="true"/> </RelativeLayout>
10. Аналогичным образом создайте несколько фрагментов с аналогичным кодом, который мы использовали для OneFragment.java. Я создал TwoFragment.java, ThreeFragment.java, FourFragemnt.java ….. до TenFragment.java, всего 10 фрагментов.
Фиксированные вкладки
Фиксированные вкладки следует использовать, когда у вас ограниченное количество вкладок. Эти вкладки имеют фиксированное положение. В библиотеке android design support library много новых элементов, например, CoordinatorLayout AppBarLayout, TabLayout и других. Мы не охватываем всех их в рамках этой статьи.
Откройте файл компоновки основной активити (activity_main.xml) и добавьте код разметки:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="fixed" app:tabGravity="fill"/> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </android.support.design.widget.CoordinatorLayout>
app:tabMode – Определяет режим макета вкладок. В нашем случае значение должно быть “fixed”
Откройте MainActivity.java и внесите изменения:
import android.os.Bundle; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import java.util.ArrayList; import java.util.List; import info.androidhive.materialtabs.R; import info.androidhive.materialtabs.fragments.OneFragment; import info.androidhive.materialtabs.fragments.ThreeFragment; import info.androidhive.materialtabs.fragments.TwoFragment; public class MainActivity extends AppCompatActivity { private Toolbar toolbar; private TabLayout tabLayout; private ViewPager viewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); viewPager = (ViewPager) findViewById(R.id.viewpager); setupViewPager(viewPager); tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(viewPager); } private void setupViewPager(ViewPager viewPager) { ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); adapter.addFragment(new OneFragment(), "ONE"); adapter.addFragment(new TwoFragment(), "TWO"); adapter.addFragment(new ThreeFragment(), "THREE"); viewPager.setAdapter(adapter); } class ViewPagerAdapter extends FragmentPagerAdapter { private final List<Fragment> mFragmentList = new ArrayList<>(); private final List<String> mFragmentTitleList = new ArrayList<>(); public ViewPagerAdapter(FragmentManager manager) { super(manager); } @Override public Fragment getItem(int position) { return mFragmentList.get(position); } @Override public int getCount() { return mFragmentList.size(); } public void addFragment(Fragment fragment, String title) { mFragmentList.add(fragment); mFragmentTitleList.add(title); } @Override public CharSequence getPageTitle(int position) { return mFragmentTitleList.get(position); } } }
tabLayout.setupWithViewPager() – Назначает ViewPager для TabLayout.
setupViewPager() – Определяет количество вкладок, установив соответствующий фрагмент и имя вкладки.
ViewPagerAdapter – Класс пользовательского адаптера обеспечивает фрагменты, необходимые для view-пейджера.
Теперь запустите приложение. Вы должны видеть вкладки и свайпом переходить между вкладками.
Вкладки, занимающие всю ширину экрана
Если вы хотите, чтобы закладки занимали по ширине весь экран, добавьте атрибут app:tabGravity=”fill” в ваш TabLayout.
Вкладки, выровненные по центру
Если вы хотите расположить ваши вкладки по центру, установите app:tabGravity=”center” для TabLayout.
Прокручиваемые вкладки
Прокручиваемые вкладки должны использоваться, когда у вас большое количество вкладок, и недостаточно места на экране, чтобы отобразить все одновременно. Чтобы сделать вкладки прокручиваемыми , установите app:tabMode=”scrollable” для TabLayout.
Откройте activity_main.xml и измените значение атрибута app:tabMode на scrollable.
<android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="scrollable"/>
Измените MainActivity.java и добавьте несколько фрагментов для ViewPager в методе setupViewPager(). В примере ниже добавлено 10 фрагментов в ViewPager. После изменения код основной активити должен выглядеть так:
import android.os.Bundle; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private Toolbar toolbar; private TabLayout tabLayout; private ViewPager viewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); viewPager = (ViewPager) findViewById(R.id.viewpager); setupViewPager(viewPager); tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(viewPager); } private void setupViewPager(ViewPager viewPager) { ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); adapter.addFrag(new OneFragment(), "ONE"); adapter.addFrag(new TwoFragment(), "TWO"); adapter.addFrag(new ThreeFragment(), "THREE"); adapter.addFrag(new FourFragment(), "FOUR"); adapter.addFrag(new FiveFragment(), "FIVE"); adapter.addFrag(new SixFragment(), "SIX"); adapter.addFrag(new SevenFragment(), "SEVEN"); adapter.addFrag(new EightFragment(), "EIGHT"); adapter.addFrag(new NineFragment(), "NINE"); adapter.addFrag(new TenFragment(), "TEN"); viewPager.setAdapter(adapter); } class ViewPagerAdapter extends FragmentPagerAdapter { private final List<Fragment> mFragmentList = new ArrayList<>(); private final List<String> mFragmentTitleList = new ArrayList<>(); public ViewPagerAdapter(FragmentManager manager) { super(manager); } @Override public Fragment getItem(int position) { return mFragmentList.get(position); } @Override public int getCount() { return mFragmentList.size(); } public void addFrag(Fragment fragment, String title) { mFragmentList.add(fragment); mFragmentTitleList.add(title); } @Override public CharSequence getPageTitle(int position) { return mFragmentTitleList.get(position); } } }
Теперь если вы запустите приложение, вы можете увидеть больше количество вкладок с полосой прокрутки.
Вкладки с иконкой и текстом заголовка
Иногда нужно добавить значок на вкладку. Ранее добавление значка на вкладку было трудоемким процессом. Но сейчас с библиотекой поддержки дизайна это очень легко. Все, что вам нужно сделать — это вызвать метод setIcon() , передав ему нужную пиктограмму. Значок будет помещен перед текстом заголовка вкладки.
tabLayout.getTabAt(0).setIcon(tabIcons[0]); tabLayout.getTabAt(1).setIcon(tabIcons[1]);
Откройте MainActivity.java и измените код, как показано ниже. Здесь добавлен новый метод под названием setupTabIcons() в котором установлены все значки для вкладок.
import android.os.Bundle; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private Toolbar toolbar; private TabLayout tabLayout; private ViewPager viewPager; private int[] tabIcons = { R.drawable.ic_tab_favourite, R.drawable.ic_tab_call, R.drawable.ic_tab_contacts }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); viewPager = (ViewPager) findViewById(R.id.viewpager); setupViewPager(viewPager); tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(viewPager); setupTabIcons(); } private void setupTabIcons() { tabLayout.getTabAt(0).setIcon(tabIcons[0]); tabLayout.getTabAt(1).setIcon(tabIcons[1]); tabLayout.getTabAt(2).setIcon(tabIcons[2]); } private void setupViewPager(ViewPager viewPager) { ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); adapter.addFrag(new OneFragment(), "ONE"); adapter.addFrag(new TwoFragment(), "TWO"); adapter.addFrag(new ThreeFragment(), "THREE"); viewPager.setAdapter(adapter); } class ViewPagerAdapter extends FragmentPagerAdapter { private final List<Fragment> mFragmentList = new ArrayList<>(); private final List<String> mFragmentTitleList = new ArrayList<>(); public ViewPagerAdapter(FragmentManager manager) { super(manager); } @Override public Fragment getItem(int position) { return mFragmentList.get(position); } @Override public int getCount() { return mFragmentList.size(); } public void addFrag(Fragment fragment, String title) { mFragmentList.add(fragment); mFragmentTitleList.add(title); } @Override public CharSequence getPageTitle(int position) { return mFragmentTitleList.get(position); } } }
Вкладки только со значками
Установка только значка для вкладки такая же, как и установка текста и значка, только метод getPageTitle() в классе ViewPagerAdapter возвращает null вместо заголовка вкладки.
Откройте MainActivity.java и измените метод getPageTitle(), как показано ниже и запустите приложение проекта.
@Override public CharSequence getPageTitle(int position) { // return null to display only the icon return null; }
Значок вкладки над текстом заголовка
Если стандартных настроек макета вкладок недостаточно, их можно кастомизировать. При настройке пользовательского представления вкладок, убедитесь, что вы будете следовать спецификации андроид для вкладок.
Когда на вкладке значок и текст, можно увидеть, что значок расположен на одной линии с текстом заголовка вкладки. Но если вы хотите поместить значок над текстом, вы должны использовать custom view для этого.
В папке res ⇒ values, создайте xml файл с именем fonts.xml и добавьте строки, как в примере ниже. Этот XML-файл определяет семейство шрифтов для заголовка.
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="font_fontFamily_medium">sans-serif</string> </resources>
В папке res ⇒ values-v21, создайте другой xml-файл с именем fonts.xml и таким кодом:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="font_fontFamily_medium">sans-serif-medium</string> </resources>
Откройте activity_main.xml и установите пользовательскую высоту custom_tab_layout_height. Настройка высоты имеет важное значение, так как установка значка выше текста занимает больше места, чем обычно.
<android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="@dimen/custom_tab_layout_height" app:tabMode="fixed" app:tabGravity="fill"/>
Создайте xml-макет с именем custom_tab.xml в папке res ⇒ layout. В этом макете мы определим custom view для вкладки.
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tab" android:textColor="@color/colorAccent" android:textSize="@dimen/tab_label" android:fontFamily="@string/font_fontFamily_medium"/>
Откройте MainActivity.java и измените код, как на примере ниже. Здесь, если вы посмотрите метод setupTabIcons(), мы присваиваем макет custom_tab.xml каждой вкладке:
TextView tabOne = (TextView) LayoutInflater.from(this).inflate(R.layout.custom_tab, null); tabOne.setText("ONE"); tabOne.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_tab_favourite, 0, 0); tabLayout.getTabAt(0).setCustomView(tabOne);
import android.os.Bundle; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.widget.TextView; import java.util.ArrayList; import java.util.List; import info.androidhive.materialtabs.R; import info.androidhive.materialtabs.fragments.OneFragment; import info.androidhive.materialtabs.fragments.ThreeFragment; import info.androidhive.materialtabs.fragments.TwoFragment; public class MainActivity extends AppCompatActivity { private Toolbar toolbar; private TabLayout tabLayout; private ViewPager viewPager; private int[] tabIcons = { R.drawable.ic_tab_favourite, R.drawable.ic_tab_call, R.drawable.ic_tab_contacts }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); viewPager = (ViewPager) findViewById(R.id.viewpager); setupViewPager(viewPager); tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(viewPager); setupTabIcons(); } private void setupTabIcons() { TextView tabOne = (TextView) LayoutInflater.from(this).inflate(R.layout.custom_tab, null); tabOne.setText("ONE"); tabOne.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_tab_favourite, 0, 0); tabLayout.getTabAt(0).setCustomView(tabOne); TextView tabTwo = (TextView) LayoutInflater.from(this).inflate(R.layout.custom_tab, null); tabTwo.setText("TWO"); tabTwo.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_tab_call, 0, 0); tabLayout.getTabAt(1).setCustomView(tabTwo); TextView tabThree = (TextView) LayoutInflater.from(this).inflate(R.layout.custom_tab, null); tabThree.setText("THREE"); tabThree.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_tab_contacts, 0, 0); tabLayout.getTabAt(2).setCustomView(tabThree); } private void setupViewPager(ViewPager viewPager) { ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); adapter.addFrag(new OneFragment(), "ONE"); adapter.addFrag(new TwoFragment(), "TWO"); adapter.addFrag(new ThreeFragment(), "THREE"); viewPager.setAdapter(adapter); } class ViewPagerAdapter extends FragmentPagerAdapter { private final List<Fragment> mFragmentList = new ArrayList<>(); private final List<String> mFragmentTitleList = new ArrayList<>(); public ViewPagerAdapter(FragmentManager manager) { super(manager); } @Override public Fragment getItem(int position) { return mFragmentList.get(position); } @Override public int getCount() { return mFragmentList.size(); } public void addFrag(Fragment fragment, String title) { mFragmentList.add(fragment); mFragmentTitleList.add(title); } @Override public CharSequence getPageTitle(int position) { return mFragmentTitleList.get(position); } } }
Теперь если вы запустите приложение, вы можете увидеть значок над текстом заголовка вкладки.
Перевод. Источник.
Пытаюсь запустить приложение (кстати делаю я через AIDE) и выдает ошибку:
appt: Error retrieving parent for item: No resource found that matches the given name: ‘MyMaterialTheme.Base
Хотя я такой стиль уже создал. Что делать то? Очень хочу вкладки сделать
Спасибо за урок, но есть вопрос по поводу, когда происходит переход на вкладку, то выполняется прогрузка соседней, например если перейти на вторую вкладку,а на третей к примеру выполняется код загрузки изображения из интернета в AsyncTask`e, то загрузка изображения произойдет при переходе на вторую вкладку. Есть ли решение, чтобы прогрузка фрагментов происходила только в выбранной вкладке?