Android开发

Android中无限滚动的Image

2016-07-27  本文已影响677人  MuMuXuan

前言

这是新开的博客第一篇文章。这一篇针对的是自定义控件。在github上有一个自定义控件的效果如下:

效果

这个水平方向上无限滚动的控件,可以用来制作自定义进度条,或者一些tab效果。
具体使用方法请移步github
它实现该效果只有50行代码不到,所以写这篇博客来记录该控件的实现过程。

1. 分析需求

2. xml资源相关

能够自行设置,那么需要去设置自定义属性来控制速度及滚动展示的图片。
在res/values下创建attrs.xml,并采用以下方式定义自定义属性:

 <resource>
<declare-styleable name="ScrollingView">
    <attr name="speed" format="dimension" />
    <attr name="src" format="reference" />
</declare-styleable>
</resource>

speed为速度,src为滚动展示的图片资源。
在layout布局文件中使用自定义属性,首先在根布局view中设置命名空间:

    xmlns:app="http://schemas.android.com/apk/res-auto"

然后在自定义控件中设置自定义属性

layout布局中使用自定义属性

3. java代码

3.1 初始化

创建一个View类,并在构造方法中获得自定义属性speed和图片。凡是在xml中定义的控件,会调用以下形式的构造方法:

public ScrollingView(Context context, AttributeSet attrs) {
    super(context, attrs);
     try{
        speed = typedArray.getDimensionPixelSize(R.styleable.ScrollingView_speed, 1);
        bitmap = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(R.styleable.ScrollingView_src, 0));
    }finally {
        typedArray.recycle();
    }
}

3.2 onMeasure测量

需要设置控件的宽高,不然会出现高度显示不正常。这里采用的方式为:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), bitmap.getHeight());
}

3.3 onDraw绘制

关键点来了,该控件最大的难点就在绘制,我将绘制逻辑分为三个要点:

3.3.1 要点一

如果当前的控件宽度大于要滚动的图片宽度,那么会出现空白,应该需要重复绘制多个图片来填充。


出现空白 填补空白

具体代码如下:

protected void onDraw(Canvas canvas) {
    //获取要滚动的图片的宽度
    float layerWidth = bitmap.getWidth();
    //使用一个变量来作为绘制bitmap时的左边基坐标
    float left = 0;
    //如果left不大于控件的宽度,则循环绘制
    while (left < getMeasuredWidth()) {
        canvas.drawBitmap(bitmap, left, 0, null);
        left += layerWidth;
    }
}

3.3.2 要点二

根据speed属性,来设置图片往后退的速度。

向后滚动

具体代码如下:

private float offset;
@Override
protected void onDraw(Canvas canvas) {
    //获取要滚动的图片的宽度
    float layerWidth = bitmap.getWidth();
    //使用一个变量来作为绘制bitmap的左边基坐标
    float left = offset;
    //如果left不大于控件的宽度,则循环绘制
    while (left < getMeasuredWidth()) {
        canvas.drawBitmap(bitmap, left, 0, null);
        left += layerWidth;
    }
    //全局变量offser用来记录left的偏移量
    offset = offset-speed;
    postInvalidate();
}

这样写导致left的偏移量越来越小,代码会在手机屏幕左方看不见的地方疯狂绘制,这显然不太效率。这里多加一个判断,如果offset超过一定的界限,就重置。

protected void onDraw(Canvas canvas) {

    //获取要滚动的图片的宽度
    float layerWidth = bitmap.getWidth();
    if (offset < -layerWidth) {
        offset += (floor(abs(offset) / layerWidth) * layerWidth);
        //offset = 0;
    }
    ...
}

3.3.3 要点三

如果速度设置为负数,图片应该不再后退,而是不断前进。

向前滚动
protected void onDraw(Canvas canvas) {
    //获取要滚动的图片的宽度
    float layerWidth = bitmap.getWidth();
    ...
    //如果left不大于控件的宽度,则循环绘制
    while (left < getMeasuredWidth()) {
        canvas.drawBitmap(bitmap, getBitmapLeft(layerWidth, left), 0, null);
        left += layerWidth;
    }
    //全局变量offser用来记录left的偏移量
    if(isStarted){
        offset = offset-abs(speed);
        postInvalidate();
    }
}

/**
 * @param layerWidth bitmap图片的宽度
 * @return
 */
private float getBitmapLeft(float layerWidth, float left) {
    if (speed < 0) {
        return getMeasuredWidth() - layerWidth - left;
    } else {
        return left;
    }
}

3.4 功能完善

为了能够让开发者自由控制图片滚动,项目中还加了一个boolean值用来控制。并提供对应的公有方法。


停止滚动

具体代码如下:

private boolean isStarted = false;

public ScrollingView(Context context, AttributeSet attrs) {
    ...
    start();
}

/**Start the animation*/
public void start() {
    if (!isStarted) {
        isStarted = true;
        postInvalidate();
    }
}

protected void onDraw(Canvas canvas) {
    ...
    //全局变量offser用来记录left的偏移量
    if(isStarted){
        offset = offset-speed;
        postInvalidate();
    }
}

/**Stop the animation*/
public void stop() {
    if (isStarted) {
        isStarted = false;
        invalidate();
    }
}
public boolean isStarted(){
    return isStarted;
}

结束语

有兴趣的小伙伴可以参考这个思路,通过修改drawBitmap方法中top基坐标,实现一下Image在竖直方向上的无限滚动。

上一篇下一篇

猜你喜欢

热点阅读