ScrollView и HorizontalScrollView
При большом количестве информации, которую нужно поместить на экране приходится использовать полосы прокрутки. В Android существуют специальные компоненты ScrollView и HorizontalScrollView, которые являются контейнерными элементами и наследуются от ViewGroup. Обратите внимание, что класс TextView использует свою собственную прокрутку и не нуждается в добавлении отдельных полос прокрутки. Но использование отдельных полос даже с TextView может улучшить вид вашего приложения и повышает удобство работы для пользователя.
На панели инструментов компоненты можно найти в разделе Containers.
В контейнеры ScrollView и HorizontalScrollView можно размещать только один дочерний элемент (обычно LinearLayout), который в свою очередь может быть контейнером для других элементов. Виджет ScrollView, несмотря на свое название, поддерживает только вертикальную прокрутку, поэтому для создания вертикальной и горизонтальной прокрутки необходимо использовать ScrollView в сочетании с HorizontalScrollView. Обычно ScrollView используют в качестве корневого элемента, а HorizontalScrollView в качестве дочернего. Можно и наоборот, пробуйте.
В в теле метода onCreate() создайте ссылку на элемент TextView, объявленный в XML-разметке, и запишите в него через метод setText() какой-нибуль длинный текст, который не поместится в видимые размеры экрана устройства:
Запустив проект, вы должны увидеть вертикальную и горизонтальную полосы прокрутки при попытке скролирования.
Если полосы прокрутки вас раздражают, то используйте атрибут android:scrollbars=»none», который скроет их.
По такому же принципу можете вложить ImageView, чтобы просматривать большие картинки:
Методы scrollBy() и scrollTo()
Вы можете программно прокручивать контент с помощью методов scrollBy() и scrollTo(). Например, можно организовать автоматическую прокрутку во время чтения. В нашем примере мы будем прокручивать контент с помощью трёх кнопок.
Сам код для методов:
Дополнительное чтение
Библиотека ParallaxScrollView с использованием эффекта параллакса. Вы прокручиваете длинный текст, а задний фон прокручивается чуть медленнее. Возможно, кому-то пригодится. Там же можно скачать готовое демо и просмотреть в действии.
Источник
ViewPager
В Android есть компонент ViewFlipper, который позволяет прокручивать экран влево-вправо. Но он имеет ряд недостатков. ViewFlipper не позволяет с лёгкостью добиться эффекта привязки, когда экраны двигаются вместе с пальцем. Также нужен механизм автоматической доводки экранов в ту или иную сторону.
Google предложила другой вариант — использовать компонент ViewPager, который входит в состав Support Package. В новых проектах необходимая библиотека добавляется автоматически, поэтому мы сразу можем попробовать написать демонстрационный пример.
Так как компонент относится к пакету совместимости, то используемые фрагменты должны относиться к классу android.support.v4.app.Fragment, даже если вы собираетесь писать приложение для новых устройств, которые поддерживают стандартные фрагменты. На данный момент библиотека переписывается на AndroidX, поэтому я буду по возможности заменять старые примеры. Все названия классов и методов остаются, меняется только имя пакета.
ViewPager относится к категории ViewGroup и схож по работе с компонентами на основе AdapterView (ListView и Gallery). На панели инструментов Android Studio компонент можно найти в разделе Containers. При использовании ViewPager в разметке используйте полное имя класса. Старый вариант.
Данные для отображения компонент берёт из адаптеров:
- PagerAdapter (фрагменты не обязательны)
- FragmentPagerAdapter (если используется мало фрагментов)
- FragmentStatePagerAdapter (если много фрагментов)
Вам нужно унаследовать класс от нужного адаптера и реализовать свои методы. Добавление и удаление экранов реализуется с помощью методов instantiateItem() и destroyItem() соответственно. Элементы View для отображения можно создавать прямо в адаптере. Такой подход хорош тем, что ViewPager можно настраивать так, чтобы в адаптере не хранились все экраны сразу. По умолчанию адаптер хранит текущий экран, и по одному слева и справа от него. Это может сэкономить память, если содержание экранов слишком сложное.
Когда пользователь переключается на другой фрагмент, то вызывается метод onDestroyView(), а не onDestroy().
FragmentPagerAdapter
Рассмотрим простой пример с минимальным количеством кода для первого знакомства. Создадим новый проект. В activity_main.xml добавим ViewPager из примера выше.
Подготовим разметки для фрагментов.
Сначала создадим ещё один файл разметки res/layout/fragment_1.xml:
И ещё один файл res/layout/fragment_2.xml:
Создадим новый класс для первого фрагмента Fragment1:
Класс для второго фрагмента Fragment2:
Фрагменты подготовлены. Теперь в основном классе MainActivity создадим адаптер на основе FragmentPagerAdapter и напишем остальной код:
В методе getCount() следует возвращать количество страниц. В методе getItem() по номеру позиции нужно вернуть фрагмент.
Если у нас имеется три экрана, то может быть удобно сразу показать второй экран, чтобы можно было прокручивать или влево к первому экрану или вправо к третьему экрану. Это достигается вызовом метода setCurrentItem(1).
В нашем примере мне было лень создавать третий фрагмент, поэтому второй фрагмент используется два раза. В качестве фрагмента по умолчанию я установил первый фрагмент. Запустите пример и подвигайте экраны приложения вправо-влево.
Сам принцип теперь понятен? Нужно подготовить фрагменты и через адаптер сменять их.
PageTransformer
Если хочется добавить анимации при смене фрагментов, то воспользуйтесь специальным классом PageTransformer. Попробуем менять прозрачность фрагмента.
Класс PageTransformer заслуживает отдельной статьи.
PagerTitleStrip
Рассмотрим другой вариант и изучим другие возможности. Для примера будем использовать всего один фрагмент, содержимое которого будем менять динамически. А сверху добавим специальную полоску с заголовками.
Изменим разметку для фрагмента (fragment_1.xml):
Создадим два строковых массива в ресурсах (res/values/strings.xml):
Теперь подготовим класс для фрагмента CatFragment:
При создании фрагмент извлекает данные из объекта Bundle и использует их для заполнения текстов и картинки.
Создадим класс для адаптера в отдельном файле для удобства с помощью мастера создания класса, указав в качестве суперкласса FragmentPagerAdapter.
У адаптера должен быть пустой конструктор и два обязательных метода getItem() и getCount(). Немного переделаем конструктор, чтобы он использовал контекст, необходимый для извлечения данных из ресурсов.
Для подсчёта количества экранов используется свойство массива length. А для формирования содержимого экрана мы помещаем нужные данные в объект Bundle, которые затем будут переданы фрагменту. Данные находятся в трёх массивах: короткий текст, длинный текст и ссылка на изображение в ресурсах.
Метод getPageTitle() позволяет задать заголовок для фрагмента. Сам заголовок мы зададим в следующем шаге.
Приготовления почти закончены. Изменим разметку главной активности, чтобы у отдельного экрана появились заголовки. Для этого добавляется тег PagerTitleStrip:
Если вы хотите видеть заголовок внизу фрагмента, то используйте android:layout_gravity=»bottom».
Загружаем ViewPager и подключаем адаптер.
Пример на планшете в альбомной ориентации:
Свойства PagerTitleStrip можно изменить программно, например, поменять размер и цвет заголовков.
Тег PagerTitleStrip можно заменить на PagerTabStrip. В этом случае заголовке станут ещё и кнопками-вкладками. Ничего переписывать не придётся. Вы по-прежнему можете перелистывать страницы пальцами или нажимать на заголовки.
У ViewPager есть обработчик событий OnPageChangeListener с тремя методами:
Например, метод onPageSelected() позволяет получить номер текущего экрана при пролистывании (отсчёт от 0).
Метод onPageScrolled() даёт представление о текущем значении скрола при пролистывании.
Метод onPageScrollStateChanged() позволяет узнать о состоянии, в котором находится скрол (SCROLL_STATE_IDLE – экраны не листаются, SCROLL_STATE_DRAGGING – пользователь «тащит» страницу, SCROLL_STATE_SETTLING – палец пользователя «доводит» страницу до конца.
Сейчас данный слушатель помечен как устаревший.
Адаптер FragmentStatePagerAdapter
Это старый вариант примера, когда компонент только появился. Я не стал делать ревизию, оставляю вам для самостоятельного изучения.
Адаптер FragmentPagerAdapter держит все фрагменты в памяти, а FragmentStatePagerAdapter создаёт их заново по мере необходимости. В этом их принципиальное отличие. Напишем пример и добавим поддержку кнопки Back.
Создадим разметку для отдельной страницы (pager.xml):
Создадим класс фрагмента с минимальным кодом
Разметку активности оставим минимальной:
Код для активности. Тут всё уже знакомо.
Заключение
В Android Studio в некоторых шаблонах встречается реализация ViewPager из коробки. Недавно Гугл представила бета-версию ViewPager2, которая должна заменить старый компонент.
Источник
Фрагменты
Введение во фрагменты
Организация приложения на основе нескольких activity не всегда может быть оптимальной. Мир ОС Android довольно сильно фрагментирован и состоит из многих устройств. И если для мобильных аппаратов с небольшими экранами взаимодействие между разными activity выглядит довольно неплохо, то на больших экранах — планшетах, телевизорах окна activity смотрелись бы не очень в силу большого размера экрана. Собственно поэтому и появилась концепция фрагментов.
Фрагмент представляет кусочек визуального интерфейса приложения, который может использоваться повторно и многократно. У фрагмента может быть собственный файл layout, у фрагментов есть свой собственный жизненный цикл. Фрагмент существует в контексте activity и имеет свой жизненный цикл, вне activity обособлено он существовать не может. Каждая activity может иметь несколько фрагментов.
Для начала работы с фрагментами создадим новый проект с пустой MainActivity. И вначале создадим первый фрагмент. Но сразу стоит отметить, что не вся функциональность фрагментов по умолчанию может быть доступна в проекте, поскольку располагается в отдельной библиотеке — AndroidX Fragment library . И вначале необходимо подключить к проекту эту библиотеку в файле build.gradle .
Найдем в нем секцию dependencies , которая выглядит по умолчанию примерно так:
В ее начало добавим строку
То есть в моем случае получится
И затем нажмем на появившуюся ссылку Sync Now .
Фактически фрагмент — это обычный класс Java, который наследуется от класса Fragment . Однако как и класс Activity, фрагмент может использовать xml-файлы layout для определения графического интерфейса. И таким образом, мы можем добавить по отдельности класс Java, который представляет фрагмент, и файл xml для хранения в нем разметки интерфейса, который будет использовать фрагмент.
Итак, добавим в папку res/layout новый файл fragment_content.xml и определим в нем следующий код:
Фрагменты содержат те же элементы управления, что и activity. В частности, здесь определены кнопка и текстовое поле, которые будут составлять интерфейс фрагмента.
Теперь создадим сам класс фрагмента. Для этого добавим в одну папку с MainActivity новый класс. Для этого нажмем на папку правой кнопкой мыши и выберем в меню New -> Java Class . Назовем новый класс ContentFragment и определим у него следующее содержание:
Класс фрагмента должен наследоваться от класса Fragment .
Чтобы указать, что фрагмент будет использовать определенный xml-файл layout, идентификатор ресурса layout передается в вызов конструктора родительского класса (то есть класса Fragment).
Весь проект будет выглядеть следующим образом:
Добавление фрагмента в Activity
Для использования фрагмента добавим его в MainActivity . Для этого изменим файл activity_main.xml , которая определяет интерфейс для MainActivity:
Для добавления фрамента применяется элемент FragmentContainerView . По сути FragmentContainerView представляет объект View, который расширяет класс FrameLayout и предназначен специально для работы с фрагментами. Собственно кроме фрагментов он больше ничего содержать не может.
Его атрибут android:name указывает на имя класса фрагмента, который будет использоваться. В моем случае полное имя класса фрагмента с учетов пакета com.example.fragmentapp.ContentFragment .
Код класса MainActivity остается тем же, что и при создании проекта:
Если мы запустим приложение, то мы увидим фактически тот же самый интерфейс, который мы могли бы сделать и через activity, только в данном случае интерфейс будет определен во фрагменте:
Стоит отметить, что Android Studio представляет готовый шаблон для добавления фрагмента. Собственно воспользуемся этим способом.
Для этого нажмем на папку, где находится класс MainActivity , правой кнопкой мыши и в появившемся меню выберем New -> Fragment -> Fragment(Blank) :
Данный шаблон предложить указать класс фрагмента и название файла связанного с ним класса разметки интерфейса.
Добавление логики к фрагменту
Фрагмент определяет кнопку. Теперь добавим к этой кнопки некоторое действие. Для этого изменим класс ContentFragment:
Здесь переопределен метод onViewCreated класса Fragment, который вызывается после создания объекта View для визуального интерфейса, который представляет данный фрагмент. Созданный объект View передается в качестве первого параметра. И далее мы можем получить конкретные элементы управления в рамках этого объекта View, в частности, TextView и Button, и выполнить с ними некоторые действия. В данном случае в обработчике нажатия кнопки в текстовом поле выводится текущая дата.
Добавление фрагмента в коде
Кроме определения фрагмента в xaml-файле интерфейса мы можем добавить его динамически в activity.
Для этого изменим файл activity_main.xml :
И также изменим класс MainActivity :
Метод getSupportFragmentManager() возвращает объект FragmentManager , который управляет фрагментами.
Объект FragmentManager с помощью метода beginTransaction() создает объект FragmentTransaction .
FragmentTransaction выполняет два метода: add() и commit(). Метод add() добавляет фрагмент: add(R.id.fragment_container_view, new ContentFragment()) — первым аргументом передается ресурс разметки, в который надо добавить фрагмент (это определенный в activity_main.xml элемент androidx.fragment.app.FragmentContainerView ). И метод commit() подтвержает и завершает операцию добавления.
Итоговый результат такого добавления фрагмента будет тем же, что и при явном определении фрагмента через элемент FragmentContainerView в разметке интерфейса.
Источник