65.9K
CodeProject 正在变化。 阅读更多。
Home

ListView 水平自动滚动

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (6投票s)

2013年9月5日

CPOL

4分钟阅读

viewsIcon

49903

downloadIcon

3050

开发一个可以自动水平滚动的列表视图。

引言

根据我的经验,自定义 View 是一项非常困难的任务微笑 | <img src=。幸运的是,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。
基于 Animation 类,很容易创建 ScaleXAnimationScaleYAnimation、 AlphaAnimation

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 动画。 

© . All rights reserved.