Introduction
Today, I will guide the way to create a listview
with looping while scrolling. Looping means that:
- When scrolling down, the last item will be the next of first item
- And when scrolling up, the first item will be the next of last item
Besides that, I added more requirements as below:
Listview
is scrolled vertically Listview
displays 5 items in maximum
Background
I continue using SurfaceView to develop this listview
to detect various gestures and events in SurfaceView, using GestureDetector.
Using the Code
List Item Definition
The same with this article, define an ImageItem
with properties below:
public float x;
public float y;
public Bitmap bitmap;
public Translate translate;
How to Scroll List Vertically?
Our listview
will implement OnGestureListener, and it must override method:
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
When scrolling, updating each item of list by adding distanceY
to item.y
, listview
will scroll vertically. For this reason, if Listview
keeps a list item items
, it will be scrolled by the below method:
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
for (ImageItem item : items) {
item.animations.clear();
item.y -= distanceY;
layout(item);
}
return false;
}
How to Loop when Scrolling?
You can see that there is layout(item)
in the above onScroll()
method. This function is implemented to loop the list. But how to do that?
Suppose that our listview
has vertical coordinates from y<sub>min</sub>
to y<sub>max</sub>
. When scrolling, the item.y
will be updated from [y<sub>min</sub>
, y<sub>max</sub>
]. To loop, we must do the following:
- If
item.y
> y<sub>max</sub>
, reset it to y<sub>max</sub>
- If
item.y
< y<sub>min</sub>
, reset it to y<sub>min</sub>
Based on the above thinking, layout(item)
will be implemented:
private void layout(ImageItem item) {
if (item.y > maxY) {
float dif = item.x - maxY;
item.y = minY + dif;
} else if (item.y < minY) {
float dif = minY - item.y;
item.y = maxY - dif;
}
}
How to Calculate ymin and ymax
y<sub>min</sub>
= the y
coordinate of the first item y<sub>max</sub>
= the height of listview
(item's height * item count)
How to Fling
Method to detect GetureDetector
is:
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
When this event happens, I will translate each item of listview
a distance velocityY
, in 1 second.
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
long current = System.currentTimeMillis();
for (ImageItem item : items) {
Translate translateY = new Translate();
translateY.startTime = current;
translateY.duration = 1500;
translateY.totalDistanceY = (int) velocityY;
item.translate = translateY;
}
return false;
}
I created Animation
and Translate
which extends Animation
to do the idea above.
Animation.java
abstract public class Animation {
public enum Type {
Translate, Alpha, Scale, Rotate,
};
public long duration;
public long startOffsetTime;
public long startTime;
public Type type;
public boolean isActive = false;
private Interpolator interpolator = new DecelerateInterpolator();
public Animation(Type type) {
this.type = type;
}
abstract public void refresh();
public long getEndTime() {
return startTime + duration;
}
public void start(long currentTime) {
startTime = currentTime + startOffsetTime;
isActive = true;
refresh();
}
protected float getRatio(long currentTime) {
if (isActive == false) {
return 0;
}
if (duration == 0) {
return 1;
}
float progress = (float) (currentTime - startTime) / (float) duration;
return interpolator.getInterpolation(progress);
}
}
Translate.java
public class Translate extends Animation {
public Translate() {
super(Type.Translate);
};
public int totalDistanceX;
public int totalDistanceY;
private int currentValueX = 0;
private int currentValueY = 0;
public int getCurrentPosX(long currentTime) {
if (currentTime > getEndTime()) {
return totalDistanceX;
} else if (currentTime < startTime) {
return 0;
}
return (int) (this.totalDistanceX * getRatio(currentTime));
}
public int getCurrentPosY(long currentTime) {
if (currentTime > getEndTime()) {
return totalDistanceY;
} else if (currentTime < startTime) {
return 0;
}
return (int) (this.totalDistanceY * getRatio(currentTime));
}
public int getDifX(long currentTime) {
int pos = getCurrentPosX(currentTime);
int dis = pos - currentValueX;
currentValueX = pos;
return dis;
}
public int getDifY(long currentTime) {
int pos = getCurrentPosY(currentTime);
int dis = pos - currentValueY;
currentValueY = pos;
return dis;
}
@Override
public void refresh() {
currentValueX = 0;
currentValueY = 0;
}
}
History