无限滚动的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的竖直方向上的无限滚动。
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-05 19:32:20