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

自定义ViewPager实现图片自动轮播无限循环

时间:2021-01-10 07:38:50

相关推荐

自定义ViewPager实现图片自动轮播无限循环

Github 获取最新版本

Viewpager图片自动轮播无限循环是Android项目中经常用到的功能,功能实现起来也比较简单,但会出现不少问题。因此很多情况下做出来的效果并不太让人满意,甚至有些上线的项目自动轮播上也会出现一些bug。比如切换过程中出现空白页面,有些甚至在滑动过程中造成程序崩溃。本文内容对ViewPager进行封装,实现了可循环轮播的CirclSeViewPager,** 该控件有较强的可扩展性,可接受任意类型的集合数据,可以自定义任意的轮播页面样式。页面切换也比较流畅。**

先看效果图:

接下来将通过以下几个小节对CircleViewPager做具体的分析。

如何使用CircleViewPagerCircleViewPager的实现思路CircleViewPager具体实现CircleViewPager实现自动轮播CircleViewPager页面点击事件

如果只是想实现无限轮播的效果那么只需看第一节即可,源码可在文章末尾下载。

一、如何使用CircleViewPager.。

CircleViewPager的使用非常简单,只需要几行代码就可实现,使用方法如下:

1.gradle中添加依赖

2.在xml文件中添加如下代码:

<com.zhpan.viewpager.view.CircleViewPagerandroid:id="@+id/viewpager2"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginEnd="35dp"android:layout_marginStart="35dp"app:interval="5000" />

3.CircleViewPager属性

// 是否显示指示器mViewpager.isShowIndicator(true);// 设置指示器位置mViewpager.setIndicatorGravity(CircleViewPager.IndicatorGravity.END);// 设置指示器圆点半径mViewpager.setIndicatorRadius(6);// 设置圆点指示器颜色mViewPager.setIndicatorColor(getResources().getColor(R.color.colorAccent),getResources().getColor(R.color.colorPrimary));// 设置是否无限循环mViewpager.setCanLoop(true); // 设置是否自动轮播mViewpager.setAutoPlay(true);// 设置图片切换时间间隔mViewpager.setInterval(3000);// 设置页面点击事件mViewpager.setOnPageClickListener(new CircleViewPager.OnPageClickListener() {@Overridepublic void onPageClick(int position) {List<DataBean> list = mViewpager.getList();Toast.makeText(MainActivity.this, "点击了" + list.get(position).getDescribe(), Toast.LENGTH_SHORT).show();}});// 设置数据mViewpager.setPages(mList, new HolderCreator<ViewHolder>() {@Overridepublic ViewHolder createViewHolder() {return new MyViewHolder();}});

4.自定义ViewHolder

public class MyViewHolder implements ViewHolder<String> {private ImageView mImageView;@Overridepublic View createView(Context context) {// 返回页面布局文件View view = LayoutInflater.from(context).inflate(R.layout.banner_item, null);mImageView = (ImageView) view.findViewById(R.id.banner_image);return view;}@Overridepublic void onBind(Context context, int position, String data) {// 数据绑定ImageLoaderUtil.loadImg(mImageView, (String) data);}}

5.为防止内存泄露在onDestory()中停止图片轮播

@Overrideprotected void onDestroy() {super.onDestroy();mViewpager.stopLoop();}

二、CircleViewPager的实现思路

页面循环切换最容易出现问题的地方就是在最后一页向第一页切换或者第一页向最后一页切换时,在这个切换过程中很容易出现空白页面。怎么解决这个问题?

CircleViewPager的实现的思路是在第一张图片前和最后一张图片后分别添加一个ImageView,最前边的ImageView背景设置为最后一张图片,最后一个ImageView背景设置第一张图片。当我们判断滑动到最后一个ImageView时则设置ViewPager.setCurrentItem(1),让其自动切换到第一张图片,这样在从最后一页切换到第一页时由于图片是用的同一张图片,所以就会使切换效果显得很流畅自然。同理,当向左滑动到第0个ImageView时用ViewPager.setCurrentItem(length)自动切换到倒数第二张图片,第0个ImageView和倒数第二个ImageView图片相同,这样就使滑动效果显得很自然。

三、CircleViewPager具体实现

1.添加自定义属性。以在value目录下创建attrs.xml文件,文件中我们可以定义一些用到的属性,attrs.xml中定义的属性如下:

<resources><declare-styleable name="MyViewPager"><!--选中时的圆点图片--><attr name="lightDotRes" format="reference"/><!--未选中时的圆点图片--><attr name="darkDotRes" format="reference"/><!--圆点半径--><attr name="dotWidth" format="dimension"/><!--页面切换时间间隔--><attr name="interval" format="integer"/></declare-styleable></resources>

2.新建CircleViewPager的布局文件view_pager_layout.xml,代码如下:

<RelativeLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v4.view.ViewPagerandroid:id="@+id/vp_main"android:layout_width="match_parent"android:layout_height="match_parent" /><LinearLayoutandroid:id="@+id/ll_main_dot"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignBottom="@id/vp_main"android:layout_marginBottom="10dp"android:gravity="center_horizontal"android:orientation="horizontal" /></RelativeLayout>

3.定义CircleViewPager类并继承FrameLayout,并在构造方法中初始化数据,代码如下:

public CircleViewPager(Context context) {super(context);init(null);}public CircleViewPager(Context context, AttributeSet attrs) {this(context, attrs, 0);init(attrs);}public CircleViewPager(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(attrs);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);if (changed) {initData();setIndicatorImage();setViewPager();setIndicatorLocation();}}private void init(AttributeSet attrs) {if (attrs != null) {TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CircleViewPager);mLightIndicator = typedArray.getResourceId(R.styleable.CircleViewPager_lightDotRes, R.drawable.red_dot);mDarkIndicator = typedArray.getResourceId(R.styleable.CircleViewPager_darkDotRes, R.drawable.red_dot_night);mDotWidth = typedArray.getDimension(R.styleable.CircleViewPager_dotWidth, 20);interval = typedArray.getInteger(R.styleable.CircleViewPager_interval, 3000);typedArray.recycle();}mView = LayoutInflater.from(getContext()).inflate(R.layout.view_pager_layout, this);mLlDot = (LinearLayout) mView.findViewById(R.id.ll_main_dot);mViewPager = (ViewPager) mView.findViewById(R.id.vp_main);mList = new ArrayList<>();mListAdd = new ArrayList<>();mIvDotList = new ArrayList<>();}

4.重写onLayout()方法,根据图片URL集合创建图片对应的ImageVIew和小圆点对应的ImageView.

@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);if (changed) {initData();setIndicatorImage();setViewPager();setIndicatorLocation();}}// 根据mList数据集构造mListAddprivate void initData() {if (mList.size() == 0) {mView.setVisibility(GONE);} else if (mList.size() == 1) {mListAdd.add(mList.get(0));} else if (mList.size() > 1) {for (int i = 0; i < mList.size() + 2; i++) {if (i == 0) {// 判断当i=0为该处的mList的最后一个数据作为mListAdd的第一个数据mListAdd.add(mList.get(mList.size() - 1));} else if (i == mList.size() + 1) {// 判断当i=mList.size()+1时将mList的第一个数据作为mListAdd的最后一个数据mListAdd.add(mList.get(0));} else {// 其他情况mListAdd.add(mList.get(i - 1));}}}}// 设置轮播小圆点private void setIndicatorImage() {// 设置LinearLayout的子控件的宽高,这里单位是像素。LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) mDotWidth, (int) mDotWidth);params.rightMargin = (int) (mDotWidth / 1.5);if (mList.size() > 1) {// for循环创建mUrlList.size()个ImageView(小圆点)for (int i = 0; i < mList.size(); i++) {ImageView imageViewDot = new ImageView(getContext());imageViewDot.setLayoutParams(params);// 设置小圆点的背景为暗红图片imageViewDot.setBackgroundResource(mDarkIndicator);mLlDot.addView(imageViewDot);mIvDotList.add(imageViewDot);}}//设置第一个小圆点图片背景为红色if (mList.size() > 1) {mIvDotList.get(dotPosition).setBackgroundResource(mLightIndicator);}}

5.为ViewPager适配数据

private void setViewPager() {CirclePagerAdapter<T> adapter = new CirclePagerAdapter<>(mListAdd, this, holderCreator);mViewPager.setAdapter(adapter);mViewPager.setCurrentItem(currentPosition);setPageChangeListener();startLoop();setTouchListener();if (showIndicator) {mLlDot.setVisibility(VISIBLE);} else {mLlDot.setVisibility(GONE);}}

6.接下来为ViewPager添加页面改变的监听事件。

// ViewPager页面改变监听private void setPageChangeListener() {mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {pageSelected(position);}@Overridepublic void onPageScrollStateChanged(int state) {// 当state为SCROLL_STATE_IDLE即没有滑动的状态时切换页面if (state == ViewPager.SCROLL_STATE_IDLE) {mViewPager.setCurrentItem(currentPosition, false);}}});}private void pageSelected(int position) {if (position == 0) {//判断当切换到第0个页面时把currentPosition设置为list.size(),即倒数第二个位置,小圆点位置为length-1currentPosition = mList.size();dotPosition = mList.size() - 1;} else if (position == mList.size() + 1) {//当切换到最后一个页面时currentPosition设置为第一个位置,小圆点位置为0currentPosition = 1;dotPosition = 0;} else {currentPosition = position;dotPosition = position - 1;}// 把之前的小圆点设置背景为暗红,当前小圆点设置为红色mIvDotList.get(prePosition).setBackgroundResource(mDarkIndicator);mIvDotList.get(dotPosition).setBackgroundResource(mLightIndicator);prePosition = dotPosition;}

至此,ViewPager已经可以实现滑动,并且圆点也会跟随页面滑动而改变。

四、CircleViewPager实现自动轮播

1.自动轮播的实现,在第三节第5步的setViewPager()方法中调用下面startLoop()方法即可开启自动轮播

Handler mHandler = new Handler();Runnable mRunnable = new Runnable() {@Overridepublic void run() {if (mViewPager.getChildCount() > 1) {mHandler.postDelayed(this, interval);currentPosition++;mViewPager.setCurrentItem(currentPosition, true);}}};private void startLoop() {if (!isLoop && mViewPager != null) {mHandler.postDelayed(mRunnable, interval);// 每两秒执行一次runnable.isLoop = true;}}

2.自动轮播实现后发现会有些问题,即在手动滑动页面时,页面仍然会自动切换。这样体验效果是非常不好的,因此我们需要在手动滑动时停止自动轮播,当手动滑动结束时再开启自动轮播。因此我们可以重写ViewPager的onTouch事件进行处理,当触发ACTION_DOWN和ACTION_MOVE时停止自动轮播,当触发ACTION_UP和ACTION_CANCEL时再开启自动轮播。代码实现如下:

// 设置触摸事件,当滑动或者触摸时停止自动轮播private void setTouchListener() {mViewPager.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {int action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:isLoop = true;stopLoop();break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:isLoop = false;startLoop();default:break;}return false;}});}private void startLoop() {if (!isLoop && mViewPager != null) {mHandler.postDelayed(mRunnable, interval);// 每interval秒执行一次runnable.isLoop = true;}}public void stopLoop() {if (isLoop && mViewPager != null) {mHandler.removeCallbacks(mRunnable);isLoop = false;}}

至此,CircleViewPager的核心功能已经实现。但是页面的点击事件还未进行处理。接下来将在第五节中实现CircleViewPager的页面点击事件。

五、CircleViewPager页面点击事件。

1.页面的点击事件是通过点击ImageView触发的,因此我们可以首先考虑给页面的ImageView设置点击事件的监听。在哪里设置点击事件?无疑在Adapter中设置是比较简单的,因此我们在CircleViewPager的Adapter中对ImageView添加点击事件监听。如下:

@Overridepublic Object instantiateItem(final ViewGroup container, final int position) {View view = getView(position, container);container.addView(view);return view;}// 根据图片URL创建对应的ImageView并添加到集合private View getView(final int position, ViewGroup container) {ViewHolder holder = holderCreator.createViewHolder();if (holder == null) {throw new RuntimeException("can not return a null holder");}View view = holder.createView(container.getContext());if (list != null && list.size() > 0) {holder.onBind(container.getContext(), position, list.get(position));}view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {viewPager.imageClick(position - 1);}});return view;}

我们事先在CircleViewPager中定义imageClick()方法,在实例化Adapter的时候将CircleViewPager自身传递进来,然后就可以在Adapter调用imageClick()方法。即图片被点击的时候会触发到CircleViewPager中的imageClick()方法。

2.模仿Android中View的监听事件来为CircleViewPager设置点击页面的事件监听。在CircleViewPager中定义OnPageClickListener接口,并在接口中定义pageClickListener(int position)的抽象方法。然后在CircleViewPager中定义OnPageClickListener的成员变量mOnPageClickListener,并为其设置set()方法。然后在imageClick()方法中调用mOnPageClickListener.pageClickListener(int position)。代码实现如下:

private OnPageClickListener mOnPageClickListener;public void setOnPageClickListener(OnPageClickListener onPageClickListener) {this.mOnPageClickListener = onPageClickListener;}// adapter中图片点击的回掉方法public void imageClick(int position) {mOnPageClickListener.pageClickListener(position);}// 监听页面点击的接口public interface OnPageClickListener {void pageClickListener(int position);}

3.MainActivity中设置页面点击的监听。通过CicleViewPager.setOnPageClickListener实现对页面点击的监听。

mViewpager.setOnPageClickListener(new CircleViewPager.OnPageClickListener() {@Overridepublic void pageClickListener(int position) {Toast.makeText(MainActivity.this, "点击了第"+position+"个美眉 \nURL:"+mViewpager.getUrlList().get(position), Toast.LENGTH_SHORT).show();}});

Github 获取最新版本

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