By Fem
18th July 2010.
15:30
good day anders..
i just want to ask how can i install games in xperia x10 mini
pls help me…tnx a lot
0
0
This is the third and final article in the series of how to make your own list view. Right now we have a basic working list with some nice graphics. Click here to go to the previus part of this tutorial. In this article we will add some behavior to our list and add the fling and bounce/snap effects. Fling support is in my view mandatory for any list where you navigate by touch. As a user I wouldn’t expect that the list simply stops when I lift my finger from the touch screen. If I give the list a velocity, I expect it to continue scrolling for a while, and gradually slow down until it comes to a halt. Fortunately, supporting fling is no big deal. In fact it’s very simple. Below is the source code for this part of the tutorial ready to be set up in e.g. Eclipse. And as usual: Don’t forget to download the ‘Sony Ericsson Tutorials’ app from Android market where all sample apps for this and other tutorials are collected.
[Download] 3D List sample project – Part 3 (37kb)
To make our list more customizable we are going to delegate the dynamics of the flinging to another class and letting the application set the specific dynamic class it wants to our list. It’s quite easy to imagine that different usages of a list could use different dynamics so doing like this will let us easily change the dynamics as well as reusing them for other views.
For the list to be able to handle different dynamics we need of course to have a common interface that the list can use. Below is an abstract class that an application can extend and implement.
[java]
public abstract class Dynamics {
private static final int MAX_TIMESTEP = 50;
protected float mPosition;
protected float mVelocity;
protected long mLastTime = 0;
public void setState(final float position, final float velocity, final long now) {
mVelocity = velocity;
mPosition = position;
mLastTime = now;
}
public float getPosition() {
return mPosition;
}
public float getVelocity() {
return mVelocity;
}
public boolean isAtRest(final float velocityTolerance) {
return Math.abs(mVelocity) < velocityTolerance
}
public void update(final long now) {
int dt = (int)(now – mLastTime);
if (dt > MAX_TIMESTEP) {
dt = MAX_TIMESTEP;
}
onUpdate(dt);
mLastTime = now;
}
abstract protected void onUpdate(int dt);
}
[/java]
It’s pretty simple. It has a position and a velocity. It also stores the last time it was updated to be able to calculate the time between to updates. There’s a set method that the list will use to initialize the dynamic each time we start an animation, get methods for position and velocity, a method to find out if the dynamics is at rest and finally a function to update position and velocity based on a new time. The update method computes the delta time, then it calls the protected method onUpdate(). It is this function that actually update the position and velocity and it’s up to the extending class to implement this.
For this application we will use a pretty simple dynamics.
[java]
class SimpleDynamics extends Dynamics {
private float mFrictionFactor;
public SimpleDynamics(final float frictionFactor) {
mFrictionFactor = frictionFactor;
}
@Override
protected void onUpdate(final int dt) {
mPosition += mVelocity * dt / 1000;
mVelocity *= mFrictionFactor;
}
}
[/java]
This is an implementation that models friction as something that decreases the velocity by a fixed number of percent each frame. The position is calculated from the velocity by using standard Euler integration. I didn’t mention it before but the unit for the velocity is pixels per second, that’s the reason for the divide by 1000. This is about as simple as it gets, but it works very well in my opinion.
One thing that’s common for both fling and bounce/snap is that we need a mechanism to move the list without relying on touch. Right now, the only time we move the list is when we receive touch move events, in other words, only when the user is touching the screen. What we need is a way to continuously update the position for as long as we want that is separated from touch events.
A convenient way to do that is to use a Runnable. Whenever we want to start an animation we post the runnable, and in the run method we update the position of the list. Then we check if we need another frame of the animation. If we do, the runnable post itself to the view. Below is a Runnable that uses the dynamics class.
[java]
mDynamicsRunnable = new Runnable() {
public void run() {
// set the start position
mListTopStart = getChildTop(getChildAt(0)) – mListTopOffset;
// calculate the new position
mDynamics.update(AnimationUtils.currentAnimationTimeMillis());
// update the list position
scrollList((int)mDynamics.getPosition() – mListTopStart);
// if we are not at rest…
if (!mDynamics.isAtRest(VELOCITY_TOLERANCE)) {
// …schedule a new frame
postDelayed(this, 16);
}
}
};
[/java]
Why use AnimationUtils.currentAnimationTimeMillis()? Well, for animation purposes like this it’s a bad idea to use System.currentTimeMillis() since it can be changed at any time by the user or any other application. CurrentAnimationTimeMillis() instead uses SystemClock.uptimeMillis() which won’t change unexpectedly (and if we want to we could just as well used that one directly).
The scrollList() method is the same as before (roughly unchanged since the first part) but it might be a good idea to go through what will happen. scrollList() will change the mListTop variable and then request a layout. Then our onLayout() method will be called and the child views will be positioned correctly and any new views will added and views that scroll outside will be removed. Then onLayout() invalidates the list. This triggers a draw call and the list is redrawn.
OK, now we have our abstract Dynamics class, our specific implementation of it, SimpleDynamics, and we know how to apply it using the Runnable. Now we just need to handle it in the list. We need to start the fling whenever the user lets go of the screen. Starting the fling means we need to set the velocity and position to the dynamics object and then post the runnable. In endTouch():
[java]
if (mDynamics != null) {
mDynamics.setState(mListTop, velocity, AnimationUtils.currentAnimationTimeMillis());
post(mDynamicsRunnable);
}
[/java]
We also modify the endTouch() method to take the velocity as a float. To get the velocity of the touch gesture we use the VelocityTracker. To use it we first need to obtain one. In the startTouch() method, we add the following.
[java]
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
[/java]
Then we also need to make sure we recycle it when we don’t need it. In the endTouch() method we add this:
[java]
mVelocityTracker.recycle();
mVelocityTracker = null;
[/java]
For each move event we feed the velocity tracker the motion event. Then, we modify our handling of UP events to look like this.
[java]
case MotionEvent.ACTION_UP:
float velocity = 0;
if (mTouchState == TOUCH_STATE_CLICK) {
clickChildAt((int)event.getX(), (int)event.getY());
} else if (mTouchState == TOUCH_STATE_SCROLL) {
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND);
velocity = mVelocityTracker.getYVelocity();
}
endTouch(velocity);
break;
default:
endTouch(0);
break;
[/java]
What’s new here is the else statement handling up events in case of a scroll. We feed the final up event to the velocity tracker and then we calculate the velocity (in pixels per second) and get the Y-part of the velocity (we are not interested in the X-part of the velocity).
Now it’s only one thing left until we have a working fling. Whenever the user starts touching the list again, we need to stop any fling animation we have. So, in startTouch() we add a call to removeCallbacks() to remove the dynamics Runnable if it’s posted.
If we try it out now, it works nice, but an old problem that we’ve ignored until now becomes even more obvious. It is very easy to scroll and fling the list so all the items disappears. What we need is some limits to how we can scroll the list. However, instead of looking at the limits as absolute, we’re going to think of them as a bit “bendable”.
First let’s add some max and min limits to our Dynamics class.
[java]
protected float mMaxPosition = Float.MAX_VALUE;
protected float mMinPosition = -Float.MAX_VALUE;
public boolean isAtRest(final float velocityTolerance, final float positionTolerance) {
final boolean standingStill = Math.abs(mVelocity) < velocityTolerance;
final boolean withinLimits = mPosition – positionTolerance mMinPosition;
return standingStill && withinLimits;
}
public void setMaxPosition(final float maxPosition) {
mMaxPosition = maxPosition;
}
public void setMinPosition(final float minPosition) {
mMinPosition = minPosition;
}
protected float getDistanceToLimit() {
float distanceToLimit = 0;
if (mPosition > mMaxPosition) {
distanceToLimit = mMaxPosition – mPosition;
} else if (mPosition < mMinPosition) {
distanceToLimit = mMinPosition – mPosition;
}
return distanceToLimit;
}
[/java]
We’ve added set functions for the min and max positions and a method that returns how much outside the limits we are. We’ve also modified the isAtRest() method. Even if the velocity is 0, if we are not within our limits then we are not at rest.
The next step is to handle the limits in our SimpleDynamic class. And actually, the only thing (almost) we are going to do is to add this line at the top of onUpdate()
[java]
mVelocity += getDistanceToLimit() * mSnapToFactor;
[/java]
If we are outside of our limits (if getDistnaceToLimit() returns anything other than 0) we modify the velocity based on the distance to the limit. This will behave as a spring, how bouncy the spring is depends on the friction and the snap to factor. This is also about as simple as you can make this, and there are lots of more advanced algorithms that you can use instead if you want to. I like the simplicity of it and depending on the use, the behavior will often be quite nice. If you switch to modifying the position instead of the velocity you will get something that exponentially decreases the distance to the limit, looking like a critically damped spring (without any bounce).
Now, we also need to set the max and min positions in the list. Since we defined the position of the list as the position of the first item the position of the list will be 0 when we start. If we scroll up the position is going to be negative so the list position will be between 0 and some negative value which will depend on how many items we have and the height of them. However, we know that 0 is our maximum position so we can set that directly.
As for the minimum position we can set that first when we know where it is. If our last visible item position is equal to the item count minus one we know we are showing the last item. If also the bottom of that item is less than the height of the view then we have scrolled passed the limit and can set the current position as the minimum limit. scrollList() is the only place where we change the list position so we can add the code there.
[java]
if (mLastSnapPos == Integer.MIN_VALUE && mLastItemPosition == mAdapter.getCount() – 1
&& getChildBottom(getChildAt(getChildCount() – 1)) < getHeight()) {
// then save the last snap position and snap to it
mLastSnapPos = mListTop;
mDynamics.setMinPosition(mLastSnapPos);
}
[/java]
Due to the fact that the items of the list rotate based on the position of the list, some positions are, in a way, better than others. The positions where all items are facing towards the screen are the positions that gives the best view of the list. Let’s recognize this fact by snapping to these positions, that is, when the user lets go of the screen we animate the list to the closest position where the items are rotated so they face the screen.
We can use the same dynamics class for the snapping by simply setting both max and min positions to the snap position. However, we need to re-set this snap point every time we scroll since we might have scrolled so that another snap position is closer. The below method setSnapPoint() is called from scrollList().
[java]
private void setSnapPoint() {
final int rotation = mListRotation % 90;
int snapPosition = 0;
// set snap position that corresponds to closest 90 degree rotation
if (rotation < 45) {
snapPosition = (-(mListRotation – rotation) * getHeight()) / DEGREES_PER_SCREEN;
} else {
snapPosition = (-(mListRotation + 90 – rotation) * getHeight())
/ DEGREES_PER_SCREEN;
}
// if we haven’t set mLastSnapPos before and…
// the last item is added as a child and..
// it’s bottom edge is visible
if (mLastSnapPos == Integer.MIN_VALUE && mLastItemPosition == mAdapter.getCount() – 1
&& getChildBottom(getChildAt(getChildCount() – 1)) 0) {
snapPosition = 0;
} else if (snapPosition < mLastSnapPos) {
snapPosition = mLastSnapPos;
}
mDynamics.setMaxPosition(snapPosition);
mDynamics.setMinPosition(snapPosition);
}
[/java]
The method starts with checking the current rotation and determining what 90 degree rotation is closest and then converts this rotation to a list position. The second part is similar to the code snippet before this. It checks if this is the last snap position and if it is, saves the value. Then we make sure the snap position is at most 0 and not less than the last snap position and set the snap position as both the max and min position of the dynamics.
Now we have reached the end of this series of tutorials. We now have a list that 1) supports basic features (we can scroll it and click and long press on items), 2) looks nice (even a bit over the top if you ask me) and 3) has a nice behavior (we can fling it and it can both bounce at the limits or snap to certain positions).
But, hopefully, it’s not the end of this list code. There are a lot of things that can be improved or tweaked and many features are still waiting to be implemented. For example, the list as it is now does not support different types of items, scrollbars, or dividers. The list also does not register an observer on the adapter so it is not aware of changes to the content. When it comes to the graphical look of the list it can of course be modified into more or less an infinite number of designs. And why not add animations when items are clicked or long pressed?
From here on I leave it to you. This list is a base to start from and, depending on your needs, you will probably need to modify, tweak and develop the list in different directions in order to use it for your application, prototype or what ever it might be. If you use and modify it, we would really appreciate if you let us know. Mail us with a link to an blog post, make a comment here, or even better, record a YouTube video like this one. We would love to see what you can come up with.
By Fem
18th July 2010.
15:30
good day anders..
i just want to ask how can i install games in xperia x10 mini
pls help me…tnx a lot
0
0
By SlowTree
11th October 2010.
13:43
Hi, I really appreciate your work and the way you explained this topic.
Merging the first and third tutorial I create a ListView with bounce.
It’s really great.
What I’m going to do now is to create a Spinner Wheel based on this list implementation.
I don’t think it is so hard to do, but actually I can’t find the proper set of values for the mDynamics Max and Min position.
Can you help me giving some suggestion on how to do that?
Many thanks for sharing your work. Have a nice week.
0
0
By 3d
4th November 2010.
22:02
Vary vary so mush.
0
0
By gold
10th November 2010.
15:57
Hi Anders,
Thank for an advance tutorial.
Can list items (suppose items are always IMAGES) be displayed as a STACK of cards until some user touch it and it will start moving up or moving down and piling up again?
Based on your experience what is your suggestion?
Thanks in advance.
0
0
By Douglas
4th January 2011.
08:15
I have a strong interest in making absolute limits (e.g. no bendable limits). How do I do that?
0
0
By Jonathan Meson
11th July 2011.
18:09
I have tried part 1, 2 and 3. What I noticed is that if I give background stages to my list item they don’t work on this list… What exactly is missing? How can I get this items background to behave correctly on touch/selection? Thanks!
0
0
Sort by