Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Listview Endless Scrolling

5.00/5 (2 votes)
7 Sep 2013CPOL2 min read 15.6K   353  
Make a vertical listview with scroll looping

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:

Java
public float x;
public float y;
public Bitmap bitmap;
public Translate translate; 
  • x y: is the coordinate of image item. The coordinate will be updated at real time. Based on x, y, item will be drawn in a thread by using Canvas
    Java
    canvas.drawBitmap(bitmap, x, y, paint); 
  • bitmap: The item bitmap will be drawn by Canvas
  • translate: When onFling() of the detector GestureDetector is detected, this animation will be set to item to start translating.

How to Scroll List Vertically?

Our listview will implement OnGestureListener, and it must override method:

C#
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:

Java
@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:

Java
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:

Java
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

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

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

  • 20130907: First created

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)