Tags
android, animation, custom-ui, graphics, kinetic, kinetic list view, list-view, listview, mozilla, over-scroll, rotate-x, rotation, scroll, transformation
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.
This is a clever effect, Sriram! Have you tested other effects? Adjusting the view’s scaleX might create a nice rubberband effect as the user pulls. 🙂
The scale might not work as intended. The space to scale is the same for each row. Hence each row might not expand its size, but try to blow up in its own space.
This a great one. Can you please send me a sample project on this sir? I am new to android and it would be a great help