Github 获取最新版本
Viewpager图片自动轮播无限循环是Android项目中经常用到的功能,功能实现起来也比较简单,但会出现不少问题。因此很多情况下做出来的效果并不太让人满意,甚至有些上线的项目自动轮播上也会出现一些bug。比如切换过程中出现空白页面,有些甚至在滑动过程中造成程序崩溃。本文内容对ViewPager进行封装,实现了可循环轮播的CirclSeViewPager, 该控件有较强的可扩展性,可接受任意类型的集合数据,可以自定义任意的轮播页面样式。页面切换也比较流畅。
接下来将通过以下几个小节对CircleViewPager做具体的分析。
- 如何使用CircleViewPager
- CircleViewPager的实现思路
- CircleViewPager具体实现
- CircleViewPager实现自动轮播
- CircleViewPager页面点击事件
如果只是想实现无限轮播的效果那么只需看第一节即可,源码可在文章末尾下载。
2.在xml文件中添加如下代码:
<com.zhpan.viewpager.view.CircleViewPager android: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() {
@Override public 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>() {
@Override public ViewHolder createViewHolder() {
return new MyViewHolder(); } });
4.自定义ViewHolder
public class MyViewHolder implements ViewHolder<String> {
private ImageView mImageView; @Override public 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; } @Override public void onBind(Context context, int position, String data) {
// 数据绑定 ImageLoaderUtil.loadImg(mImageView, (String) data); } }
5.为防止内存泄露在onDestory()中停止图片轮播
@Override protected void onDestroy() {
super.onDestroy(); mViewpager.stopLoop(); }
<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="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.ViewPager android:id="@+id/vp_main" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android: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); } @Override protected 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.
@Override protected 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数据集构造mListAdd private 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() {
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
} @Override public void onPageSelected(int position) {
pageSelected(position); } @Override public 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-1 currentPosition = mList.size(); dotPosition = mList.size() - 1; } else if (position == mList.size() + 1) {
//当切换到最后一个页面时currentPosition设置为第一个位置,小圆点位置为0 currentPosition = 1; dotPosition = 0; } else {
currentPosition = position; dotPosition = position - 1; } // 把之前的小圆点设置背景为暗红,当前小圆点设置为红色 mIvDotList.get(prePosition).setBackgroundResource(mDarkIndicator); mIvDotList.get(dotPosition).setBackgroundResource(mLightIndicator); prePosition = dotPosition; }
Handler mHandler = new Handler(); Runnable mRunnable = new Runnable() {
@Override public 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() {
@Override public 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; } }
@Override public 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() {
@Override public void onClick(View v) {
viewPager.imageClick(position - 1); } }); return view; }
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() {
@Override public void pageClickListener(int position) {
Toast.makeText(MainActivity.this, "点击了第"+position+"个美眉 \nURL:"+mViewpager.getUrlList().get(position), Toast.LENGTH_SHORT).show(); } });
Github 获取最新版本
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203939.html原文链接:https://javaforall.net
