ViewPager实现无限轮播踩坑记

Posted 一名Android小生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ViewPager实现无限轮播踩坑记相关的知识,希望对你有一定的参考价值。

        最近笔者想通过ViewPager来实现一个广告Banner,并实现无限轮播的效果,但是在这个过程中踩了不少的坑,听我慢慢道来。如果大家有遇到和我一样的情况,可以参考我的解决方法,没有那就更好,如果针对我的解决方法,有啥更好的方案,欢迎和我分享

使用ViewPager实现无限轮播代码

 MainActivity代码

public class MainActivity extends AppCompatActivity {

private ViewPager mViewPager;
private TextView mTvDesTitle;
private LinearLayout mPointContainer;
private List<ImageView> mImageViewList;
private String[] mDesTitles;
private int[] mImageIds;
private int previousSelectPos = 0;//用于记录上一次选中的小圆点位置
private boolean isRunning = false;//定义一个标记,用于判断是否轮播

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData();
initAdapter();
//实现ViewPager的无限轮播
new Thread() {
@Override
public void run() {
isRunning = true;
while (isRunning) {
try {
Thread.sleep(2 * 1000);//每隔2s切换一次
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新ViewPager的显示,更新UI要在主线程实现
runOnUiThread(new Runnable() {
@Override
public void run() {
mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
}
});

}
}
}.start();
}

private void initViews() {
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mTvDesTitle = (TextView) findViewById(R.id.tv_des_title);
mPointContainer = (LinearLayout) findViewById(R.id.ll_point_container);


}

private void initData() {
//初始化轮播图片
mImageIds = new int[]{R.drawable.bannerone, R.drawable.bannertwo, R.drawable.bannerthree};
//初始化描述标题
mDesTitles = new String[]{"描述标题1", "描述标题2", "描述标题3"};
//初始化ImageView集合
mImageViewList = new ArrayList<>();
//for循环,添加ImageView和小圆点
ImageView imageView;
View pointView;
LinearLayout.LayoutParams layoutParams;
for (int i = 0; i < mImageIds.length; i++) {
//添加ImageView
imageView = new ImageView(this);
imageView.setBackgroundResource(mImageIds[i]);
mImageViewList.add(imageView);
//添加小圆点
pointView = new View(this);
pointView.setBackgroundResource(R.drawable.point_selector);
//设置小圆点的布局参数,宽和高
layoutParams = new LinearLayout.LayoutParams(dp2px(5), dp2px(5));
//设置enable状态,默认都是false 灰色。
pointView.setEnabled(false);
if (i != 0) {
layoutParams.leftMargin = dp2px(10);
}
//将小圆点添加到LinearLayout中
mPointContainer.addView(pointView, layoutParams);
}

}

private void initAdapter() {
mViewPager.setAdapter(new BannerAdapter());
//设置默认显示第一个标题
mTvDesTitle.setText(mDesTitles[0]);
//设置第一个小圆点为白色
mPointContainer.getChildAt(0).setEnabled(true);
//设置在中间某个位置
int pos = Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2 % mImageViewList.size());
mViewPager.setCurrentItem(pos);//可以实现左右无限轮播
//ViewPager设置滑动监听
mViewPager.addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {
/* //滑到某个位置
mTvDesTitle.setText(mDesTitles[position]);
//将上一次选中的置为false
mPointContainer.getChildAt(previousSelectPos).setEnabled(false);
//新选中的置为true
mPointContainer.getChildAt(position).setEnabled(true);
//更新选中的记录
previousSelectPos = position;*/
//实现无限轮播,positon会变,要更新位置
//滑到某个位置
int newPosition = position % mImageViewList.size();
mTvDesTitle.setText(mDesTitles[newPosition]);
//将上一次选中的置为false
mPointContainer.getChildAt(previousSelectPos).setEnabled(false);
//新选中的置为true
mPointContainer.getChildAt(newPosition).setEnabled(true);
//更新选中的记录
previousSelectPos = newPosition;

}

@Override
public void onPageScrollStateChanged(int state) {

}
});

}

class BannerAdapter extends PagerAdapter {

@Override
public int getCount() {
//return mImageViewList.size();
//实现无限轮播
return Integer.MAX_VALUE;
}

@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
//ImageView imageView = mImageViewList.get(position);
//container.addView(imageView);
//return imageView;
//实现无限轮播
final int newPosition = position % mImageViewList.size();
ImageView imageView = mImageViewList.get(newPosition);
/**
* 我在轮播的时候同时手动滑动, java.lang.IllegalStateException:
* The specified child already has a parent. You must call removeView() on the child‘s parent first.
*/
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "你点击了"+newPosition, Toast.LENGTH_SHORT).show();
}
});
container.addView(imageView);
return imageView;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//这一句在只有三张图片的时候出现了滑动出现空白情况
container.removeView((View) object);
}
}

/**
* px转dp
*/
public int px2dp(float pxValue) {
float scale = this.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}

/**
* dp转px
*/
public int dp2px(float dpValue) {
final float scale = this.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}

@Override
protected void onDestroy() {
super.onDestroy();
isRunning = false;
}
}

  

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.zhiji.bannerdemo.MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="180dp">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent">

</android.support.v4.view.ViewPager>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentBottom="true"
android:paddingTop="5dp"
android:paddingBottom="6dp"
android:gravity="center_horizontal"
android:background="#66000000">
<TextView
android:id="@+id/tv_des_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#fff"
android:textSize="16sp"
android:text="Hello World!" />
<LinearLayout
android:id="@+id/ll_point_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="horizontal">

</LinearLayout>
</LinearLayout>
</RelativeLayout>

</RelativeLayout>

  小圆点

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<corners android:radius="5dp" />
<solid android:color="@android:color/darker_gray" />

</shape>

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<corners android:radius="5dp"/>
<solid android:color="@android:color/white"/>

</shape>

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@drawable/point_white" android:state_enabled="true" />
<item android:drawable="@drawable/point_gray" android:state_enabled="false" />
</selector>

  

 踩坑分析


1.笔者轮播图片使用了三张,也就是三张图片的轮回切换,上面代码基本实现了无限轮播的效果
但是运行过程中,在它自身无限自动轮播的同时,我同时手动进行了向左或者向右滑动,这时候程序崩溃了,报错如下:


 java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child‘s parent first.
询问度娘后,也参考了网上的一些方案,最终解决方案如下:

@Override
public Object instantiateItem(ViewGroup container, int position) {
//ImageView imageView = mImageViewList.get(position);
//container.addView(imageView);
//return imageView;
//实现无限轮播
final int newPosition = position % mImageViewList.size();
ImageView imageView = mImageViewList.get(newPosition);

imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "你点击了"+newPosition, Toast.LENGTH_SHORT).show();
}
});

//新增的代码,用于解决上面提出的错误
ViewGroup parent= (ViewGroup) imageView.getParent();
if(parent!=null){
parent.removeView(imageView);
}

container.addView(imageView);
return imageView;
}

  

加上上面的代码后,笔者在滑动的时候确实不会发生崩溃了,但是左右滑动得时候,会出现一块空白区域,很是苦恼,最终询问度娘看了网友的一些解决方案,方案如下

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//这一句在只有三张图片的时候出现了滑动出现空白情况
//新增,将下面这行代码去除就可以解决上面的问题
//container.removeView((View) object);
}

  


好了。使用上面的方案后,确实不会发生白块的现象了。但是很纳闷,这个问题是为什么呢???接着笔者又把三张轮播图换成了4张、5张。神奇的现象发生了,竟然没有出现上面的两种错误问题。

 总结

ViewPager实现无限轮播,图片在三张及以下,使用的时候要注意,出现笔者的问题可参考我的解决方案,如果大家有更好的方案,或者针对我的问题,能指出我的问题所在,很是感谢。滑动图片如果在3上以上就可以直接使用我上面的代码。

以上是关于ViewPager实现无限轮播踩坑记的主要内容,如果未能解决你的问题,请参考以下文章

android实现无限轮播

使用ViewPager实现图片轮播

ViewPager,实现真正的无限循环(定时+手动)

Android ViewPager+轮播图

Android无限广告轮播 - ViewPager源码分析

ViewPager网络图片无限轮播