Всем доброго времени суток и с Рождеством!) Сегодня я хотел рассказать о создании незамысловатого, но довольно удобного и простого меню в стиле Material Design. Начнем!
Создадим новый проект и в шаблонах выберем Blank Activity
Запускаем, проверяем - все работает отлично. Далее заходим в наш сгенерированный main*.xml (у всех по разному, кто как назвал) и смотрим что же у нас там есть. Там у нас AppBarLayout, Toolbar, FloatingActionButton в обвертке CoordinatorLayout, не плохо так). По замыслу, мы будем создавать меню, которое будет как бы появляться/расширять наш Toolbar. Для этого под Toolbar помещаем RelativeLayout. Сделаем ей высоту в 1dp и цвет самого Toolbar. В результате должно получиться такое:
Можно удалить FloatingActionButton, тут мы его использовать не будем.
Открываем код нашего активити, удаляем onCreateOptionsMenu метод и инициализацию FloatingButton, они нам не пригодятся. Далее добавляем пару строк после объявления Toolbar-аgetSupportActionBar().setDisplayHomeAsUpEnabled(true); toolbar.setNavigationIcon(R.drawable.ic_burger);
Мы включили Home кнопку и поменяли ей иконку. Самое простое закончилось), дальше мы будем анимировать размеры добавленного RelativeLayout.
Может возникнуть вопрос:"Зачем эта возня с левыми слоями, почему просто не манипулировать размерами самого Tollbar-a?". Загвоздка тут в том, что контейнер тулбара - эластичный, изменяя размеры тулбара весь контент (Название активити, функциональный кнопки и тд.) остается в его центре и даже если установить выравнивание, то ничего хорошего при растяжении у вас не получиться. А RelativeLayout всегда был хорошим помощником в создании кастомных меню.
Создадим новый класс, для него я создал отдельный пакет Utils - AnimatingUtils.
Тут мы будем творить чудо нашего "кинематографа". В первую очередь нам понадобиться база, на которой будет работать анимация размеров. Добавим метод slideAnimator.
public static ValueAnimator slideAnimator(int start, int end, final View v) { ValueAnimator animator = ValueAnimator.ofInt(start, end); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { int value = (Integer) valueAnimator.getAnimatedValue(); ViewGroup.LayoutParams layoutParams = v.getLayoutParams(); layoutParams.height = value; v.setLayoutParams(layoutParams); } }); return animator; }
Из метода понятно три вещи: анимация довольно проста, метод универсален почти для любого View и все методы данного класса будут статичными.
При создании классов "помощников" старайтесь, по возможности, делать все методы статичными - это облегчит доступ к ним в любом уголке программы, но не увлекайтесь и выносите только те функции, которые используются по всему приложению.
Чуть не забыл! :) В layout у нас лежит content_main.xml, который вставляется под наш тулбар. Зайдем не надолго на чаек. Удалим текст "Hello Word" и добавим RelativeLayout, сделаем его прозрачным и назначим ему черный цвет и удалим стандартные отступы внутри. Чуть позже я расскажу зачем мы это сделали.
Вернемся к нашему аниматору. Добавим одну переменную - animProgress. Она понадобиться для отслеживания прогресса анимации. И объявим следом AnimatorListener, его я сделал приватным, он понадобиться только в этом классе. И сразу установим значение animProgress: при старте = true, при окончании = false.
public static boolean animProgress = false; private static Animator.AnimatorListener animator = new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { animProgress = true; } @Override public void onAnimationEnd(Animator animator) { animProgress = false; } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } };
Осталось дело за малым. Объявим метод expand. В нем определим границы нашего меню.
public static void expand(View view, final View hide) { final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); view.measure(widthSpec, heightSpec); final float size_menu = hide.getHeight()/3; ValueAnimator mAnimator = slideAnimator(1, (int)size_menu, view); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int animProgress = (Integer) animation.getAnimatedValue(); float anim = animProgress / size_menu / 2.0f; if (anim <= 0.5f) { hide.setAlpha((float) anim); } else { hide.setAlpha(0); } } }); mAnimator.addListener(animator); mAnimator.start(); }
Тут нам и понадобится тот слой, который мы добавили в content_main. Он будет создавать впечатление затухания основного слоя при появлении меню.
Определим константу size_menu и возьмем 1/3 от скрытого слоя, он у нас растянут по габаритам экрана. на самом деле это костыль и лучше бы передавать сразу 1/3 от экрана, но для нашей цели сойдет ^_^.
И еще один момент, почему RelativeLayout нашего меню имеет высоту 1dp и в sliderAnimator мы передаем начальную высоту 1, а не 0. Встретил статью, что android версии ниже 5 не адекватно воспринимает анимацию слоев с высотой в 0.
Добавляем UpdateListener, он понадобиться что бы в рантайме изменять прозрачность слоя затухания.
Тянуть кота за уши не будем и сразу добавим метод collapse.
public static void collapse(final View v, final View hide) { int finalHeight = v.getHeight(); ValueAnimator mAnimator = slideAnimator(finalHeight, 1, v); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int animProgress = (Integer) animation.getAnimatedValue(); float anim = animProgress / (hide.getHeight()/3) / 2.0f; hide.setAlpha(anim); } }); mAnimator.addListener(animator); mAnimator.start(); }
Тут я уже ничего объяснять не буду и так все понятно. :)
Вернемся к нашему главному активити. Определим сразу наши слои меню и затухающий. Переходим к методу onOptionsItemSelected. Удаляем из него все лишнее и добавим обработку нажатия на кнопку Home, с использованием нашей анимации.
@Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { if (!AnimatingUtils.animProgress) if (menuLayout.getMeasuredHeight() <= 10) { AnimatingUtils.expand(menuLayout, hideLayer); } else { AnimatingUtils.collapse(menuLayout, hideLayer); } } return super.onOptionsItemSelected(item); }
Все! Анимация готова, менюшка тоже. Можно запустить и проверить.
Полный проект можно скачать на github.
Всех еще раз с Рождеством и да пребудет с вами сила!)
Комментариев нет :
Отправить комментарий