ListView 水平自动滚动
开发一个可以自动水平滚动的列表视图。
- 下载 AdvandeHorizontalAutoScrollListView.zip - 170.6 KB
- 下载 HorizontalAutoScrollListView.zip - 171.5 KB
引言
根据我的经验,自定义 View 是一项非常困难的任务。幸运的是,Android 支持 SurfaceView,它在视图自定义方面非常有用。
在这篇文章中,我指导了如何自定义一个半圆形列表视图。今天,我将同样使用 SurfaceView 来自定义一个自动水平滚动视图。
背景
在阅读本文之前,您应该花些时间阅读 Android 开发者网站上关于 SurfaceView 的文档。
使用代码
Animation.java
要实现列表视图从右到左自动滑动,思路是我们在后台线程中持续更新列表视图中每个项目的坐标并绘制它们。
如何持续更新每个项目的坐标?
假设从时间t1到时间t2,我们必须将项目从坐标x1平移到x2。那么问题是,在时间t时,x = ? 公式将很简单
x = x1 + t * (x2-x1) / (t2-t1)
基于以上思路,我创建了一个简单的 Animation 类,用于计算当前值,具有以下属性:
mStartValue
:动画开始时的初始值。mEndValue
:动画结束时的最终值。mDuration
:从 mStartValue 变到 mEndValue 所需的时间。mStartTime
:动画开始的时间。动画启动时,通过System.currentTimeMillis()
获取。mEndTime
:动画结束的时间。mEndTime
=mStartTime
+mDuration
。
在指定时间获取当前值的方法将是
public float getCurrentValue(long currentTime) {
float currentValue = mStartValue + (currentTime)
* (mEndValue - mStartValue) / mDuration;
if (currentTime > mEndTime) {
currentValue = mEndValue;
}
return currentValue;
}
在上述类基础上,我创建了TranslateXAnimation.java
public class TranslateX extends Animation {
public TranslateX() {
super(Type.Translate);
}
}
当然,我们可以用同样的想法创建更多动画,例如:FadeAnimation, ScaleAnimation...。在这篇文章中,我只使用了TranslateXAnimation。
ImageItem.java
这个对象是 ListView 的一个项目。
private class ImageItem {
public float x;
public float y;
public Bitmap bitmap;
public TranslateXAnimation translateX;
}
x
和y:
是图像项目的坐标。坐标将实时更新。根据 x, y,项目将在一个线程中使用 Canvas 绘制 canvas.drawBitmap(bitmap, x, y, paint);
translateX
: 当开始自动滚动时,项目将执行此动画。它将在运行时更新x
值。
x = translateX.getCurrentValue(System.currentTimeMillis() - translateX.mStartTime);
项目将由 Canvas 绘制。
canvas.drawBitmap(bitmap, x, y, paint);
AutoHorizontalScrollView.java
这个类继承自 SurfaceView,并包含一个 ImageItem 的数组列表。
如何每 2 秒自动滚动一次?
我在列表中保留了一个名为 startDisplayTime
的变量,思路是:
- 从 AutoHorizontalScrollView 显示的第一个时间点开始,
startDisplayTime =
System.currentTimeMillis().
- 运行时,如果
System.currentTimeMillis() - startDisplayTime
>= 2 秒, - 开始自动滚动。
- 更新
startDisplayTime = System.currentTimeMillis()
如何循环?
循环意味着,当最后一个项目开始从右侧显示时,下一个右侧的项目将是第一个项目:)
如果我们有ITEM_WIDTH
作为每个列表项目的宽度,我们将更新其 x 坐标。
if (item.x <= -ITEM_WIDTH) {
item.x = VIEW_WIDTH - 2 * ITEM_WIDTH;
}
如何绘制列表项目?
这是一项简单的工作,因为我们只是在后台线程中持续绘制列表中的所有项目。Canvas canvas = getHolder().lockCanvas();
if (canvas == null) {
return;
}
canvas.save();
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
for (int i = 0; i < items.size(); i++) {
ImageItem item = items.get(i);
item.draw(canvas);
}
canvas.restore();
getHolder().unlockCanvasAndPost(canvas);
ImageItem.draw()
public void draw(Canvas canvas) {
canvas.save();
if (translateX != null && !translateX.isEnded()) {
x = translateX.getCurrentValue(System.currentTimeMillis()
- translateX.mStartTime);
} else {
// translateX = null;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setAntiAlias(true);
canvas.drawBitmap(bitmap, x, y, paint);
canvas.restore();
}
为了方便理解,我在这里放了类图。
高级动画
20130906.
我已经完成了这篇关于在 SurfaceView 中执行动画的文章。基于这篇文章中的动画,我将应用它来改进这个列表视图的功能?您认为如果我添加更多如下要求,会怎么样?
- 默认情况下,所有项目的 alpha 值都为 100。
- 当从右向左滑动时,最右边的最后一个项目将从 1 缩放到 1.5,其 alpha 值将从 100 变为 255。
- 当从右向左滑动时,中间的项目将从 1.5 缩放到 1,其 alpha 值将从 255 变为 100。
ImageItem 将添加更多属性:
scaleX
:保持缩放比例scaleY
:保持缩放比例alpha
:保持 alpha 值用于淡入/淡出动画。animations
:正在执行的动画的数组列表。
在 AutoHorizontalScrollView.java 中,我添加了:
firstItemPos
:左侧第一个项目的 items 列表中的位置。默认从开始时为 0。secondItemPos
: 中间第一个项目的 items 列表中的位置。默认从开始时为 1。thirdItemPos
: 中间第一个项目的 items 列表中的位置。默认从开始时为 2。
动画
滑动时,如果位置是 thirdItemPos,除了 TranslateXAnimation, 还要添加:
ScaleXAnimation
ScaleXAnimation scaleX = new ScaleXAnimation();
scaleX.mStartTime = System.currentTimeMillis();
scaleX.mDuration = 500;
scaleX.mEndTime = System.currentTimeMillis() + 500;
scaleX.mStartValue = 1f;
scaleX.mEndValue = 1.5f;
item.animations.add(scaleX);
ScaleYAnimation
ScaleYAnimation scaleY = new ScaleYAnimation();
scaleY.mStartTime = System.currentTimeMillis();
scaleY.mDuration = 500;
scaleY.mEndTime = System.currentTimeMillis() + 500;
scaleY.mStartValue = 1f;
scaleY.mEndValue = 1.5f;
item.animations.add(scaleY);
以及AlphaAnimation
AlphaAnimation alpha = new AlphaAnimation();
alpha.mStartTime = System.currentTimeMillis();
alpha.mDuration = 500;
alpha.mEndTime = System.currentTimeMillis() + 500;
alpha.mStartValue = 150;
alpha.mEndValue = 255;
item.animations.add(alpha);
如果它是中间位置 secondItemPos
,则添加:
ScaleXAnimation
ScaleXAnimation scaleX = new ScaleXAnimation();
scaleX.mStartTime = System.currentTimeMillis();
scaleX.mDuration = 500;
scaleX.mEndTime = System.currentTimeMillis() + 500;
scaleX.mStartValue = 1.5f;
scaleX.mEndValue = 1f;
item.animations.add(scaleX);
ScaleYAnimation
ScaleYAnimation scaleY = new ScaleYAnimation();
scaleY.mStartTime = System.currentTimeMillis();
scaleY.mDuration = 500;
scaleY.mEndTime = System.currentTimeMillis() + 500;
scaleY.mStartValue = 1.5f;
scaleY.mEndValue = 1f;
item.animations.add(scaleY);
以及AlphaAnimation
AlphaAnimation alpha = new AlphaAnimation();
alpha.mStartTime = System.currentTimeMillis();
alpha.mDuration = 500;
alpha.mEndTime = System.currentTimeMillis() + 500;
alpha.mStartValue = 255;
alpha.mEndValue = 100;
并且这 3 个位置将按如下方式更新:
thirdItemPos++;
if (thirdItemPos == items.size()) {
thirdItemPos = 0;
}
secondItemPos++;
if (secondItemPos == items.size()) {
secondItemPos = 0;
}
firstItemPos++;
if (firstItemPos == items.size()) {
firstItemPos = 0;
}
绘制
要绘制 ItemImage,请执行以下操作:
for (int i = 0; i < animations.size(); i++) {
Animation animation = animations.get(i);
if (animation.isStarted()) {
if (!animation.isEnded()) {
if (animation.mType == Type.TranslateX) {
x = animation
.getCurrentValue(System.currentTimeMillis()
- animation.mStartTime);
} else if (animation.mType == Type.ScaleX) {
scaleX = animation
.getCurrentValue(System.currentTimeMillis()
- animation.mStartTime);
} else if (animation.mType == Type.ScaleY) {
scaleY = animation
.getCurrentValue(System.currentTimeMillis()
- animation.mStartTime);
} else if (animation.mType == Type.Alpha) {
alpha = (int) animation
.getCurrentValue(System.currentTimeMillis()
- animation.mStartTime);
}
} else {
animations.remove(i);
}
}
}
paint.setAlpha(alpha);
canvas.scale(scaleX, scaleX, x, y);
canvas.drawBitmap(bitmap, x, y, paint);
您可以下载 AdvandeHorizontalAutoScrollListView.zip 来查看结果。
历史记录
20130905:首次创建。
20130906:在完成关于 SurfaceView 动画的文章后,向其中添加了 alpha, scaleX, scaleY 动画。