参考文章:https://blog.csdn.net/qq_20521573/article/details/52037929
使用流程:
1 。 gradle中添加依赖
compile 'com.zhpan.library:viewpager:1.0.3'
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属性
mList.add(R.drawable.books_image_1); mList.add(R.drawable.books_image_2); mList.add(R.drawable.books_image_3); mList.add(R.drawable.books_image_4); mList.add(R.drawable.books_image_5); // 是否显示指示器 mViewpager.isShowIndicator(true); // 设置指示器位置 mViewpager.setIndicatorGravity(CircleViewPager.IndicatorGravity.CENTER); // 设置指示器圆点半径 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) { UIUtils.showToast(" 我是轮播图 我点击了!"+ position + "张"); } }); // 设置数据 mViewpager.setPages(mList, new HolderCreator<ViewHolder>() { @Override public ViewHolder createViewHolder() { return new MyViewHolder(); } });
4.自定义 ViewHolder
public class MyViewHolder implements ViewHolder<Integer> { private ImageView mImageView; @Override public View createView(Context context, int position) { // 返回页面布局文件 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, Integer data, int position, int size) { // ImageLoaderUtil.loadImg(mImageView, (String) data); Glide.with(context).load(data).into(mImageView); } }
5.为防止内存泄露在onDestory()中停止图片轮播
@Override
protected 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="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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
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;
}
至此,ViewPager已经可以实现滑动,并且圆点也会跟随页面滑动而改变。
四、CircleViewPager实现自动轮播
1.自动轮播的实现,在第三节第5步的setViewPager()方法中调用下面startLoop()方法即可开启自动轮播
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;
}
}
至此,CircleViewPager的核心功能已经实现。但是页面的点击事件还未进行处理。接下来将在第五节中实现CircleViewPager的页面点击事件。
五、CircleViewPager页面点击事件。
1.页面的点击事件是通过点击ImageView触发的,因此我们可以首先考虑给页面的ImageView设置点击事件的监听。在哪里设置点击事件?无疑在Adapter中设置是比较简单的,因此我们在CircleViewPager的Adapter中对ImageView添加点击事件监听。如下:
@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;
}
我们事先在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() {
@Override
public void pageClickListener(int position) {
Toast.makeText(MainActivity.this, "点击了第"+position+"个美眉 \nURL:"+mViewpager.getUrlList().get(position), Toast.LENGTH_SHORT).show();
}
});