Подтвердить что ты не робот

Android: анимация скроллеров?

Я новичок в разработке Android, и я просто хотел бы немного узнать о виджере Scroller (android.widget.Scroller). Как это оживляет представление? Можно ли получить доступ к объекту Animation, если он существует? Если да, то как? Я прочитал исходный код, но не нашел подсказок, или, может быть, я слишком новичок?

Я просто хотел выполнить некоторые операции после того, как Scroller завершит прокрутку, что-то вроде

m_scroller.getAnimation().setAnimationListener(...);
4b9b3361

Ответ 1

Виджет Scroller фактически не выполняет большую часть работы для вас. Он не запускает никаких обратных вызовов, он ничего не оживляет, он просто реагирует на различные вызовы методов.

Итак, что хорошего? Ну, он выполняет все вычисления, например. бросок для вас, что удобно. Так что вы, как правило, делаете, это создать Runnable, который неоднократно спрашивает Scroller: "Какова должна быть моя позиция прокрутки? Затем вы отправляете этот обработчик на обработчике (обычно в представлении) до тех пор, пока не будет выполнено удаление.

Вот пример из фрагмента, над которым я сейчас работаю:

private class Flinger implements Runnable {
    private final Scroller scroller;

    private int lastX = 0;

    Flinger() {
        scroller = new Scroller(getActivity());
    }

    void start(int initialVelocity) {
        int initialX = scrollingView.getScrollX();
        int maxX = Integer.MAX_VALUE; // or some appropriate max value in your code
        scroller.fling(initialX, 0, initialVelocity, 0, 0, maxX, 0, 10);
        Log.i(TAG, "starting fling at " + initialX + ", velocity is " + initialVelocity + "");

        lastX = initialX;
        getView().post(this);
    }

    public void run() {
        if (scroller.isFinished()) {
            Log.i(TAG, "scroller is finished, done with fling");
            return;
        }

        boolean more = scroller.computeScrollOffset();
        int x = scroller.getCurrX();
        int diff = lastX - x;
        if (diff != 0) {
            scrollingView.scrollBy(diff, 0);
            lastX = x;
        }

        if (more) {
            getView().post(this);
        }
    }

    boolean isFlinging() {
        return !scroller.isFinished();
    }

    void forceFinished() {
        if (!scroller.isFinished()) {
            scroller.forceFinished(true);
        }
    }
}

Детали использования Scroller.startScroll должны быть похожими.

Ответ 2

как сказал Билл Филлипс, Scroller - это всего лишь класс Android SDK, который помогает вычислять позиции прокрутки. Здесь у меня есть полный рабочий пример:

public class SimpleScrollableView extends TextView {
    private Scroller mScrollEventChecker;

    private int mLastFlingY;
    private float mLastY;
    private float mDeltaY;

    public SimpleScrollableView(Context context) {
        this(context, null, 0);
    }

    public SimpleScrollableView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SimpleScrollableView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mScrollEventChecker != null && !mScrollEventChecker.isFinished()) {
            return super.onTouchEvent(event);
        }

        final int action = event.getAction();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastY = event.getY();
                return true;

            case MotionEvent.ACTION_MOVE:
                int movingDelta = (int) (event.getY() - mLastY);
                mDeltaY += movingDelta;
                offsetTopAndBottom(movingDelta);
                invalidate();
                return true;

            case MotionEvent.ACTION_UP:
                mScrollEventChecker = new Scroller(getContext());
                mScrollEventChecker.startScroll(0, 0, 0, (int) -mDeltaY, 1000);
                post(new Runnable() {
                    @Override
                    public void run() {
                        if (mScrollEventChecker.computeScrollOffset()) {
                            int curY = mScrollEventChecker.getCurrY();
                            int delta = curY - mLastFlingY;
                            offsetTopAndBottom(delta); // this is the method make this view move
                            invalidate();
                            mLastFlingY = curY;
                            post(this);
                        } else {
                            mLastFlingY = 0;
                            mDeltaY = 0;
                        }
                    }
                });
                return super.onTouchEvent(event);
        }

        return super.onTouchEvent(event);
    }
}

Пользовательский вид демонстрации выше будет прокручиваться назад до исходной позиции после того, как пользователь выпустит представление. Когда пользователь выпускает представление, вызывается метод startScroll(), и мы можем знать, какое значение расстояния должно быть для каждого сообщения.

Полный рабочий пример: Репозиторий Github

Ответ 3

Мы можем расширить класс Scroller, а затем перехватить соответствующие методы запуска анимации, чтобы пометить, что было запущено, после computeScrollOffset() вернуть false, что означает завершенное значение анимации, мы сообщаем слушателю вызывающему:

public class ScrollerImpl extends Scroller {
    ...Constructor...

    private boolean mIsStarted;
    private OnFinishListener mOnFinishListener;

    @Override
    public boolean computeScrollOffset() {
        boolean result = super.computeScrollOffset();
        if (!result && mIsStarted) {
            try { // Don't let any exception impact the scroll animation.
                mOnFinishListener.onFinish();
            } catch (Exception e) {}
            mIsStarted = false;
        }
        return result;
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        super.startScroll(startX, startY, dx, dy);
        mIsStarted = true;
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        super.startScroll(startX, startY, dx, dy, duration);
        mIsStarted = true;
    }

    @Override
    public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) {
        super.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
        mIsStarted = true;
    }

    public void setOnFinishListener(OnFinishListener onFinishListener) {
        mOnFinishListener = onFinishListener;
    }

    public static interface OnFinishListener {
        void onFinish();
    }
}

Ответ 4

Отличный ответ выше. Скроллер # startScroll (...) действительно работает одинаково.

Например, источник для пользовательской прокрутки TextView: http://bear-polka.blogspot.com/2009/01/scrolltextview-scrolling-textview-for.html

Устанавливает Скроллер в TextView с помощью TextView # setScroller (Scroller).

Источник для SDK TextView: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/widget/TextView.java#TextView.0mScroller

Показывает, что TextView # setScroller (Scroller) устанавливает поле класса, которое используется в таких ситуациях, как showPointIntoView (int), где вызывается Scrolller # scrollTo (int, int, int, int).

Команда bringPointIntoView() настраивает mScrollX и mScrollY (с некоторым кодом фрагментации SDK), а затем вызывает invalidate(). Дело в том, что mScrollX и mScrollY используются в таких методах, как onPreDraw (...), чтобы влиять на положение рисованного содержимого представления.