Android: Scroller Animation?

前端 未结 4 1272
醉梦人生
醉梦人生 2020-12-23 18:29

I\'m a newbie in Android development, and I would just like to know a little bit about the Scroller widget (android.widget.Scroller). How does it animate the view? Can the A

相关标签:
4条回答
  • 2020-12-23 18:42

    We can extend the Scroller class then intercept corresponding animation start methods to mark that was started, after computeScrollOffset() return false which means animation finished's value, we inform by a Listener to caller :

    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();
        }
    }
    
    0 讨论(0)
  • 2020-12-23 18:46

    The Scroller widget doesn't actually do much of the work at all for you. It doesn't fire any callbacks, it doesn't animate anything, it just responds to various method calls.

    So what good is it? Well, it does all of the calculation for e.g. a fling for you, which is handy. So what you'd generally do is create a Runnable that repeatedly asks the Scroller, "What should my scroll position be now? Are we done flinging yet?" Then you repost that runnable on a Handler (usually on the View) until the fling is done.

    Here's an example from a Fragment I'm working on right now:

    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);
            }
        }
    }
    

    The details of using Scroller.startScroll should be similar.

    0 讨论(0)
  • 2020-12-23 18:46

    like Bill Phillips said, Scroller is just an Android SDK class helping with calculating scrolling positions. I have a full working example here:

    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);
        }
    }
    

    The demo custom view above will scroll back to original position after the user release the view. When user release the view, then startScroll() method is invoked and we can know what the distance value should be for every single message post.

    Full working example: Github repository

    0 讨论(0)
  • 2020-12-23 18:47

    Great answer above. Scroller#startScroll(...) indeed works the same way.

    For example, the source for a custom scrolling TextView at: http://bear-polka.blogspot.com/2009/01/scrolltextview-scrolling-textview-for.html

    Sets a Scroller on a TextView using TextView#setScroller(Scroller).

    The source for the SDK's TextView at: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/widget/TextView.java#TextView.0mScroller

    Shows that TextView#setScroller(Scroller) sets a class field which is used in situations like bringPointIntoView(int) where Scroller#scrollTo(int, int, int, int) is called.

    bringPointIntoView() adjusts mScrollX and mScrollY (with some SDK fragmentation code), then calls invalidate(). The point of all this is that mScrollX and mScrollY are used in methods like onPreDraw(...) to affect the position of the drawn contents of the view.

    0 讨论(0)
提交回复
热议问题