一般Android的轮播图(俗名:Banner)都是用ViewPager实现的,可是我在实际项目运用中碰到了一些小问题,因而决定另寻思路,采用RecyclerView这个更优雅更强大的控件来实现轮播的功能,顺便复习下RecyclerView的相关知识。android
通常轮播图就两个重要的部分:能够无限左右滑动的图片流和图片位置的标示点,可能更简单的连指示点都省略了。主要的难点仍是在前者,由于一个轮播图要播放的图片通常也就十来张,不作任何处理直接塞到RecyclerView里面,不只稍微滑一下就没了并且开始还不能先往左边滑,因此咱们须要在设置Adapter的总数时设置成一个比较大的数(能够是Integer.MAX_VALUE
),而后在设置完图片数据后把RecyclerView的当前位置转到中间的一个数(为了保证从第一张开始播放,必须是图片总数的倍数,好比10000*size),这样item回收复用的时候,咱们只要取当前位置和图片数量的余数,获得真正的图片位置。听起来有点复杂,仍是直接看下代码吧:git
private class RecyclerAdapter extends RecyclerView.Adapter {
List<String> urlList;
public void setData(List<String> urlList) {
this.urlList = urlList;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RecyclerView.ViewHolder(new ImageView(getContext())) { };
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (urlList == null || urlList.isEmpty())
return;
String url = urlList.get(position % bannerSize);
Glide.with(getContext()).load(url).into((ImageView) holder.itemView);
}
@Override
public int getItemCount() {
//若是只有一张图片就不滑动了
return bannerSize < 2 ? 1 : Integer.MAX_VALUE;
}
}复制代码
至于自动滑动图片,就用Handler不断延迟发送消息就行了:github
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == WHAT_AUTO_PLAY) {
mRecyclerView.smoothScrollToPosition(++currentIndex);
refreshIndicator();
mHandler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration);
}
return false;
}
});复制代码
好了,无限轮播解决了,接下来就是标示点了,既然无限轮播图都用RecyclerView解决了,那么标示点也用它来解决吧:bash
private class IndicatorAdapter extends RecyclerView.Adapter {
int currentPosition = 0;
public void setPosition(int currentPosition) {
this.currentPosition = currentPosition;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RecyclerView.ViewHolder(new ImageView(getContext())) {
};
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ImageView bannerPoint = (ImageView) holder.itemView;
bannerPoint.setImageDrawable(currentPosition == position ? mSelectedDrawable : mUnselectedDrawable);
}
@Override
public int getItemCount() {
return bannerSize;
}
}复制代码
其实Adapter也很简单,设置一个当前位置的标识点,而后在图片改变的时候notifyDataSetChanged()
就好了。
好了最后就剩下怎么监听RecyclerView的位置改变了(可没有像Viewpager的addOnPageChangeListener那么直接的方法),没办法直接分析RecyclerView.OnScrollListener
中的回调方法吧:ide
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//解决连续滑动时指示器不更新的问题
if (bannerSize < 2) return;
int firstReal = mLinearLayoutManager.findFirstVisibleItemPosition();
View viewFirst = mLinearLayoutManager.findViewByPosition(firstReal);
float width = getWidth();
if (width != 0 && viewFirst != null) {
float right = viewFirst.getRight();
float ratio = right / width;
if (ratio > 0.8) {
if (currentIndex != firstReal) {
currentIndex = firstReal;
refreshIndicator();
}
} else if (ratio < 0.2) {
if (currentIndex != firstReal + 1) {
currentIndex = firstReal + 1;
refreshIndicator();
}
}
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
//连续滑动时可能不会回调
int first = mLinearLayoutManager.findFirstVisibleItemPosition();
int last = mLinearLayoutManager.findLastVisibleItemPosition();
if (currentIndex != first && first == last) {
currentIndex = first;
refreshIndicator();
}
}
});复制代码
好了解决了这些东西,再加些自定义View经常使用的属性,回调方法,设置的接口,轮播图就作好了,咱们来看看效果:
工具
嗯,看着还不错,但是怎么有点怪?唉,这图片怎么滑动的这么快,并且还能停在中间,这个不是咱们想要的`标准`轮播图。要解决这个问题就要用到RecyclerView的另外一个功能:SnapHelper。SnapHelper旨在支持RecyclerView的对齐方式,也就是经过计算对齐RecyclerView中TargetView 的指定点或者容器中的任何像素点。自定义一个SnapHelper挺麻烦的,还好android已经为咱们内置好了两个实现: LinearSnapHelper & PagerSnapHelper。其中PagerSnapHelper真是咱们须要的能够把RecyclerView改的像Viewpager的工具。new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
post
如今看着顺畅多了:一次只能滑动一张图片,中止的时候图片的位置也对了。这样一个基础版的轮播图就作成了。由于本质是一个RecyclerView,咱们能够RecyclerView.Itemanimator,来作出更多的动画效果(这个我目前就不太会了(T_T))。最后奉上github地址,里面有更完整代码,封装了不少自定义属性,欢迎star!动画