码迷,mamicode.com
首页 > 其他好文 > 详细

无限滚动的ImageView

时间:2015-08-16 18:18:45      阅读:108      评论:0      收藏:0      [点我收藏+]

标签:imageview   自定义控件   

限滚动的ImageView


1)链接

      github上地址为:  https://github.com/Q42/AndroidScrollingImageView

      附效果图:

      技术分享

2)实现源码

      源码比较少,在此贴上自定义属性和代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollingView">
<attr name="src" format="reference"/>
<attr name="speed" format="dimension"/>
</declare-styleable>
</resources>

      代码:

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
 
import static java.lang.Math.abs;
 
public class ScrollingView extends View {
 
    private static final String TAG = ScrollingView.class.getSimpleName();
    private int speed;
    private Bitmap bitmap;
    private boolean isStarted = false;
    private float offset;
    private Rect clipBounds = new Rect();
 
    public ScrollingView(Context context) {
        super(context);
    }
 
    public ScrollingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScrollingView, 0,0);
 
        try{
            speed = typedArray.getDimensionPixelSize(R.styleable.ScrollingView_speed, 1);
            bitmap = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(R.styleable.ScrollingView_src, 0));
        }finally {
            typedArray.recycle();
        }
        start();
    }
 
    public ScrollingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onDraw(Canvas canvas) {
        if (canvas == null) {
            return;
        }
        //拿到画布的大小
        canvas.getClipBounds(clipBounds);
        //获取src的图片宽度
        float layerWidth = bitmap.getWidth();
        //后面如果开启了滚动动画,那么将会不断减少位移,这里判断如果减少的位移已经不少于src图片宽度时,则让其复位,不然在下面的循环中将会重复绘制多遍影响性能
        //源码中的写的较复杂,不理解,这里我直接改成了复位为0
        if (offset < -layerWidth) {
//            offset += (floor(abs(offset) / layerWidth) * layerWidth);
            offset = 0;
        }
        //使用一个变量接收一下位移量
        float left = offset;
        //如果left不大于画布的宽度,则循环绘制
        while (left < clipBounds.width()) {
            //前提说明:
            //通过drawBitmap进行绘制,这里的核心是通过控制左边的起始绘制坐标来达成图片的滚动效果
            //left为负数时,起始点将在绘制原点偏左,但是控件显示区域不变,这样将会使得图片的左边一块区域不会显示,右边空一块(附图)
            //left为正数时,起始点将在绘制原点偏右,但是控件显示区域不变,这样将会使得图片的右边一块区域不会显示,左边空一块
            //所以通过控制left参数,就可以达成拼接图片的目的!就相当于把图片从中间某一个地方剪开,把左边的图片
 
            //原理:将left坐标通过speed速度进行变化不断偏移,然后基于left进行绘制,此处while循环和left+=layoutWidth是为了画布中不出现空白断节的目的,如果改成if的话,当left偏移足够多时,就没法接头造成显示空白断截的情况
            //其中,关于方向的控制,设置为speed为正时,滚动方向为从右到左;
            // 为负数时,滚动方向从左到右
            //具体控制手段为:为正时,getBitmapLeft方法不作处理,left将会与偏移量offset一致越负越多,导致左边的绘制坐标从开始不断向左,图片会从左向右绘制直到把右边画布填满,达到图片在从右向左滚的效果
            //为正数时,getBitmapLeft方法对left进行处理,为了使方向相反,需要使绘制坐标从开始不断向右,所以返回的是clipBounds.width() +(- layerWidth) - left;
            //图片会从右向左绘制直到把左边填充满,之所以需要-layoutWidth,是因为如果画布宽度是图片宽度的几倍时,没有减去的话最左边的一个图片宽度将会出现空白出现断层(left将不能从最左边开始画)
            canvas.drawBitmap(bitmap, getBitmapLeft(layerWidth, left), 0, null);
            left += layerWidth;
            //PS:left是接收的offset值,该值在下面的代码中是一直递减的(offset -= abs(speed);)
        }
        if (isStarted) {
            offset -= abs(speed);
            //通过speed修改偏移量重绘
            postInvalidateOnAnimation();
        }
    }
 
    /**
     * 用于设置drawBitmap时左边的坐标,控制滚动的方向,
     * 当速度为正数时,将left坐标原样返回,滚动方向为从右到左(返回的left的变化趋势将会从0一直减少到-layoutWidth)
     * 当速度为负数时,则取全left剩余的部分,滚动方向从左到右(返回的数的变化趋势将会从clipBounds.width() - layerWidth增加到clipBounds.width())
     * @param layerWidth bitmap图片的宽度
     * @param left       当前的left坐标,即记录的offset位移量
     * @return
     */
    private float getBitmapLeft(float layerWidth, float left) {
        if (speed < 0) {
            return clipBounds.width() - layerWidth - left;
        } else {
            return left;
        }
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), bitmap.getHeight());
    }
 
    /**
     * Start the animation
     */
    public void start() {
        if (!isStarted) {
            isStarted = true;
            postInvalidateOnAnimation();
        }
    }
 
    /**
     * Stop the animation
     */
    public void stop() {
        if (isStarted) {
            isStarted = false;
            invalidate();
        }
    }
 
    public boolean isStarted(){
        return isStarted;
    }
 
}

总结:其核心逻辑可见注释,即通过不断改变drawBitmap方法的左边起始绘制坐标来控制图片的左右横向滚动。同理,有兴趣的小伙伴可以参考这个思路实现一下ImageView的竖直方向上的无限滚动。

版权声明:本文为博主原创文章,未经博主允许不得转载。

无限滚动的ImageView

标签:imageview   自定义控件   

原文地址:http://blog.csdn.net/a6511631/article/details/47703879

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!