Android smooth vertical scroll on touch

2019-08-03 12:27发布

问题:

I have a view that's inside a frame layout. I am tracking the ontouch event for the view and I want the view to scroll vertically in a smooth scrolling fashion up and down as I move my finger. At the moment the view scrolls but I can't get the calculation right (and perhaps logic), the scrolling is very choppy. This is similar to Sliding Drawer and for my purpose Sliding Drawer is not going to work.

Would appreciate any help with this problem. Thank you!

    cardImage.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            float startY = 0;
            float moveY = 0;
            float velocityX=0, velocityY=0;
            int index = event.getActionIndex();
            int action = event.getActionMasked();
            int pointerId = event.getPointerId(index);

            switch(event.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                    startY = event.getRawY();
                    isTouchUp = false;                      

                    if(mVelocityTracker == null) {
                        // Retrieve a new VelocityTracker object to watch the velocity of a motion.
                        mVelocityTracker = VelocityTracker.obtain();
                    }
                    else {
                        // Reset the velocity tracker back to its initial state.
                        mVelocityTracker.clear();
                    }
                    // Add a user's movement to the tracker.
                    mVelocityTracker.addMovement(event);

                    previousRawY = startY;

                    return true;
                case MotionEvent.ACTION_MOVE:
                    moveY = event.getRawY();
                    isTouchUp = false;

                    mVelocityTracker.addMovement(event);
                    // When you want to determine the velocity, call 
                    // computeCurrentVelocity(). Then call getXVelocity() 
                    // and getYVelocity() to retrieve the velocity for each pointer ID. 
                    mVelocityTracker.computeCurrentVelocity(1000);
                    // Log velocity of pixels per second
                    // Best practice to use VelocityTrackerCompat where possible.
                            velocityX = VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId);
                            velocityY = VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId);


                    if(Math.abs(velocityX) + 100 > Math.abs(velocityY)) {
                        // right left
                    }
                    else {
                        // up down                          
                        if(action != MotionEvent.ACTION_OUTSIDE) {                          
                            if(moveY > previousRawY) {
                                // Moving down


                                for(float i=0;i<moveY-previousRawY;i++) {
                                    cardCurrentOffset += (moveY-previousRawY)/(moveY-previousRawY);

                                    cardImage.setTranslationY(cardCurrentOffset);
                                }
                            }
                            else {
                                // Moving Up

                                if(cardImage.getY() > topHeaderLayout.getBottom()) {

                                    for(float i=0;i<previousRawY-moveY;i++) {
                                        cardCurrentOffset -= (previousRawY-moveY)/(previousRawY-moveY);

                                        cardImage.setTranslationY(cardCurrentOffset);
                                    }
                                }
                            }

                            previousRawY = moveY;
                        }
                    }

                    return true;    
                case MotionEvent.ACTION_UP:
                    isTouchUp = true;
                    return true;
                case MotionEvent.ACTION_CANCEL:
                    // Return a VelocityTracker object back to be re-used by others.
                    mVelocityTracker.recycle();
                    return true;
                case MotionEvent.ACTION_OUTSIDE:
                    return false;
                default:
                    return false;
            }
        }
    });    

回答1:

I've observed that dinamycally changing properties such as margins, isn't as smooth as I'd like. Instead of that I use TranslateAnimations; the trick is applying animations during the scrolling, and changing the real margins on touch up.



回答2:

try this one (you can add some checks at the top/bottom or fling as well):

class ScrollingTextView extends TextView {
    private GestureDetector mDetector;

    public ScrollingTextView(Context context) {
        super(context);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 30; i++) {
            sb.append("Line #").append(i).append("\n");
        }
        setTextSize(24);
        setText(sb);
        setTextColor(0xffeeeeee);
        mDetector = new GestureDetector(mListener);
    }

    private OnGestureListener mListener = new SimpleOnGestureListener() {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            scrollBy(0, (int) distanceY);
            return true;
        }
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDetector.onTouchEvent(event);
        return true;
    }
}

to test it add the following in Activity.onCreate:

View v = new ScrollingTextView(this);
setContentView(v);