自定义View-Banner轮播控件

转载请注明出处

这里写图片描述

注意这是个自定义View而不是自定义ViewGroup
使用也很简单就一行代码就搞定bannerView.setBitmaps(bitmaps).start();
setBitmaps和start两个方法比较简单,就是设置数据源和调用invalidate(),代码如下:

public BannerView setBitmaps(ArrayList<Bitmap> bitmaps) {
    if (bitmaps != null && bitmaps.size() > 0) {
        if (this.bitmaps.size() > 0) {
            if (animator != null) {
                animator.cancel();
            }
            this.bitmaps.clear();
        }
        this.bitmaps.addAll(bitmaps);
    }
    return this;
}

public void start() {
    invalidate();
}

主要实现类似用ViewPager的Banner功能是在onDraw中,在onDraw中会使用到三个方法,一个是getHandleBitmaps(ArrayList<Bitmap> bitmaps),这个是获取确定enterBitmap(下一个进入屏幕的图片)和currentBitmap(当前要显示在屏幕的图片),另一个是drawBitmap(Canvas canvas, float currentBitmapLeft, float enterBitmapLift) ,其中currentBitmapLeft:currentBitmap的绘制起点;enterBitmapLift:enterBitmap的绘制起点,最后一个是startAnimator(float start, float end, long duration, long delayMillis) ,其中start:enterBitmap开始移动的位置;end:enterBitmap最终所停止的位置;duration:动画间隔时间;delayMillis:停留时间,onDraw代码如下:

/**
 * 1.代码中的实现核心主要就是确定当前要显示在屏幕中的图片是哪一个,即将进入屏幕的图片是哪一个
 * 2.本例中将采用ValueAnimator来实现图片的自动移动以及暂停
 */
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (bitmaps.size() > 0) {
        // 画暂停的图片
        if (animator == null) {
            // 第一次绘制时,绘制currentBitmap在屏幕上
            canvas.drawBitmap(bitmaps.get(currentIndex), 0, 0, paint);
        } else {
            // 如果ValueAnimator是暂停的,则绘制currentBitmap在屏幕上
            if (!animator.isRunning()) {
                canvas.drawBitmap(bitmaps.get(currentIndex), 0, 0, paint);
            }
        }
        // 获取enterBitmap和currentBitmap
        getHandleBitmaps(bitmaps);
        /**
         * 代码流程
         * 判断是否处于手势操作中:
         * --->是
         * ------>判断手势向左还是向右绘制,调用drawBitmap
         * --->否
         * ------>animator是否为null或animator是否是运行完毕
         * --------->true
         * ------------>startAnimator
         * --------->false
         * ------------>drawBitmap
         * 
         */
        if (mIsBeingDragged) {
            // 向左滑动
            if (scrollAction == AUTO_SCROLL_TO_LEFT) {
                // 向左滑动
                drawBitmap(canvas, animatedValue - mViewWidth, animatedValue);
            } else if (scrollAction == AUTO_SCROLL_TO_RIGHT) {
                // 向右滑动
                drawBitmap(canvas, animatedValue, animatedValue - mViewWidth);
            }
        } else {
            if (animator == null) {
                if (scrollAction == AUTO_SCROLL_TO_LEFT) {
                    // 向左滑动
                    startAnimator(mViewWidth, 0, duration, mDelayMillis);
                } else if (scrollAction == AUTO_SCROLL_TO_RIGHT) {
                    // 向右滑动
                    startAnimator(0, mViewWidth, duration, mDelayMillis);
                }
            } else {
                if (animator.isRunning()) {
                    //绘制
                    // 向左滑动
                    if (scrollAction == AUTO_SCROLL_TO_LEFT) {
                        // 向左滑动
                        drawBitmap(canvas, animatedValue - mViewWidth, animatedValue);
                    } else if (scrollAction == AUTO_SCROLL_TO_RIGHT) {
                        // 向右滑动
                        drawBitmap(canvas, animatedValue, animatedValue - mViewWidth);
                    }
                } else {
                    if (scrollAction == AUTO_SCROLL_TO_LEFT) {
                        // 向左滑动
                        startAnimator(mViewWidth, 0, duration, mDelayMillis);
                    } else if (scrollAction == AUTO_SCROLL_TO_RIGHT) {
                        // 向右滑动
                        startAnimator(0, mViewWidth, duration, mDelayMillis);
                    }
                }
            }
        }
    }
}

private void drawBitmap(Canvas canvas, float currentBitmapLeft, float enterBitmapLift) {
    canvas.drawBitmap(currentBitmap, currentBitmapLeft, 0, paint);
    canvas.drawBitmap(enterBitmap, enterBitmapLift, 0, paint);
    if (BuildConfig.DEBUG) {
        canvas.drawText(currentIndex + "", (enterBitmapLift) / 2, mViewHeight / 2, paint);
        canvas.drawText(enterIndex + "", (mViewWidth - currentBitmapLeft) / 2, mViewHeight / 2, paint);
    }
}

private void getHandleBitmaps(ArrayList<Bitmap> bitmaps) {
    if (null != bitmaps && !bitmaps.isEmpty()) {
        if (scrollAction == AUTO_SCROLL_TO_LEFT) {
            enterIndex = currentIndex + 1;
            if (enterIndex >= bitmaps.size()) {
                enterIndex = 0;
            }
        } else if (scrollAction == AUTO_SCROLL_TO_RIGHT) {
            enterIndex = currentIndex - 1;
            if (enterIndex < 0) {
                enterIndex = bitmaps.size() - 1;
            }
        }
        enterBitmap = bitmaps.get(enterIndex);
        currentBitmap = bitmaps.get(currentIndex);
    }
}

private void startAnimator(float start, float end, long duration, long delayMillis) {
    if (animator == null) {
        animator = ValueAnimator.ofFloat(start, end);
        animator.setDuration(duration);
        //animator.setInterpolator(new LinearInterpolator());
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {//只要animator.cancel();都会调用此方法
                if (!mIsBeingTouch) {
                    currentIndex = enterIndex;
                    setPreNextIndex();
                    /**
                     * 只有自动动画完毕才会被重置scrollAction
                     * 当scrollAction==AUTO_SCROLL_TO_RIGHT时,并且再次触发点击事件animator被取消
                     * 则此处scrollAction不会被赋值,由onTouchEvent中处理
                     */
                    if (scrollAction != defaultScrollAction && isAutoScrollToRight) {
                        scrollAction = defaultScrollAction;
                        isAutoScrollToRight = false;
                    }
                    invalidate();
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }
        });
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animatedValue = (Float) animation.getAnimatedValue();
                invalidate();
            }
        });
        if (!animator.isRunning()) {
            animator.setStartDelay(delayMillis);
            if (animator.getStartDelay() == mDelayMillis) {
                startTime = System.currentTimeMillis();
            }
            animator.start();
        }
    } else {//动画取消后再运行,会重置一切animator的参数
        if (!animator.isRunning()) {
            if (null == propertyValuesHolder) {
                propertyValuesHolder = PropertyValuesHolder.ofFloat("x", start, end);
            } else {
                propertyValuesHolder.setFloatValues(start, end);
            }
            animator.setValues(propertyValuesHolder);
            animator.setDuration(duration);
            animator.setStartDelay(delayMillis);// 这一行代码不能再判断条件后执行
            if (animator.getStartDelay() == mDelayMillis) {
                startTime = System.currentTimeMillis();
            }
            animator.start();
        }
    }
}

至此就实现了图片的自动滑动并暂停显示,在上面的代码中startAnimator方法没什么好说的,就是开启ValueAnimator或者对ValueAnimator重新赋值,在ValueAnimator结束后,如果不是处于手势操作状态中会对currentIndex重新赋值并且计算currentIndex的上一个和下一个图片的索引值(preIndex\nextIndex),这两个索引将会用到手势操作中,在onTouchEvent 中主要就是判断手松开后图片应该是向左滑还是向右滑,并且在计算剩余滑动时间和滑动距离或者是剩余等待事件以及修正currentIndex,主要就是一些规则制定以及数值计算的问题,最终实现了Banner轮播控件和手势操作,具体细节请看源码

https://github.com/cqf-hn/BannerView


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值