Сразу, наверное, стоит предупредить, что мы сделали плавающую кнопку немножко по-своему и собственно до релиза библиотеки, реализующей материальный дизайн как таковой. Но обо всем по порядку.
В нашей концепции (приложений, сайта 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
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="0.3" android:toXScale="1.0"
android:fromYScale="0.3" android:toYScale="1.0"
android:pivotX="50%" android:pivotY="0%"
android:duration="@android:integer/config_shortAnimTime"
/>
<alpha
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_shortAnimTime"
/>
</set>
//shrink
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0" android:toXScale="0.3"
android:fromYScale="1.0" android:toYScale="0.3"
android:pivotX="50%" android:pivotY="100%"
android:duration="@android:integer/config_shortAnimTime"
/>
<alpha
android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="@android:integer/config_shortAnimTime"
/>
</set>
Вот и всё. Надеемся кому нибудь пригодится. Приятного сёрфа!
Автор: recompileme