一个优雅的无限轮播控件

       出处:http://blog.csdn.net/hzwailll/article/details/54288466


       最近几个月有些颓废,有篇博客甚至在草稿箱里躺了好几个月了。换了工作刚稳定下来,也不那么忙了,正好写几篇博客总结下,就算是对操蛋而又不同寻常的2016的总结吧!


造轮子


        虽然网上能搜出一火车皮的无限轮播控件来,但是那始终是别人家的轮子,大轮子(那些牛逼的不行的控件或第三方框架)虽然我们造不出来,但是这种花点时间和精力能造的小轮子还是很有必要自己去实现下的。
        这里的轮播肯定还是建立在ViewPager的基础上,使用Handler控制轮播,即使用 Handler.sendEmptyMessageDelayed(MESSAGE_WHAT, showDuration)。这里无限轮播的思路是重写PagerAdapter的时候,getCount方法返回要轮播图数量的一定倍数,假如说有三张轮播图,轮播倍数为2,这样ViewPager的Item个数实际就有6个,然后轮播展示的时候使用 ViewPager.setCurrentItem(3)方法将首次展示的Item的定位在第四张,这样既可以向前滑动也可以向后滑动,滑动到边界时,假如滑动到第一张或最后一张时,在 PagerAdapter.finishUpdate()方法中,使用 ViewPager.setCurrentItem(position)将Item设置在同一图的对应位置。这里关键代码如下:
    private class BannerPagerAdapter extends PagerAdapter {
        @Override
        public int getCount() {
            return pagerMaxSize;
        }

        ......

        @Override
        public void finishUpdate(ViewGroup container) {
            super.finishUpdate(container);
            int position = pager.getCurrentItem();
            if (position == 0) {
                position = urls.size();
                pager.setCurrentItem(position, false);
            } else if (position == pagerMaxSize - 1) {
                position = urls.size() - 1;
                pager.setCurrentItem(position, false);
            }
        }
        ......
    }
OK,这样无限轮播就搞定了。

没有个轮播指示器啥的吗?当然得有啊!

轮播指示器


这里指示器使用自定义View去实现,我希望实现一个大小和颜色能动态展示当前ViewPager变化的Indicator,就命名为IndicatorView吧。关键代码如下,顺便写个ValueAnimator测试下:
public class TestView extends View {
    ......
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float width = getWidth();
        float height = width / 3 ;

        float x = (width - height) * (1 - offset) / 2.0f;
        path.reset();
        rectF.set(x, 0, height + x, height);
        path.arcTo(rectF, 90, 180);
        rectF.set(width - height - x, 0, width - x, height);
        path.arcTo(rectF, 270, 180);
        path.close();

        canvas.drawPath(path, paint);
    }

    private void startAnim() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(3000);
        animator.setRepeatCount(1000);
        animator.setRepeatMode(ValueAnimator.REVERSE);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                offset = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
    }
}
实际效果是这样的:

颜色怎么平滑过渡呢?可以使用属性动画来实现动态的设置Paint的颜色设置,首先继承Paint实现Paint颜色的get,set方法:
public class ColorPaint extends Paint {
    public final static Property<ColorPaint, Integer> PAINT_COLOR =
            new Property<ColorPaint, Integer>(Integer.class, "PaintColor") {
                @Override
                public Integer get(ColorPaint object) {
                    return object.getColor();
                }

                @Override
                public void set(ColorPaint object, Integer value) {
                    object.setColor(value);
                }
            };
}
使用属性动画实现动态的Paint颜色:
    private void colorAnim(int currentTime) {
        if (mAnimator == null) {
            mAnimator = ObjectAnimator.ofInt(paint, ColorPaint.PAINT_COLOR, Color.GRAY, Color.BLUE);
            mAnimator.setEvaluator(new ArgbEvaluator());
            mAnimator.setInterpolator(new LinearInterpolator());
            mAnimator.setDuration(3000);
        }
        mAnimator.setCurrentPlayTime(currentTime);
    }
ValueAnimator.setCurrentPlayTime(long playTime),见名知义,唉!想当初为了实现一个色值渐变功能,费了老大的劲而且效果还不太好,后来才知道有这个方法,老泪纵横,相见恨晚啊!
然后效果这样的:


总结:


然后将他们组装起来就OK了。为了使用方便定义了几个属性和方法:
    <declare-styleable name="SupperBannerView">
        <attr name="indicatorColor" format="color" />//指示器颜色
        <attr name="selectIndicatorColor" format="color" />//当前选中的指示器颜色
        <attr name="indicatorSpace" format="dimension" />//指示器之间的间距
        <attr name="indicatorWidth" format="dimension" />//指示器的宽
        <attr name="indicatorHeight" format="dimension" />//指示器的高
        <attr name="showDuration" format="integer" />//轮播展示时间
        <attr name="pagerChangeDuration" format="integer" />//page切换时间,通过反射ViewPager设置
        <attr name="onePageCanScroll" format="boolean" />//只有一页时是否可以切换
        <attr name="isAutoScroll" format="boolean" />//是否自动切换
    </declare-styleable>
使用SupperBannerView.start(List<String> urls)设置轮播并启动,使用pause()和resume()暂定和恢复轮播,cancel()取消轮播。使用SupperBannerView.setItemClickListener(ItemClickListener listener)设置轮播的Item点击,最后的效果如下:


另外关于 ViewPager.OnPageChangeListener在当前界面处于不可见时,其中的几个实现方法便不再执行,看了下源码,是在ViewPager类中 public void computeScroll()方法中被调用,这只是调用的其中之一,而这个方法是重写View类的一个方法,然后顺藤摸瓜......额,不好意思!藤太长了,看到最后也没看出个所以然来,但是看View类中相关的几个方法,应该是跟View的可见性有关(当我没说吧)。若你知道,还请大神告知,谢谢!




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值