Создание анимаций переходов между Activity в Android
Начиная с Android 4.4 в арсенале разработчиков появился дополнительный инструмент для создания анимаций - Transitions Framework. Изначально он предназначался для создания анимаций изменения состояния приложения путём манипулирования несколькими View. С выходом Android 5.0 набор доступных для использования анимаций был расширен, чтобы соответствовать представленной тогда же концепции Material Design.
Transitions Framework позволяет быстро и безболезненно создавать различные анимации. Поэтому в процессе работы над iFunny было невозможно пройти мимо этого инструментария. Вниманию читателей предлагается частный случай использования Transitions API - создание анимации перехода между Activity с эффектом «бесшовности».
С визуальной точки зрения представленные в Transitions Framework анимации переходов между Activity можно условно разделить на два типа: обычные анимации и анимации с общим элементом. Концепт анимации с общим элементом продемонстрирован на честно украденном с сайта developer.android.com рис. 1. На нём в роли общих элементов выступают аватар и имя контакта.
Рис. 1. Анимация перехода между Activity с общими элементами
Но никто не любит длинные вступления, поэтому сразу перейдём к рассказу о том, как создавались анимации данного типа в приложении iFunny. В качестве первого примера рассмотрим анимацию, показанную на рис. 2. Для её использования нам потребуется Android версии 5.0 и выше.
Рис. 2. Анимация перехода между Activity на экране аутентификации пользователя
С точки зрения пользователя, здесь нет ничего необычного: один экран, простенькая анимация. Но, как вы уже могли догадаться, «под капотом» - переход между двумя экранами с одним общим элементом.
Первым шагом к созданию подобного перехода является, как ни странно, выбор этого самого элемента и определение его местоположения в вёрстке обеих Activity. После этого в описание каждого View, отображающего выбранный элемент, нужно добавить атрибут android:transitionName, а также назначить им android:id, если таковой отсутствует.
В нашем случае это обычные ImageView следующего вида:
Здесь стоит отметить два важных момента. Во-первых, в обоих ImageView необходимо установить одинаковые transitionName, что логично. Во-вторых, коль скоро мы используем ImageView, то и содержимое у них должно быть одним и тем же, поскольку использование двух отличающихся ресурсов может привести к неожиданным последствиям (как минимум к морганию анимируемого View в начале и конце анимации).
На втором шаге необходимо добавить опции для запускаемой (второй) Activity, сообщающие о том, что при её запуске должна быть запущена анимация.
Примечание. Под «второй» подразумевается запускаемая Activity, переход к которой должен быть осуществлён, а под «первой» - запускающая Activity.
Делается это следующим образом:
Bundle bundle = null;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
View v = activity.findViewById(R.id.auth_logo);
if (v != null) {
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, v, activity.getString(R.string.email_auth_transition));
bundle = options.toBundle();
}
}
Intent intent = new Intent(activity, SecondActivity.class);
if (bundle == null) {
activity.startActivity(intent);
} else {
activity.startActivity(intent, bundle);
}
В приведённом листинге:
- R.id.auth_logo - ImageView из первой Activity, используемый в анимации;
- activity - первая Activity;
- R.string.email_auth_transition - метка, ранее оставленная в вёрстке обоих ImageView;
- SecondActivity.class - вторая Activity.
На третьем шаге необходимо описать анимацию перехода, т.е. указать путь, проходимый анимируемым View, и трансформацию самого View. Для этого создадим отдельный файл projectName/src/main/res/transitions/email_auth_transition.xml со следующим содержимым:
Немного теории. Тег transitionSet предназначен для описания сразу нескольких трансформаций, применяемых к анимируемому View. Параметр transitionOrdering отвечает за порядок применения этих трансформаций. В нашем случае они применяются одновременно. Существует несколько типов готовых трансформаций, представленных в Transitions Framework. С полным списком можно ознакомиться на этой странице . Мы же остановимся на двух конкретных: changeBounds и changeImageTransform.
Первая предназначена для трансформации размера View. Вторая работает только с ImageView и в связке с первой позволяет изменять не только размер, но и форму ImageView. Использовав данные трансформации, получаем на выходе анимацию изменения размера изображения, представленную на рис. 2. Если не указывать тип движения анимируемого View, то он будет двигаться по кратчайшему пути. Более интересный способ передвижения рассмотрим во втором примере.
Последним шагом создания анимации является её объявление в темах обеих Activity. Для этого отредактируем описание тем следующим образом (или создадим новые в папке projectName/src/main/res/values-v22/theme.xml):
Здесь:
- android:windowActivityTransitions разрешает выполнение анимации перехода;
- android:windowSharedElementEnterTransition указывает на файл с описанием анимации перехода от первой Activity ко второй;
- android:windowSharedElementExitTransition указывает на файл с описанием анимации перехода при возвращении из второй Activity в первую.
Итак, для создания анимации перехода от Activity к Activity необходимо:
- Описать анимации (в нашем случае в xml-файле);
- Добавить эти анимации в xml-описание темы Activity;
- Пометить анимируемый общий элемент (View) в разметке;
- При запуске второй Activity указать в параметрах запуска, что для неё необходимо задействовать анимацию перехода.
Рис. 3. Анимация перехода из комментариев к профилю пользователя
Все шаги по созданию перехода, рассмотренные выше, также подходят для этой анимации. А вот трансформация общего элемента реализована немного иначе. В приведённом ниже листинге описано перемещение общего элемента «по дуге» вместе с изменением его размера.
В чём же сложность второго примера? В первом случае использовалось изображение из ресурсов самого приложения, а тут - картинка загружается из сети. К тому же для комментариев изображение аватара пользователя берётся в более низком разрешении, чем для профиля. Поэтому требуется не только дать второй Activity доступ к изображению, используемому в первой, но и по завершении анимации подгрузить требуемое изображение в более высоком качестве. Так и получается две проблемы.
Для решения первой можно было бы собственноручно закэшировать изображение на диск или же передать его адрес в параметре второй Activity. Однако решение данной проблемы переложили на используемую в приложении библиотеку для загрузки изображений - Glide. При загрузке изображения достаточно просто добавить параметр diskCacheStrategy(DiskCacheStrategy.SOURCE), и оно будет закэшировано самой библиотекой (актуально для Glide версии 3.x). Следовательно, при повторном обращении к данному ресурсу из второй Activity будет использоваться кэшированный файл, что поможет нам избежать моргания анимируемого ImageView.
Вторая проблема также решается достаточно просто. В то время как осуществляется анимация перехода, профиль пользователя вместе с аватаром в более высоком разрешении скачиваются из сети и ожидают её завершения. Как только выполняются оба условия (завершение анимации и завершение загрузки), аватар пользователя обновляется. Добиться такого поведения можно, если использовать специальный Listener, в котором реализованы колбэки, вызываемые при смене статуса анимации. Для этого во Fragment, который принадлежит второй Activity, зададим этот самый Listener:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
getActivity().getWindow().getSharedElementEnterTransition()
.addListener(mEnterTransitionListener);
}
setAvatar();
}
Здесь происходит следующее:
- С помощью getSharedElementEnterTransition().addListener() задаётся Listener для анимации появления Activity;
- В методе setAvatar() производится попытка загрузки и установки аватара (который уже лежит в кэше).
Private Transition.TransitionListener mEnterTransitionListener =
new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
onProfileUpdated();
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
};
В методе onProfileUpdated() мы обновляем содержимое профиля, в т.ч. и аватар.
Стоит отдельно упомянуть случай, когда общий элемент уходит за пределы экрана. Особенность его состоит в том, что, вопреки (а может, и согласно) логике, анимация перехода всё равно будет выполнена и будет смотреться достаточно забавно (рис. 4).
Рис. 4. Анимация возвращения из профиля в комментарии
Чтобы избежать подобного поведения, достаточно в момент ухода общего элемента за пределы экрана выставить ему видимость, отличную от View.VISIBLE.
В целом можно сказать, что Transitions Framework является простым и мощным инструментом для создания анимаций. Он не ограничивается только анимациями перехода между Activity - в статье был рассмотрен лишь частный случай его использования. Также стоит отметить, что помимо предоставляемых трансформаций имеется возможность создавать свои собственные, но это уже совсем другая история, достойная отдельного поста.
P.S. А о том, как придумывались анимации для iFunny, вы можете прочитать .
Теги:
- android
- material design
Обозначение металлических труб. Рассмотрим обозначения наиболее распространенных металлических трубопроводов согласно ГОСТ. Стальные водогазопроводные трубы по ГОСТ В обозначении трубы указывают условны диаметр, длину (при использовании труб мерной длины), толщину стенки. Рассмотрим пример обозначения трубы с диаметром условного прохода 32 мм, толщиной стенки 2,8 мм, без покрытия цинком: Труба 32х2,8 ГОСТ Наличие цинкового покрытия обозначают буквой Ц, которую размещают после слова "Труба".
При необходимости в обозначении трубы указывается отметка о наличии резьбы. Маркировка стальных труб по ГОСТ производится на заводе после изготовления и содержит информацию о размере, марке стали, из которой изготовлена труба, а также товарный знак производителя. Размер букв и цифр клеймления прямо пропорционально зависит от размера изделия и может наноситься несколькими методами.
Самые распространенные – клеймление и водостойкая краска. Виды контроля и их обозначение в маркировке изделий рассмотрены в таблице (Таблица); Следующий пункт «» обозначает толщину стенки в миллиметрах; Четвертое значение указывает длину изделия в миллиметрах от среза до среза, в данном случае это «».
Как расшифровать маркировку стальных труб: диаметр, марка стали и иные показатели по ГОСТу. Условные обозначения, нанесенные на стальную трубу, дают исчерпывающую информацию о продукции.
Маркировка – своеобразный паспорт изделия, из которого становится понятно, кто, где и для каких целей его изготовил. О том, как правильно расшифровать все символы маркировки на трубах, расскажет эта статья.
Примечание. Маркировка труб стальных (и чугунных) регламентируется ГОСТ № от года. В этом документе определены все нюансы нанесения пояснительных надписей, их размеры, расстояния между цифрами и.
Трубы стальные бесшовные холоднодеформированные ГОСТ (Сортамент) Настоящий стандарт распространяется на холоднодеформированные бесшовные трубы общего назначения из углеродистой и легированной стали. Диаметры: 5 - мм. В условных обозначениях труб индекс А или В проставляется перед маркой стали. Трубы изготовляют термически обработанными или без термической обработки.
Концы труб должны быть обрезаны под прямым углом. Способы нанесения маркировки на стальные трубы: стандарты ГОСТ, примеры расшифровки условных обозначений. Прочитав цифры на изделии, специалист может определить размер трубы, ее тип, марку стали, прочность, предприятие-изготовитель и некоторые другие параметры изделия.
Маркировка стальных труб специального назначения содержит дополнительную маркировку. К изделиям специального назначения относятся: Трубы из легированных марок стали. 4. Ссы ЛО чн Ы Е Норм ативно-технические докум енты. Обозначение НТД. на который дана ссылка. Ш с г -80 ГОСТ ю С1 Номер пункта. Изменение № 2 ГОСТ - 91 Трубы стальные элоктросвариые прямош овные.
Сортамент Принято Межгосударственным советом по стандартизации, метрологии и сертификации по пере писке (протокол № 45- от) Зарегистрировано Бюро по стандартам МГС № За принятие изменения проголосовали национальные органы по стандартизации следующих госу дарств: BY, KG, RU, TJ [код ы альфа-2 по МК (ИСО) ].
Продажа и покупка. Трубы стальные обозначение. Трубы стальные электросварные прямошовные. Сортамент. ГОСТ ИПК Издательство с тандартов. Государственный стандарт союза сср. Трубы стальные электросварные пр ямо шов ные. С ортамент. Electrically w elded steel line-weld lubes. Range. ГОСТ Дата введ ения 1. Настоящи й стандарт устана вливает сор тамент стальных электросварных прямошовных труб.
2. Размеры труб должны соответство вать табл. 1. 3. По длине трубы изготовляют: немерной длины Обозначение НТД, на который дана ссылка. Номер пункта. ГОСТ
Categories Post navigationНачиная с Android 4.4 в арсенале разработчиков появился дополнительный инструмент для создания анимаций - Transitions Framework. Изначально он предназначался для создания анимаций изменения состояния приложения путём манипулирования несколькими View. С выходом Android 5.0 набор доступных для использования анимаций был расширен, чтобы соответствовать представленной тогда же концепции Material Design.
Transitions Framework позволяет быстро и безболезненно создавать различные анимации. Поэтому в процессе работы над iFunny было невозможно пройти мимо этого инструментария. Вниманию читателей предлагается частный случай использования Transitions API - создание анимации перехода между Activity с эффектом «бесшовности».
С визуальной точки зрения представленные в Transitions Framework анимации переходов между Activity можно условно разделить на два типа: обычные анимации и анимации с общим элементом. Концепт анимации с общим элементом продемонстрирован на честно украденном с сайта developer.android.com рис. 1. На нём в роли общих элементов выступают аватар и имя контакта.
Рис. 1. Анимация перехода между Activity с общими элементами
Но никто не любит длинные вступления, поэтому сразу перейдём к рассказу о том, как создавались анимации данного типа в приложении iFunny. В качестве первого примера рассмотрим анимацию, показанную на рис. 2. Для её использования нам потребуется Android версии 5.0 и выше.
Рис. 2. Анимация перехода между Activity на экране аутентификации пользователя
С точки зрения пользователя, здесь нет ничего необычного: один экран, простенькая анимация. Но, как вы уже могли догадаться, «под капотом» - переход между двумя экранами с одним общим элементом.
Первым шагом к созданию подобного перехода является, как ни странно, выбор этого самого элемента и определение его местоположения в вёрстке обеих Activity. После этого в описание каждого View, отображающего выбранный элемент, нужно добавить атрибут android:transitionName, а также назначить им android:id, если таковой отсутствует.
В нашем случае это обычные ImageView следующего вида:
Здесь стоит отметить два важных момента. Во-первых, в обоих ImageView необходимо установить одинаковые transitionName, что логично. Во-вторых, коль скоро мы используем ImageView, то и содержимое у них должно быть одним и тем же, поскольку использование двух отличающихся ресурсов может привести к неожиданным последствиям (как минимум к морганию анимируемого View в начале и конце анимации).
На втором шаге необходимо добавить опции для запускаемой (второй) Activity, сообщающие о том, что при её запуске должна быть запущена анимация.
Примечание. Под «второй» подразумевается запускаемая Activity, переход к которой должен быть осуществлён, а под «первой» - запускающая Activity.
Делается это следующим образом:
Bundle bundle = null; if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { View v = activity.findViewById(R.id.auth_logo); if (v != null) { ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, v, activity.getString(R.string.email_auth_transition)); bundle = options.toBundle(); } } Intent intent = new Intent(activity, SecondActivity.class); if (bundle == null) { activity.startActivity(intent); } else { activity.startActivity(intent, bundle); }
В приведённом листинге:
- R.id.auth_logo - ImageView из первой Activity, используемый в анимации;
- activity - первая Activity;
- R.string.email_auth_transition - метка, ранее оставленная в вёрстке обоих ImageView;
- SecondActivity.class - вторая Activity.
И сейчас внимательный читатель может испытать недоумение: во вступлении речь шла об использовании API level 19, в примере фигурировал API level 21, а в листинге выше стоит ограничение на API level 22. К сожалению, при написании кода выяснилось, что анимации перехода с общим элементом могут вести себя некорректно на телефонах c API level 21. Проявляется это в виде подтормаживаний анимации в целом и артефактов на анимируемом View в частности. Если вы уже знакомы с темой, знаете причины подобного поведения и/или способы решения описанной проблемы - расскажите нам об этом в комментариях.
На третьем шаге необходимо описать анимацию перехода, т.е. указать путь, проходимый анимируемым View, и трансформацию самого View. Для этого создадим отдельный файл projectName/src/main/res/transitions/email_auth_transition.xml со следующим содержимым:
Немного теории. Тег transitionSet предназначен для описания сразу нескольких трансформаций, применяемых к анимируемому View. Параметр transitionOrdering отвечает за порядок применения этих трансформаций. В нашем случае они применяются одновременно. Существует несколько типов готовых трансформаций, представленных в Transitions Framework. С полным списком можно ознакомиться на этой странице . Мы же остановимся на двух конкретных: changeBounds и changeImageTransform.
Первая предназначена для трансформации размера View. Вторая работает только с ImageView и в связке с первой позволяет изменять не только размер, но и форму ImageView. Использовав данные трансформации, получаем на выходе анимацию изменения размера изображения, представленную на рис. 2. Если не указывать тип движения анимируемого View, то он будет двигаться по кратчайшему пути. Более интересный способ передвижения рассмотрим во втором примере.
Последним шагом создания анимации является её объявление в темах обеих Activity. Для этого отредактируем описание тем следующим образом (или создадим новые в папке projectName/src/main/res/values-v22/theme.xml):
- android:windowActivityTransitions разрешает выполнение анимации перехода;
- android:windowSharedElementEnterTransition указывает на файл с описанием анимации перехода от первой Activity ко второй;
- android:windowSharedElementExitTransition указывает на файл с описанием анимации перехода при возвращении из второй Activity в первую.
Следует отметить, что для версий ОС ниже 5.1 необходимо создать темы с идентичными стилями, чтобы избежать вполне ожидаемых последствий в виде падения приложения. Например, поместим их в файл projectName/src/main/res/values/theme.xml:
Итак, для создания анимации перехода от Activity к Activity необходимо:
- Описать анимации (в нашем случае в xml-файле);
- Добавить эти анимации в xml-описание темы Activity;
- Пометить анимируемый общий элемент (View) в разметке;
- При запуске второй Activity указать в параметрах запуска, что для неё необходимо задействовать анимацию перехода.
Как видите, создавать такие анимации совсем не трудно, если не считать некоторых ограничений, упомянутых в первом примере. Теперь рассмотрим второй, более сложный пример. Тут нас интересует переход из раздела комментариев к профилю пользователя (рис. 3).
Рис. 3. Анимация перехода из комментариев к профилю пользователя
Все шаги по созданию перехода, рассмотренные выше, также подходят для этой анимации. А вот трансформация общего элемента реализована немного иначе. В приведённом ниже листинге описано перемещение общего элемента «по дуге» вместе с изменением его размера.
В чём же сложность второго примера? В первом случае использовалось изображение из ресурсов самого приложения, а тут - картинка загружается из сети. К тому же для комментариев изображение аватара пользователя берётся в более низком разрешении, чем для профиля. Поэтому требуется не только дать второй Activity доступ к изображению, используемому в первой, но и по завершении анимации подгрузить требуемое изображение в более высоком качестве. Так и получается две проблемы.
Для решения первой можно было бы собственноручно закэшировать изображение на диск или же передать его адрес в параметре второй Activity. Однако решение данной проблемы переложили на используемую в приложении библиотеку для загрузки изображений - Glide. При загрузке изображения достаточно просто добавить параметр diskCacheStrategy(DiskCacheStrategy.SOURCE), и оно будет закэшировано самой библиотекой (актуально для Glide версии 3.x). Следовательно, при повторном обращении к данному ресурсу из второй Activity будет использоваться кэшированный файл, что поможет нам избежать моргания анимируемого ImageView.
Вторая проблема также решается достаточно просто. В то время как осуществляется анимация перехода, профиль пользователя вместе с аватаром в более высоком разрешении скачиваются из сети и ожидают её завершения. Как только выполняются оба условия (завершение анимации и завершение загрузки), аватар пользователя обновляется. Добиться такого поведения можно, если использовать специальный Listener, в котором реализованы колбэки, вызываемые при смене статуса анимации. Для этого во Fragment, который принадлежит второй Activity, зададим этот самый Listener:
@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { getActivity().getWindow().getSharedElementEnterTransition() .addListener(mEnterTransitionListener); } setAvatar(); }
Здесь происходит следующее:
- С помощью getSharedElementEnterTransition().addListener() задаётся Listener для анимации появления Activity;
- В методе setAvatar() производится попытка загрузки и установки аватара (который уже лежит в кэше).
Рассмотрим, как именно реализован Listener:
Private Transition.TransitionListener mEnterTransitionListener = new Transition.TransitionListener() { @Override public void onTransitionStart(Transition transition) { } @Override public void onTransitionEnd(Transition transition) { onProfileUpdated(); } @Override public void onTransitionCancel(Transition transition) { } @Override public void onTransitionPause(Transition transition) { } @Override public void onTransitionResume(Transition transition) { } };
В методе onProfileUpdated() мы обновляем содержимое профиля, в т.ч. и аватар.
Стоит отдельно упомянуть случай, когда общий элемент уходит за пределы экрана. Особенность его состоит в том, что, вопреки (а может, и согласно) логике, анимация перехода всё равно будет выполнена и будет смотреться достаточно забавно (рис. 4).
Рис. 4. Анимация возвращения из профиля в комментарии
Чтобы избежать подобного поведения, достаточно в момент ухода общего элемента за пределы экрана выставить ему видимость, отличную от View.VISIBLE.
В целом можно сказать, что Transitions Framework является простым и мощным инструментом для создания анимаций. Он не ограничивается только анимациями перехода между Activity - в статье был рассмотрен лишь частный случай его использования. Также стоит отметить, что помимо предоставляемых трансформаций имеется возможность создавать свои собственные, но это уже совсем другая история, достойная отдельного поста.
P.S. А о том, как придумывались анимации для iFunny, вы можете прочитать .
Всем доброго времени суток. Этот пост хочу посвятить теме фрагментов для Android. На Хабре есть уже переводы и некоторые статьи, которые упоминают о том, как начать работать с фрагментами под Android. Например, статья . В ней находится описание того, что такое фрагменты и в какой версии Android они доступны, поэтому те, кто ещё не добрался до неё могут при желании ознакомиться, я же не буду пересказывать этого в своём посте. Поэтому сразу перейду к делу.
Начало работы
Скажу только кратко, что фрагменты - это компоненты UI пользователя, которые могут использоваться с помощью класса Activity для отображение пользовательских данных, но их жизненный цикл от него не зависит. Функционал, которым наделены фрагменты имеет более широкий функционал для работы с ними, чем Activity, поэтому их использование для разработчиков имеет не малое значение, если они хотят, чтобы их приложение имело более современный по нынешним нормам интерфейс пользователя.Теперь перейдём к сути поста. Разработчиками Google фрагменты были наделены, на мой взгляд, отличной поддержкой анимации отображения самого фрагмента. Об этом и пойдёт далее речь. Я искал по Хабру посты на данную тематику, но так ничего и не нашёл, поэтому сейчас поделюсь своими знаниями.
Создаём проект
Давайте создадим небольшой проект. Я создал проект под свой Samsung Nexus S, там у меня стоит версия Android 4.1.2, собственно её я и использовал (Api Level 16). Сам проект я назвал FragmentsAnimationTest.Для демонстрации нам понадобится главное активити и его лейаут, пара фрагментов, каждый также со своим лейаутом и ещё пара xml-файлов для самой анимации, о которых я расскажу позже.
Приложение будет выглядеть следующим образом: на экране будет отображаться один из фрагментов, переключение между ними будет осуществляться с помощью обычной кнопки, и, соответственно, само переключение фрагментов будет сопровождаться анимационными эффектами.
Сначала расположим элементы главного активити в файле activity_main.xml:
Из кода видно, что используется главный лейаут - RelativeLayout, достаточно удобный при работе с фрагментами, в него помещаются два стандартных элемента FrameLayout - собственно, он и будет являться контейнером для фрагментов и кнопка, которой будем переключать фрагменты между собой. Пока что всё должно быть предельно просто.
Дальше займёмся нашими фрагментами. Создадим для них разметки и сами классы:
fragment1.xml
Fragment2.xml
Для обоих фрагментов код практически одинаков, отличаются только текстом, который будет отображаться в самом фрагменте для его идентификации и цветом фона, чтобы хорошо было видно анимацию.
Fragment1.java
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_1, null);
}
}
Fragment2.java
public class Fragment2 extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_2, null);
}
}
В классах также должно быть всё понятно, если знакомы с темой фрагментов. В них просто указывается какой именно леаут будет использоваться при отображении конкретного фрагмента и всё.
Теперь приступим к самому вкусному. Поработаем с классом главной активити, вот её код:
public class MainActivity extends Activity {
private Fragment fragment2;
private Fragment fragment1;
private FragmentTransaction ft;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragment1 = new Fragment1();
fragment2 = new Fragment2();
ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.animator.slide_in_left, R.animator.slide_in_right);
// ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.replace(R.id.fragCont, fragment1);
ft.addToBackStack(null);
ft.commit();
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.animator.slide_in_left, R.animator.slide_in_right);
// ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if(fragment1.isVisible()){
ft.replace(R.id.fragCont, fragment2);
}else{
ft.replace(R.id.fragCont, fragment1);
}
ft.commit();
}
});
}
}
Разберём что именно происходит в нашей активити. Сначала создаются оба фрагмента, которые, как уже говорилось, будут поочерёдно менять друг друга. Далее указываем строкой ft = getFragmentManager().beginTransaction() получаем FragmentTransaction, с помощью которой мы сможем взаимодействовать с нашими фрагментами, но это всё есть в статье, которую я указывал ранее. Прежде, чем перейти к разбору следующего кода, сделаю небольшое отступление.
Существует две возможности создавать анимацию для отображения фрагментов:
- 1ый способ - это подключение стандартной анимации с помощью метода setTransition(int transit). В классе FragmentTransaction есть несколько уже предописанных анимаций.
- 2ой способ - это именно то, что нас интересует в данной теме, реализация кастомной анимации. Осуществляется с помощью метода setCustomAnimations()
Пара сток-комментариев - это для того, чтобы можно было попробовать поиграться с предописанной анимацией, достаточно их раскомментировать и закомментировать предыдущую строку - ft.setCustomAnimations(R.animator.slide_in_left, R.animator.slide_in_right), в обоих случаях, хотя это и необязательно.
Давайте разберём код активити до конца и перейдём к созданию самой анимации.
После установки анимации, происходит показ фрагмента, добавление его в стек и завершение транзакции для отображения всех изменений. Потом инициализируем нашу кнопку и прикрепляем к ней слушатель события нажатия кнопки, внутри которого находится код для смены фрагментов, по нажатию кнопки начинаем транзакцию, подключаем анимацию и меняем фрагмент на противоположный показываемому в настоящий момент. Код прост, поэтому глубокого объяснения не требует.
Создаём анимацию
Перейдём к главной части нашей темы. Научимся создавать саму анимацию. Способ создания анимации здесь немного отличается от того, как мы привыкли это делать в ранних версиях Android. Реализация проходит следующим образом. Для на чала нужно создать папку animator в папке ресурсов приложения, это будет выглядеть так - res/animator/. Сюда мы должны положить xml-файлы, которые будут описывать как именно должна проигрываться анимация. Поместим их туда:slide_in_left.xml
И slide_in_right.xml
Теперь подробно их разберём. Элементы самих визуальных эффектов описываются в теге objectAnimator, каждый такой тег говорит об описании нового эффекта анимации. Теперь посмотрим на сами атрибуты. Первый атрибут в файле slide_in_left.xml - это interpolator, у него имеется несколько значений, о них можно более подробно узнать из документации Property Animation . Interpolator отвечает за то, чтобы отобразить определённым способом в течении определённого времени наш фрагмент. Далее у нас следует атрибут propertyName, в нём указывается с какое именно свойство фрагмента мы будем изменять при анимации, в нашем примере первым идёт y, а valueType указывает какого типа у нас именяемый параметр. В книге Pro Android 4 аргументируется эта ситуация тем, что если посмотреть на метод setX() в классе View, то станет понятно, что он принимает значение типа float, с методом setY() дело обстоит также, отсюда и значение floatType.
Далее идут не маловажные атрибуты valueFrom и valueTo, они указывают от какого до какого значания изменять значение указаннное в propertyName, в нашем первом случае это y. Если параметр valueFrom не указан, то значение берётся равное текущему. В нашем случае valueFrom равен -1280, это означает, что движение фрагмента по оси y будет начинаться со значения -1280, это значение было выбрано из-за того, что оно находится за пределами экрана устройства и перемещение будет происходить пока значение y не станет равным 0 для верхнего левого угла нашего фрагмента в течении 1500 миллисекунд. И, наконец, duration - атрибут указывает сколько именно будет длиться наш анимированный эффект в миллисекундах.
И последний нюанс, который я хочу описать. Глядя в какой-либо из файлов описания анимации, можно заметить тэг set, в который помещены все эффекты анимации, он служит для объединения эффектов либо их разделения. В файле slide_in_right.xml используется атрибут ordering в теге set, в нашем случае он имеет значение together, что означает проигрывать эффекты одновременно, в противовес ему существует значение sequentially, которое требует последовательного отображения эффектов в анимации, что очень удобно в некоторых случаях.
Собственно и всё. В файле slide_in_right.xml приведен пример как можно использовать другие свойства для анимирования, например alpha-канал. Надеюсь данная статья пригодится тем, кому не всё равно как будет выглядеть его приложение.
Как вы сами понимаете, уважаемые хабраюзеры, скрины не смогут отобразить результат работы.
Литература и источники, которые использовались при написании поста были упомянуты в ходе самой статьи.
В этой статье рассмотрим как сделать анимацию элементов интерфейса в android. Под элементами интерфейса в данном случае имеются в виду все наследники класса View (полный список наследников можно посмотреть в документации класса View). Анимация это простой способ сделать приложение более живеньким:)
1.
Начнем с создания тестового полигона. Сделаем простое приложение с кнопкой и картинкой посередине экрана. Код приводить не буду, он простой, если что, смотрите в исходниках (они в конце статьи).
2.
В директории /res/anim создадим файл anim.xml и напишем туда
xml
version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
<
set
xmlns:android="
http
:
//
schemas.android.com
/apk/res/android
"
android:shareInterpolator="
false
"
>
<
alpha
android:fromAlpha="
0.0
"
android:toAlpha="
1.0
"
android:duration="
1000
"
/>
set
>
Это описание анимации, которое мы будем применять к нашей картинке. Подробнее что тут происходит рассмотрим ниже, а пока просто скопируем это в файл.
3.
Чтобы загрузить анимацию из xml файла используется статический метод класса AnimationUtils
loadAnimation(Context context, int id)
, где context
- текущий контекст, а id
- идентификатор ресурса с анимацией. Метод возвращает экземпляр класса Animation.
Animation - абстрактный класса для представления анимации в приложении.
Чтобы применить ее, полученный экземпляр класса Animation передается методу
startAnimation(Animation animation)
класса View (и всех его наследников).
4.
Напишем в файл AnimationTestActivity.java:
public
class
AnimationTestActivity extends
Activity {
ImageView image;
Button
button;
Animation anim;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
image = (ImageView)findViewById(R.id.image);
button = (Button
)findViewById(R.id.button);
anim = AnimationUtils.loadAnimation(this
, R.anim.anim); // 1
button.setOnClickListener(new
OnClickListener() {
@Override
public
void
onClick(View v) {
image.startAnimation(anim); //2
}
});
}
}
1) Читаем файл с идентификатором R.anim.anim (что соответствует файлу /res/anim/anim.xml) и получаем экземпляр класса Animation.
2) По нажатию на кнопку применяем анимацию для изображения.
5. Можно запустить наше приложение. По нажатию на кнопку, картинка исчезнет, а потом медленно начнет прорисовываться обратно.
6.
Теперь рассмотрим подробно как же создается анимация в xml файле.
Существует 4 вида анимации:
- alpha (прозрачность, видимость)
- scale (масштабирование)
- rotate (поворот)
- translate (перемещение)
Для создания анимации мы должны описать начальное и конечное состояние объекта, а система сама решит как перейти из одного состояния в другое. В нашем примере
<
alpha
android:fromAlpha="
0.0
"
android:toAlpha="
1.0
"
android:duration="
1000
"
/>
мы описываем анимацию alpha, то есть изменяем видимость объекта. Задаем начальное состояние fromAlpha="0.0"
(полностью невидимое) и конечное toAlpha="1.0"
(полностью видимое). Указываем продолжительность анимации duration="1000"
(в миллисекундах). А все остальное, то есть как нужно изменять видимость объекта чтобы за секунду сделать его из невидимого в видимое, система делает сама. Рассчитывается это с помощью интерполяции
- в вычислительной математике способ нахождения промежуточных значений величины по имеющемуся дискретному набору значений. Для каждой анимации можно задать интерполятор
-AccelerateDecelerateInterpolator
(@android:anim/accelerate_decelerate_int erpolator) - скорость изменения в начале и конце низкая, а в середине ускоряется
-AccelerateInterpolator (@android:anim/accelerate_interpolator) - скорость изменения в начале низкая, а затем ускоряется
-AnticipateInterpolator (@android:anim/anticipate_interpolator) - изменения начинаются в обратную сторону, а затем резко двигаются вперед
-AnticipateOvershootInterpolator (@android:anim/anticipate_overshoot_inte rpolator) - изменения начинаются в обратную сторону, затем резко двигаются вперед и пролетают выше конечного значения, а затем возвращаются до конечного значения
-BounceInterpolator (@android:anim/bounce_interpolator) - скорость изменения увеличивается в конце
-CycleInterpolator (@android:anim/cycle_interpolator) - повторение анимации указанное число раз. Скорость изменения следует синусоиде
-DecelerateInterpolator (@android:anim/decelerate_interpolator) - скорость изменения уменьшается в конце
-LinearInterpolator (@android:anim/linear_interpolator) - скорость изменения постоянна
-OvershootInterpolator (@android:anim/overshoot_interpolator) - изменения резко двигаются вперед и пролетают выше конечного значения, а затем возвращаются до конечного значения
Задается интерполятор с помощью атрибута android:interpolator. Например
android:interpolator="@android:anim/cycl e_interpolator"
. По умолчанию используется LinearInterpolator.
7. Описание начальных и конечных состояний
1) alpha (прозрачность, видимость)
- android:fromAlpha
- начальное значение прозрачности. 0.0 - полностью прозрачное (невидимое), 1.0 - полностью непрозрачное (видимое)
- android:toAlpha
- конечное значение прозрачности
2) scale (масштабирование)
- android:fromXScale
- начальное значение масштаба по оси X (где текущий размер соответствует значению 1.0)
- android:toXScale
- конечное значение масштаба по оси X
- android:fromYScale
- начальное значение масштаба по оси Y (где текущий размер соответствует значению 1.0)
- android:toYScale
- конечное значение масштаба по оси Y
- android:pivotX
- х координата точки, которая останется неизменна после масштабирования
- android:pivotY
- y координата точки, которая останется неизменна после масштабирования
Возможные значение pivotX и pivotY:
в пикселях относительно левого (или верхнего для координаты Y) края элемента (например «5»)
в процентах относительно левого (верхнего) края (например «5%»)
в процентах относительно левого (верхнего) края родительского элемента (например «5%p»)
Например, если pivotX=0, pivotY=0 (что соответствует верхнему левому углу элемента), то масштабирование будет изменять размер элемента вниз и вправо. Если pivotX=50%, pivotY=50%, то точка находится по центру элемента и размер изменяется во все сторону, при этом центр будет оставаться а в одной точке.
3) rotate (поворот)
- android:fromDegrees
- Начальное значение угла поворота (в градусах, возможно отрицательное значение)
- android:toDegrees
- конечное значение угла поворота
- android:pivotX
- x координаты центра поворота.
- android:pivotY
- y координата центра поворота.
Возможные значения pivotX и pivotY как у анимации scale
4) translate (перемещение)
- android:fromXDelta
- x координата начальной точки перемещения. Возможные значения:
в пикселях относительно изначальной позиции (например «5»)
в процентах относительно ширины элемента (например «5%»)
в процентах относительно ширины родительского элемента (например «5%p»)
- android:toXDelta
- x координата конечной точки перемещения
- android:fromYDelta
- y координата начальной точки перемещения
- android:toYDelta
- y координата конечной точки перемещения
8. Дополнительные параметры
Также есть атрибуты общие для всех четырех типов анимации, наиболее полезные из них:
- android:duration
- длительность анимации (в миллисекундах)
- android:interpolator
- определяет интерполятор для анимации
- android:repeatCount
- кол-во дополнительных повторений анимации. Именно дополнительных, то есть один раз анимация выполнится по любому. Значением по умолчанию является «0» - это значит анимация выполнится только один раз. Значение «1» значит, что анимация выполнится два раза (один раз основной и один раз дополнительный). Значение «-1» или «infinite» - бесконечный повтор.
- android:repeatMode
- определяет поведение анимации, когда она дошла до конца, а параметр repeatCount не равен 0. Есть два значения «restart» - анимация начинается заново и «reverse» - анимация пойдет в обратном порядке.
- android:startOffset
- задержка перед началом анимации (в миллисекундах)
9. Объединение нескольких анимаций
К элементу можно применить одновременно несколько типов анимаций. Например если мы напишем:
xml
version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
<
set
xmlns:android="
http
:
//
schemas.android.com
/apk/res/android
"
>
<
alpha
android:fromAlpha="
0.0
"
android:toAlpha="
1.0
"
android:duration="
1000
"
/>
<
rotate
android:fromDegrees="
0
"
android:toDegrees="
360
"
android:pivotX="
50%
"
android:pivotY="
50%
"
android:duration="
1000
"
/>
set
>
Картинка за 1 секунду изменит прозрачность (с полностью прозрачной до непрозрачной) и при этом повернется на 360 градусов.
Анимациям можно выставлять разную длительность, например поставим duration=5000 у анимации rotate. Теперь картинка будет поворачиваться гораздо медленнее, а прозрачность меняется все-также за секунду.
С помощью startOffset , можно сделать анимации последовательными. Добавим к rotate атрибут startOffset="1000" (то есть сделаем задержку равную длительности первой анимации). Теперь картинка вначале за 1 секунду станет видимой, а затем только повернется на 360 градусов.
Несколько анимаций можно объединять в наборы тегом . Один такой тег будет в файле всегда и является корневым. Для набора можно задавать следующие атрибуты:
- duration
(длительность), repeatMode
(режим повторения) - эти атрибуты будут применяться для каждой анимации в наборе
- interpolator
- определяет интерполятор анимации и shareInterpolator
- будет ли этот интерполятор применятся для каждой анимации в наборе (возможные значения «true» и «false»)
- startOffset
(задержка) - задержка для всего набора анимаций.
К сожалению, к набору нельзя применить атрибут repeatCount
, то есть повторить несколько раз набор анимаций не получится.
Наборы могут быть любой вложенности.
10. Создание анимации без xml
Анимацию можно создать и без использования xml, непосредственно в коде программы. Для этого используются классы наследники Animation:
1) AlphaAnimation для создания анимации alpha. Конструктор класса имеет вид
AlphaAnimation(float fromAlpha, float toAlpha)
где fromAlpha и toAlpha соответственно начальное и конечное значение прозрачности (от 0.0 до 1.0)
11.
Создадим в коде анимацию, которая по нажатию на кнопку будет поворачивать картинку на случайный угол (от 0 до 360) и увеличивать до случайного размера (не более чем в два раза). Я для этого добавила еще одну кнопку randomButton
randomButton.setOnClickListener(new OnClickListener() {
@Override
public
void
onClick(View v) {
Random
random = new
Random
(); //1
RotateAnimation rotate = new
RotateAnimation (0, (float
)random.nextInt(360),
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); //2
rotate.setDuration(1000); //3
rotate.setRepeatMode(Animation.REVERSE); //4
rotate.setRepeatCount(1); //5
long
duration = rotate.computeDurationHint(); //6
float
size = random.nextFloat() + 1.0; //7
ScaleAnimation scale = new
ScaleAnimation(1.0f, size, 1.0f, size,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); //8
scale.setDuration(1000);
scale.setStartOffset(duration); //9
AnimationSet set = new
AnimationSet (false
); //10
set.addAnimation(rotate); //11
set.addAnimation(scale);
image.startAnimation(set); //12
}
});
1) Создаем объект Random, для генерации случайных чисел. Про Random подробнее можно прочитать в документации, сейчас нас интересуют методы int nextInt(int n) - генерирующий целое число в диапозоне от 0 до n. И метод float nextFloat() - генерирующий вещественное число от 0 до 1.
2) Создаем анимацию вращения. Начальный угол = 0, конечный угол = случайному числу от 0 до 360. Animation.RELATIVE_TO_SELF означает, что точку центра поворота мы будем указывать в процентах относительно ширины элемента. Не забываем, что значение 1.0 соотвествует 100%, а значит 0.5f - это 50%. Значит точка центра поврота будет посередине картинки.
3) Задаем длительность анимации 1000 миллисекунд (это 1 секунда)
4) Определяем режим повторения как Animation.REVERSE, то есть при повторении анимация пойдем в обратном порядке.
5) Задаем кол-во дополнительных повторений = 1. Значит всего анимация повторится два раза, один раз в прямом порядке и один в обратном.
6) Метод long computeDurationHint() расчитывает сколько суммарно будет продолжаться анимация. Есть метод getDuration(), но он просто возвращает значение длительности, которое мы задали методом setDuration(). В нашем случае мы задали значение длительности 1000 и метод getDuration() вернет 1000 и не учтет, что анимация будет повторяться два раза, а значит в действительности будет продолжаться 2000 миллисекунд. Метод computeDurationHint() рассчитает длительность с учетом повторов и задержек.
7) Расчитываем новый размер картинки. Значение 1.0 это текущий масштаб картинки, значит значение 2.0 - увеличение картинки в два раза. Мы генерируем число от 0.0 до 1.0 и приплюсовываем 1, значит получаем число от 1.0 до 2.0
8) Создаем анимацию масштабирования от текущего размера картинки до случайно сгенерированного число от 1.0 до 2.0
9) Задаем задержку равную суммарной длительности анимации вращения. Чтобы вторая анимация начиналась сразу после окончания первой
10) Создаем набор анимаций.
11) Добавляем две созданные анимации в набор
12) Применяем набор анимаций к картинке
12.
Еще один интересный метод класса Animation
setAnimationListener (Animation.AnimationListener listener)
- устанавливает слушателя изменений состояний анимации. Интерфейс Animation.AnimationListener определяет следующие методы:
onAnimationStart (Animation animation)
- вызывается при старте анимации
onAnimationRestart (Animation animation)
- вызывается при повторе анимации
onAnimationEnd (Animation animation)
- вызывается по окончании анимации
Например:
anim = AnimationUtils.loadAnimation(this, R.anim.anim);
anim.setAnimationListener(new AnimationListener () {
@Override
public
void
onAnimationEnd(Animation animation) {
Log.d("MY"
, "animation end"
);
}
@Override
public
void
onAnimationRepeat(Animation animation) {
Log.d("MY"
, "animation repeat"
);
}
@Override
public
void
onAnimationStart(Animation animation) {
Log.d("MY"
, "animation start"
);
}
});
Ничего полезного мы при изменении состояния анимации не делаем, просто пишем это в лог.
На этом все. Основное я рассказала, остальное лучше изучать экспериментами:)
Исходники можно скачать тут