Как мы учили кнопку плавать

в 16:19, , рубрики: action button, android, floating button, Блог компании Surfingbird, Разработка под android

Сразу, наверное, стоит предупредить, что мы сделали плавающую кнопку немножко по-своему и собственно до релиза библиотеки, реализующей материальный дизайн как таковой. Но обо всем по порядку.

В нашей концепции (приложений, сайта surfingbird.ru) активно используется такое понятие как «сёрф». Сервис генерирует для пользователя набор релевантных его интересам статей и переход к следующей, рекомендуемой статье мы называем «сёрфом». После очередного редизайна мы кнопку сёрф потеряли (раньше она была сверху, в акшенбаре, и до неё было не очень удобно тянуться), чем пользователи были очень возмущены. После просмотра ролика с превью материального дизайна мы загорелись идеей реализовать нечто подобное в приложении.

С одной стороны, нам очень понравилась сама концепция крупной кнопки — на которую хочется нажать, с другой — нас сильно смущало то, что кнопка заслоняет контент и как бы отвлекает, что ли во время чтения. Тогда наш дизайнер предложил научить её «плавать».

image

Перехватываем событие скроллинга

Стандартный компонент 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js