Android轮播实现

Android轮播实现

前几天研究了Android的事件分发机制和View的实现流程,然后按照教程实现了轮播图,现在赶紧整理一下写成博客,方便日后参考,防止自己忘记。整个实现过程是一个由浅入深,循序渐进的过程,所以,实现的思路很重要。
首先要明白,轮播图核心是一个自定义ViewGroup,而非自定义View,在此称之为LoopViewGroup,大体的实现思路主要有一下几点:

  1. LoopViewGroup的绘制过程;
  2. 利用Scroller实现手动轮播效果
  3. 利用Timer、TimerTask、Handler实现自动轮播的效果
  4. 实现轮播图片的点击事件
  5. 底部轮播圆点的布局及切换过程

LoopViewGroup的绘制过程

首先,要重写onMeasure()方法,在该方法中,根据子视图的个数以及第一个子视图的测量宽高,来设置LoopViewGroup自身的测量宽高,相关代码如下:

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //1、获取子视图的个数
        mChildCount = getChildCount();
        //2、测量子视图的宽高
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        View view = getChildAt(0);
        if (view != null && view.getVisibility() != GONE) {
            childMeasureWidth = view.getMeasuredWidth();
            childMeasureHeight = view.getMeasuredHeight();
        }
        //3、设置测量宽高
        if (mChildCount == 0) {
            setMeasuredDimension(0, 0);
        } else {
            setMeasuredDimension(childMeasureWidth * mChildCount, childMeasureHeight);
        }
    }

其次,要重写onLayout()方法,在该方法中,遍历子视图,并设置子视图的位置,子视图是在LoopViewGroup中水平依次平铺的,相关代码:

protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int marginLeft = 0;
            for (int i = 0; i < mChildCount; i++) {
                View childView = getChildAt(i);
                if (childView != null && childView.getVisibility() != GONE) {
                    childView.layout(marginLeft, t, marginLeft + childMeasureWidth, b);
                    marginLeft += childMeasureWidth;
                }
            }
        }
    }

由于绘制过程是最简单的自定义ViewGroup的过程,所以此处不作说明。

利用Scroller实现手动轮播效果

要想实现手动轮播,肯定要涉及到事件的分发过程,所以就需要拦截事件和对事件进行处理,也就是重写onInterceptTouchEvent()返回true,并重写onTouchEvent()方法,其中有四个要点需要清楚:

  1. 我们在滑动图片的过程中,其实就是我们自定义ViewGroup滑动子试图的移动过程,那么只需要知道滑动之前的横坐标和滑动之后的横坐标,此时可以求得此次移动过程的距离,我们再利用scrollBy方法实现图片的滑动,所以,有两个值需要知道,滑动前的横坐标和滑动后的横坐标;
  2. 我们在第一次按下的一瞬间,此时的移动之前的横坐标和移动之后的横坐标是相等的,也就是我们按下的那一点的横坐标;
  3. 我们在滑动过程中,是不断的调用ACTION_MOVE方法的,因此我们就应该将移动之前和移动之后的值进行保存,以便我们计算滑动的距离;此时如果是第一张图片,就不允许再向右滑,如果是最后一张图片,就不允许再向左滑;
  4. 我们在抬起手的一瞬间,需要计算将要滑动到哪个图片上,此时可以求的将要滑动到那张图片的索引值:(当前ViewGroup的滑动位置 + 我们每张图片的宽度 / 2) / 每张图片的宽度;然后就可以利用Scroller滑动到图片上。

相关代码如下:

public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {  //如果滑动还未停止,又发生了点击事件,则停止之前的滑动,重新开始
                    mScroller.abortAnimation();
                }
                x = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getX();
                int distance = moveX - x;
                if (distance > 0 && index <= 0) {
                } else if (distance < 0 && index >= mChildCount - 1) {
                } else {
                    scrollBy(-distance, 0);
                    x = moveX;
                }
                break;
            case MotionEvent.ACTION_UP:
                int scroll = getScrollX();
                index = (scroll + childMeasureWidth / 2) / childMeasureWidth;
                if (index < 0) {
                    index = 0;
                } else if (index > mChildCount - 1) {
                    index = mChildCount - 1;
                } else {
                    int dx = index * childMeasureWidth - scroll; //需要滑动的距离
                    smoothScrollBy(dx, 0);
                }
                break;
            default:
                break;
        }
        return true;
    }
private void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(getScrollX(), 0, dx, dy, 500);
        invalidate();
}
@Override
public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

要想利用Scroller实现弹性滑动,而非突兀的完成切换过程,需要在抬起手的瞬间启动Scroller,而invalidate()方法会不断的触发computeScroll()方法,从而实现动画切换的效果;当然,在按下屏幕的瞬间,要判断Scroller是否结束,没有结束需要手动结束,以便进入下一次循环,否则会造成事件紊乱。现在,已经可以实现手动轮播效果了。

利用Timer、TimerTask、Handler实现自动轮播的效果

利用Timer,TimerTask,Handler三者实现自动轮播,有以下几个要点:

  1. 需要两个方法来控制自动轮播的启动和关闭,我们称之为自动轮播的开关,分别为startAuto(),stopAuto();还需要一个标志来表明当前自动轮播的状态时开启还是关闭,设为布尔类型isAuto,true表示自动轮播启动,false表示自动轮播关闭。
  2. 在LoopViewGroup的构造方法中,设置一个定时任务,如果自动轮播处于开启状态,则利用Handler每间隔一段时间发送一个空的消息,而在Handler接受消息后利用scrollTo()方法实现图片的轮播,相关代码如下:
private void init() {
        if (mScroller == null) {
            mScroller = new Scroller(getContext());
        }
        timerTask = new TimerTask() {
            @Override
            public void run() {
                if (isAuto) {
                    autoHandler.sendEmptyMessage(0);
                }
            }
        };
        timer.schedule(timerTask, 100, periodTime);
}

private Handler autoHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    if (++index >= mChildCount) { //如果当前图片时最后一张,那么下一张图片是第一张
                        index = 0;
                    }
                    scrollTo(index * childMeasureWidth, 0); //1
                    break;
            }
            super.handleMessage(msg);
        }
    };

在点击发生时,关闭自动轮播,抬起手后需要开启自动轮播,实现过程只需要在onTouchEvent()方法中,当MotionEvent.ACTION_DOWN时,调用stopAuto();当MotionEvent.ACTION_UP时,调用startAuto()即可。

实现轮播图片的点击事件

要想获得点击事件,采用的方法是利用一个变量进行判断,当用户离开屏幕的一瞬间,判断变量开关来判断是点击事件还是移动事件;实现过程如下:

  1. onTouchEvent()中,当MotionEvent.ACTION_DOWN时,设置isClick变量为true;
  2. 当MotionEvent.ACTION_MOVE时,根据移动的距离distance与设备能识别的最小移动距离filter作对比,如果distance>filter,则isClick变量为false,否则为true;
  3. 当MotionEvent.ACTION_UP时,根据isClick的值判断是点击还是移动事件,若为点击事件则将传递给监听者,若为移动事件就进行轮播操作;

底部轮播圆点的布局及切换过程

实现底部圆点以及圆点的切换功能的实现思想:

  1. 需要自定义一个继承FramLayout的布局,利用FrameLayout的特性(在同一个位置放置不同的View,显示最后放入的View),我们就可以实现底部圆点的布局;
  2. 我们需要准备素材,底部圆点的素材,利用Drawable实现两种效果的图片;
  3. 需要自定义一个类继承FrameLayout,在该类的实现过程中,加载自定义的LooPViewGroup类和我们需要实现的底部圆点的布局LinearLayout来实现;

关于自定义类ImageLoopFrameLayout,有以下几个要点需要注意:

  1. 先加载LoopViewGroup布局,后加载圆点布局LinearLayout;
  2. 既然加载了LoopViewGroup,就需要对其点击事件进行监听,并且需要对图片轮播事件进行监听,做出相应的处理;
  3. 对调用者提供接口addBitmaps(List<Bitmap> bitmaps)方法,以便调用者可以设置轮播的图片,LooPViewGroup布局每增加一个图片,LinearLayout布局就必须对应增加一个圆点;
  4. 对调用者提供点击事件的接口,以便调用者可以监听点击事件。

以上,轮播效果就全部完成了,详细的代码请参阅:
https://github.com/jemon9/CustomizeVIew/blob/master/viewlib/src/main/java/imageloader/zzf/com/viewlib/ImageLoopFrameLayout.java
https://github.com/jemon9/CustomizeVIew/blob/master/viewlib/src/main/java/imageloader/zzf/com/viewlib/LoopViewGroup.java

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值