Как мы учили кнопку плавать
Сразу, наверное, стоит предупредить, что мы сделали плавающую кнопку немножко по-своему и собственно до релиза библиотеки, реализующей материальный дизайн как таковой. Но обо всем по порядку.В нашей концепции (приложений, сайта surfingbird.ru) активно используется такое понятие как «сёрф». Сервис генерирует для пользователя набор релевантных его интересам статей и переход к следующей, рекомендуемой статье мы называем «сёрфом». После очередного редизайна мы кнопку сёрф потеряли (раньше она была сверху, в акшенбаре, и до неё было не очень удобно тянуться), чем пользователи были очень возмущены. После просмотра ролика с превью материального дизайна мы загорелись идеей реализовать нечто подобное в приложении.
С одной стороны, нам очень понравилась сама концепция крупной кнопки — на которую хочется нажать, с другой — нас сильно смущало то, что кнопка заслоняет контент и как бы отвлекает, что ли во время чтения. Тогда наш дизайнер предложил научить её «плавать».
Перехватываем событие скроллинга
Стандартный компонент ListView не очень-то горит желанием делиться подробной информацией о том, на сколько пикселей и в каком направлении юзер только что проскролил. В RecyclerView с этим получше, но и ListView можно без труда научить «делиться», тем более что нужная функция в нем есть. Расширяем класс:
/** * Created by recoilme on 29.08.14. */ public class ScrollDetectingListView extends ListView { public ScrollDetectingListView (Context context) { super (context); }
public ScrollDetectingListView (Context context, AttributeSet attrs) { super (context, attrs); }
public ScrollDetectingListView (Context context, AttributeSet attrs, int defStyle) { super (context, attrs, defStyle); }
//we need this protected method for scroll detection public int getVerticalScrollOffset () { return computeVerticalScrollOffset (); } } Теперь, в активити мы можем определить в какую сторону смеcтился скролл:
listView = (ScrollDetectingListView) aq.id (R.id.detailmain_listview).getListView ();
listView.setOnScrollListener (new AbsListView.OnScrollListener () {
private int mInitialScroll = 0;
@Override public void onScrollStateChanged (AbsListView view, int scrollState) {
}
@Override public void onScroll (AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int scrolledOffset = listView.getVerticalScrollOffset (); if (scrolledOffset!=mInitialScroll) { //if scroll position changed boolean scrollUp = (scrolledOffset — mInitialScroll) < 0; mInitialScroll = scrolledOffset; if (scrollUp) { if (isNetworkAvailable && !animationProcess && aq.id(R.id.surfButton).getView().getVisibility() == View.GONE) { actionBar.show(); aq.id(R.id.surfButton).animate(animFadeIn); } } else { if (isNetworkAvailable && !animationProcess && aq.id(R.id.surfButton).getView().getVisibility() == View.VISIBLE) { actionBar.hide(); aq.id(R.id.surfButton).animate(animFadeOut); } } } } }); Когда пользователь скроллит вверх — запускается анимация появления кнопки и экшенбара, когда вниз — кнопка скрывается. При нажатии — кнопка скрывается, при загрузке следующего сёрфа — появляется. Всё просто.
Собственно анимации:
isNetworkAvailable = UtilsApi.isOnline (activity); if (isNetworkAvailable) { aq.id (R.id.surfButton).visible (); } else { aq.id (R.id.surfButton).gone (); } animFadeIn = AnimationUtils.loadAnimation (getApplicationContext (), R.xml.grow_from_top); animFadeIn.setAnimationListener (new Animation.AnimationListener () { @Override public void onAnimationStart (Animation animation) { animationProcess = true; aq.id (R.id.surfButton).visible (); }
@Override public void onAnimationEnd (Animation animation) { animationProcess = false; aq.id (R.id.surfButton).visible (); }
@Override public void onAnimationRepeat (Animation animation) {} }); animFadeOut = AnimationUtils.loadAnimation (getApplicationContext (), R.xml.shrink_from_top); animFadeOut.setAnimationListener (new Animation.AnimationListener () { @Override public void onAnimationStart (Animation animation) { animationProcess = true; aq.id (R.id.surfButton).visible (); }
@Override public void onAnimationEnd (Animation animation) { animationProcess = false; aq.id (R.id.surfButton).gone (); }
@Override public void onAnimationRepeat (Animation animation) {} });
//grow