Rubberband effect for scrolling containers

Viewed 639

I'd like to have a rubberband effect on scrolling containers for which I feel the "tensile scrolling" that is build into the Component base class is no sufficient replacement.

Is there a reasonably feasible way like disabling the default overscroll behavior in order to control the property scrollY in a way like in this example - How to create the rubberband effect?

1 Answers

Since there was no answer to my question I answer it myself. Beware that I am not an expert and therefore this might not fit Your purpose!

The default overscroll behaviour can be overridden by extending the Container class and overriding the pointer methods without calling their super methods.

Here is an example:

import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Form;
import com.codename1.ui.Graphics;
import com.codename1.ui.Label;
import com.codename1.ui.animations.Motion;
import com.codename1.ui.geom.Point;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;

public class FormOverscroll extends Form {
    FormOverscroll() {
        super("FormOverscroll");
        setScrollable(false);
        setLayout(new BorderLayout());
        Container container = new Container(BoxLayout.y()) {
            boolean bounce = false;
            Motion motion = null;
            Point pointPressed = null;
            long millisPoint = 0;
            int scrollYPressed = 0, scrollYPrevious = 0;
            {
//              setTensileDragEnabled(false);
                setScrollableY(true);
            }
            @Override
            protected boolean isStickyDrag() {
                return true;
            }
            @Override
            public void pointerPressed(int x, int y) {
                pointPressed = new Point(x, y);
                scrollYPressed = scrollYPrevious = getScrollY();
                millisPoint = System.currentTimeMillis();
                motion = null;
                bounce = false;
            }
            @Override
            public void pointerDragged(int x, int y) {
                if (null == pointPressed) {
                    return;
                }
                int yDist = y - pointPressed.getY();
                int scrollY = scrollYPressed - yDist;
                if (scrollY < 0) {
                    Motion motionRubberband = Motion.createCubicBezierMotion(0, scrollY, getHeight(), 0f, 1.2f, 0.5f, 0.6f);
                    motionRubberband.setStartTime(0);
                    motionRubberband.setCurrentMotionTime(Math.abs(scrollY));
                    scrollY = motionRubberband.getValue();
                }
                setScrollY(scrollY);
            }
            float getVelocity(int scrollY) {
                long millisNow = System.currentTimeMillis();
                long timediff = millisNow - millisPoint;
                float diff = scrollYPrevious - scrollY;
                float velocity = (diff / timediff) * -1f;
                scrollYPrevious = scrollY;
                millisPoint = millisNow;
                return velocity;
            }
            @Override
            public void pointerReleased(int x, int y) {
                if (null == pointPressed) {
                    return;
                }
                int yDist = y - pointPressed.getY();
                int scrollY = scrollYPressed - yDist;
                float velocity = getVelocity(scrollY);
                motion = Motion.createFrictionMotion(scrollY, Integer.MIN_VALUE, velocity,  0.0007f);
                motion.start();
                getComponentForm().registerAnimated(this);
                pointPressed = null;
            }
            @Override
            public boolean animate() {
                boolean animate = super.animate();
                if (null != motion) {
                    int scrollY = motion.getValue();
                    setScrollY(scrollY);
                    int target = 0;
                    if (scrollY < target && !bounce) {
                        createRubberbandMotion(scrollY, target);
                        bounce = true;
                        motion.start();
                    }
                    int maxScrollY = Math.max(target, getScrollDimension().getHeight() - getHeight());
                    if (scrollY > maxScrollY && !bounce) {
                        createRubberbandMotion(scrollY, maxScrollY);
                        bounce = true;
                        motion.start();
                    }
                    scrollYPrevious = scrollY;
                    if (motion.isFinished()) {
                        motion = null;
                    }
                    return true;
                }
                return animate;
            }
            private void createRubberbandMotion(int source, int target) {
                motion = Motion.createCubicBezierMotion(source, target, 500, 0.0f, 1.0f, 1.2f, 1.0f);
            }
            @Override
            public Component getComponentAt(int x, int y) {
                return this;
            }
            @Override
            public void paint(Graphics aGraphics) {
                super.paint(aGraphics);

            }
        };
        for (int index = 0; index < 100; index++) {
            container.add(new Label("Zeile " + index));
        }
        add(BorderLayout.CENTER, container);
    }

}
Related