Tags

, , , , , , , , , , , , ,

On one hand, Apple has patented the elastic over-scrolling behavior leaving Samsung to pay $1B. On the other, the blue Holo over-scroll doesn’t quite match with the app’s color scheme. Instagram or Pinterest cannot depend on either of these to give a List View that could evoke the emotions of the user. How could we inform the user that they have reached the end of the list, yet not use one of the above?

Some of the default Android apps like Gallery and Play Books uses a over-scroll behavior like above. The idea of this is simple. ListView provides an overScrollBy() method, that provides different metrics on how the user over scrolled. We can use these values to get the individual views of the ListView and rotate them on X axis (the camera is in Z axis).

public class KineticListView extends ListView {
    public KineticListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
	
    @Override
    protected boolean overScrollBy(... long list of parameters ...) {
        // Get the first and last visible positions.
        int first = getFirstVisiblePosition();
        int last = getLastVisiblePosition();

        // Snap delta to be a maximum of 60 on either sides.
        deltaY = deltaY > 60 ? 60 : deltaY;
        deltaY = deltaY < -60 ? -60 : deltaY;

        float factor = (float) (1/ (float) (last - first));

        // Rotate half the entries.
        if (deltaY > 0) {
            // Over-scrolled at the bottom.
            for (int i = last - (2 * first), j = 1; i <= last - first; i++, j++) {
                View item = getChildAt(i);
                if (item != null)
                    tilt(item, deltaY * j * factor);
            }
        } else {
           // Over-scrolled at the top.
           for (int i = first, j = 1; i <= ((int) (last/2)); i++, j++) {
               View item = getChildAt(i);
               if (item != null)
                   tilt(item, deltaY * (1 - (j * factor)));
           }
        }
        return true;
    }

    public void tilt(final View view, final float deg) {
        view.setRotationX(deg);

        // Reset the rotation.
        view.postDelayed(new Runnable() {
            @Override
            public void run() {
                view.setRotationX(0);
            }
        }, 200);
    }
}

Note: setRotationX() is available only from Android version 11.0. Hence, this is not backward compatible with pre-honeycomb phones. We can achieve the same with Camera and Matrix based transformations, which I will cover in my next blog post.

Advertisement