300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android之自定义ViewPager实现图片的无线轮播

Android之自定义ViewPager实现图片的无线轮播

时间:2020-05-30 00:02:52

相关推荐

Android之自定义ViewPager实现图片的无线轮播

PS:以前也写过关于图片轮播这一块的博客.不过写的很烂,并且很多情况没有考虑到(没有支持无线轮播,和手势点击事件).因此这里写一篇补上.也是当时太年轻了.

注:图片请放大后再看.否则看不清楚.

学习内容:

1.自定义ViewPager

2.图片无限轮播由于ViewPager的预加载机制所导致的问题.

以前也写过关于图片轮播的相关博客,不过总体写的非常的烂,并且不能够无线轮播,而且也无法对手势事件进行相关的处理,因此在这里补上,也是属于自定义View的一篇内容吧.并且通过这个过程发现了无限轮播由于ViewPager的预加载机制所导致的问题.也正赶上项目要上一个新的版本,发现了这个bug.我的同事想到了一个比较不错的方案解决了这个问题.总体还是很不错的.因此在这里记录一下.

总体实现无限轮播的思想,其实和网上大部分的思路都是相同的,设置一个Integer的最大值的一半,然后根据position和图片的数量去计算,来实现向左向右无限滑动这个功能.总体不是特别的难.自定义ViewPager之后,把相关的图片和跟随图片滑动时的小圆点传递到ViewPager当中,然后设置相关的滑动监听,Adapter就可以完美的实现图片的无限轮播了.

i.初始化轮播.

初始化轮播需要线程和Handler配合来完成,设置一个无线循环的线程,让这个线程按照一定的周期一直向Activity发送Message,Handler在接收到消息后会不断的对消息进行处理.改变ViewPager当前显示的CurrentItem就可以实现无限轮播了.

/*** 初始化轮播的线程*/public void initLoop() {new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {Thread.sleep(3000);if (!stopLoopTag) {Message message = Message.obtain();message.what = 10;message.arg1 = getCurrentItem() + 1;mHandler.sendMessage(message);}} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}/*** 处理轮播的Handler*/private Handler mHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {if (msg.what == 10 && !stopLoopTag) {setCurrentItem(msg.arg1);} else {mHandler.removeMessages(10);}return true;}});

ii.图片轮播时小圆点随之变动

有了一个子线程和一个Handler就可以实现无线循环的一个过程,我们知道一般图片轮播都需要伴随小圆点的移动,小圆点一般是直接布局到ViewPager当中的,因为需要给用户一种更好的体验性,因此在图片轮播的同时伴随着小圆点也随之变动.那么小圆点如何在ViewPager的Item改变的时候也随之变动呢?这就需要addOnPageChangeListener()来实现了.

private OnPageChangeListener onPageChangeListener = new OnPageChangeListener() {

@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {/*** 改变小圆点的状态* 三种情况:* 1.在初始位置* 2.向左滑动* 3.向右滑动* 不要放在上一个方法中,会有明显的延迟现象出现* lastPointPosition 上一个点的位置,初始化为0* lastPointIndex 上一个点在集合中的位置* currentPointIndex 当前的点在集合中的位置* */if (lastPointPosition == 0) {currentPointIndex = 0;lastPointIndex = 0;} else if (lastPointPosition < position) {if (currentPointIndex == getImages().size() - 1) {currentPointIndex = 0;} else {currentPointIndex += 1;}} else if (lastPointPosition > position) {if (currentPointIndex == 0) {currentPointIndex = getImages().size() - 1;} else {currentPointIndex -= 1;}}dots.get(lastPointIndex).setImageResource(R.drawable.dot_normal);dots.get(currentPointIndex).setImageResource(R.drawable.dot_focus);lastPointPosition = position;lastPointIndex = currentPointIndex;}@Overridepublic void onPageScrollStateChanged(int state) {}};

这里我们通过addOnPageChangeListener()绑定Page的改变监听来改变小圆点随着Page改变的同时随之改变.图片的改变需要在适配器里去设置,适配器我留到最后说,因为其中有很多的细节.这样有了无限循环,原点移动,那么就需要说一下当我们手指停留在ViewPager的时候,如何使ViewPager停止播放.因为涉及到了手势事件,因此就要重写相关的方法.

iii.重写手势事件

/*** 手势事件的重写*/@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:/*** 按下的时候停止轮播* */stopLoop();xDown = (int) event.getX();yDown = (int) event.getY();break;case MotionEvent.ACTION_MOVE:/*** 这里不用做任何处理,移动的时候ViewPager内的图片会自动滑动* */break;case MotionEvent.ACTION_UP:/*** 记录按下时间* */if (timeThread == null) {touchTime = new Date().getTime();timeTag = true;timeThread = new TimeThread();timeThread.start();} else {touchTime = new Date().getTime();}/*** 判断是否是点击事件* */int xUp = (int) event.getX();int yUp = (int) event.getY();if (Math.abs(xDown - xUp) < 20 && Math.abs(yDown - yUp) < 20) {if (onImageItemClickListener != null) {onImageItemClickListener.onItemClick(currentPointIndex);}}break;}return super.onTouchEvent(event);}

手势事件的重写就非常的简单了,当点击的时候停止轮播,移动的时候我们不需要做任何的处理,在手指离开之后,我们需要计算离开的时间,离开的时间超过两秒,就再次开启轮播.关闭轮播只需要停止最开始的那个子线程,让标记位StopLoopTag为true,再将消息队列中的消息移除队列.这样Handler也接收不到消息了.这样就停止了轮播的效果.计算手指按下和离开的时间也需要一个子线程去处理.

/*** 时间线程,用于记录手指离开点击ViewPager的时间* 如果离开的时间 >= 2000毫秒,那么ViewPager继续轮播*/class TimeThread extends Thread {@Overridepublic void run() {while (timeTag) {long currentTime = new Date().getTime();if (currentTime - touchTime >= 2000) {openLoop();timeTag = false;timeThread = null;}}}}

iv.PagerAdapter.

PagerAdapter也是最蛋疼的一块.instantiateItem()方法里的那一大堆代码是关键.很多人都用这样的代码来实现图片的加载过程.

@Overridepublic Object instantiateItem(ViewGroup container, int position) {position %= images.size();if (position < 0) {position = position + images.size();}ImageView imageView = images.get(initPosition);ViewParent parent = imageView.getParent();if (parent != null) {ViewGroup viewGroup = (ViewGroup) parent;viewGroup.removeView(imageView);}container.addView(imageView);return imageView;}

其实这样写是有很大的问题的,因为这里没有考虑到ViewPager的预加载机制的问题,这也是我们项目出现的一个bug,我们是从服务器上获取相关的图片数据,然后保存在集合当中,如果我们在将图片加载到ViewPager中的时候,仅调用image.get(position),position为上面代码计算出的position,这样的话实际上会导致图片错位.虽然显示的是5张图片,但是实际上他们在ViewPager中显示的顺序是不对的.比如说我们在后台定义了这样的顺序, 1,2,3,4,5的顺序发送给我们,但是如果我们按照上面的方法从集合中拿数据的时候,ViewPager显示的不是1,2,3,4,5这样的顺序,这也是我们在项目中发现的问题.因为在点击图片的时候,是需要走不同的链接的,也正是这个原因我们发现了这个bug.因此这里做了很多的处理.

public class PictureAdapter extends PagerAdapter {private List<ImageView> images;/*** initPosition -1为初始化的位置,后面是当前的图片索引位置* topPosition 记录上一次初始化的索引位,用于计算上次的position和本次position的偏移量* * */private int initPosition = -1;private int topPosition = -1;public PictureAdapter(List<ImageView> images) {this.images = images;}@Overridepublic int getCount() {return Integer.MAX_VALUE;}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {}/*** 实例化Item*/@Overridepublic Object instantiateItem(ViewGroup container, int position) {if(images.size() <=1){ImageView imageView = images.get(topPosition);container.addView(imageView);return imageView;}else{/*** 初始化状态* 向左滑动* 向右滑动* 由于ViewPager有预加载机制,默认加载一页,因此在第一次初始化的时候,会调用三次这个方法.* (第一次: position=1073741823 第二次: position=1073741822 第三次: position=1073741824)** 而后续,这个方法仅被执行一次,并且执行的是预加载下一页的请求.* */Log.e("TAG","position="+position);if (initPosition == -1) {/*** 初始化状态* topPosition 记录第一次初始化的索引位.用于后续作比较判断下次是向右滑动还是向左滑动* initPosition 初始化图片集合的索引值* */topPosition = position;initPosition = 0;} else if (topPosition < position) {/*** 向左滑动* 得出偏移量后比较是否超过图片集合的大小* */int value = position - topPosition;initPosition += value;if (initPosition == images.size()) {/*** 滑动到了最后一页* */initPosition = 0;} else if (initPosition > images.size()) {/*** 如果超出了图片集合的大小,则 initPosition = 超过的数值* */initPosition = (initPosition - images.size());}topPosition = position;} else if (topPosition > position) {int value = topPosition - position;initPosition -= value;if (initPosition == -1) {/*** 滑动到了第一页* */initPosition = images.size() - 1;} else if (initPosition < -1) {/*** 当计算后的值小于了集合大小,则用集合大小减去小于的这部分* */initPosition = (images.size() - (Math.abs(initPosition)));}topPosition = position;}Log.e("TAG","topPosition="+topPosition);Log.e("TAG","initPosition="+initPosition);/*** 只用这句话应该会出现问题* */// position %= images.size();// if (position < 0) {// position = position + images.size();// }ImageView imageView = images.get(initPosition);ViewParent parent = imageView.getParent();if (parent != null) {ViewGroup viewGroup = (ViewGroup) parent;viewGroup.removeView(imageView);}container.addView(imageView);return imageView;}}}

这就是我们定义的PagerAdapter.里面做了很多的逻辑处理.一张图解释一下其中的原理.

这张图说明了一种的道理,上面代码不难发现.会出现 (initPosition > images.size())和initPosition < -1这两种情况.这种原因的导致就是因为position会由于这种预加载机制出现数值跳跃问题.这里大家可以去根据Log信息结合我说的原理,好好的研究一下.相信大家会研究明白的.

最后就是点击事件了.对Activity暴露接口,让Activity去实现就可以了.

/*** 对Activity暴露接口*/private OnImageItemClickListener onImageItemClickListener;public interface OnImageItemClickListener {void onItemClick(int itemPosition);}public void setOnImageItemClickListener(OnImageItemClickListener onImageItemClickListener) {this.onImageItemClickListener = onImageItemClickListener;}

剩下的就是MainActivity了.这里就不粘贴代码了.里面的内容比较的简单.这样就实现了图片的无线轮播,并且支持点击停止,松开继续播放的功能,还有的图片点击事件.最后放一个源代码:

源代码地址:/s/1dFqig17

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。