老早就听说RecyclerView也是能用来做轮播图的了,然后又有点厌烦了ViewPager的实现轮播图实现方式。
就网上找了些文章看看,实际做了一下,发觉用起来还挺简单的。
布局中添加控件
<!--轮播图-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/banner_rv"
android:layout_width="match_parent"
android:layout_height="200dp"/>
布局上并没有什么不同,总之先放置一个RecyclerView吧。
设置横向的布局管理器
mBannerLM = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
banner_rv.setLayoutManager(mBannerLM);
轮播图通常是横向的,所以这里也是设置的横向的布局管理器。
绑定 LinearSnapHelper
mLinearSnapHelper = new LinearSnapHelper();
mLinearSnapHelper.attachToRecyclerView(banner_rv);
LinearSnapHelper是SnapHelper的一个官方实现类,作用是让RecyclerView在滚动时可以保持有一个item在中间位置,类似于轮播图的效果。
创建好之后,通过attachToRecyclerView()方法绑定上RecyclerView即可。
设置适配器
mBannerAdapter = new HomeBannerAdapter();
banner_rv.setAdapter(mBannerAdapter);
老样子,需要设置个适配器。
对滑动事件进行过滤
banner_rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
int firstVisibleItemPosition = manager.findFirstVisibleItemPosition();
switch (newState) {
case SCROLL_STATE_IDLE: //(静止没有滚动)
if(isSlidingByHand){
mCurrentPosition = firstVisibleItemPosition;
}
break;
case SCROLL_STATE_DRAGGING: //(正在被外部拖拽,一般为用户正在用手指滚动)
isSlidingByHand = true;
isSlidingAuto = false;
break;
case SCROLL_STATE_SETTLING: //(自动滚动)
isSlidingAuto = !isSlidingByHand;
break;
}
}
});
为了更好的用户体验,对RecyclerView的滑动事件做一下处理。
适配器中修改item数为最大值
public int getInitPosition(){
if(mList == null){
return 0;
}
int position = Integer.MAX_VALUE / 2;
int diff = position % mList.size();
return position - diff;
}
类似于ViewPager做轮播图无限轮播时的手法,当然也一样是个假的无限。
给适配器设置数据
HttpHelper.getPlayTabBanner(new HttpResponseRunOnUI() {
@Override
public void onErrorRunOnUI(String msg) {
showMoErrorToast("获取Banners时网络错误");
}
@Override
public void onSuccessRunOnUI(String json) {
Log.i("mo--", "json=" + json);
List<PlayTabBannerBean> bannerList = JsonUtil.parseList(json, PlayTabBannerBean.class);
if(bannerList.size() == 0){
// 没有轮播图
return;
}
// 设置轮播图
mBannerAdapter.setData(bannerList);
// 通知更新
mBannerAdapter.notifyDataSetChanged();
// 设置展示到中间
AppUtils.MoveToPosition(mBannerLM, banner_rv, mBannerAdapter.getInitPosition());
mCurrentPosition = mBannerAdapter.getInitPosition();
}
});
给适配器设置数据后记得要notifyDataSetChanged();。
跳到中间位置
/**
* RecyclerView 移动到当前位置,
*
* @param manager 设置RecyclerView对应的manager
* @param mRecyclerView 当前的RecyclerView
* @param n 要跳转的位置
*/
public static void MoveToPosition(LinearLayoutManager manager, RecyclerView mRecyclerView, int n) {
int firstItem = manager.findFirstVisibleItemPosition();
int lastItem = manager.findLastVisibleItemPosition();
if (n <= firstItem) {
mRecyclerView.scrollToPosition(n);
} else if (n <= lastItem) {
int top = mRecyclerView.getChildAt(n - firstItem).getTop();
mRecyclerView.scrollBy(0, top);
} else {
mRecyclerView.scrollToPosition(n);
}
}
由于item设置为了一个非常大的数值了,因此只需要将初始的坐标移动到总item数的中间就达到了无限轮播的假象了。
使用定时器开始轮播
mBannerInterval = Observable.interval(4, 4, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.map(aLong -> isVisible())
.subscribe(aLong -> {
if (mBannerAdapter != null && mBannerAdapter.getItemCount() != 0) {
LogUtil.i("Banner定时器进行中");
mCurrentPosition++;
banner_rv.smoothScrollToPosition(mCurrentPosition);
}
});
最近都喜欢用RxJava来弄定时器,RxJava2的写法与上面代码有差异了,使用java的定时器还是RxJava或者RxJava2来定时,就自行解决吧,能周期性得动起来就行。
处理轮播图点击
mBannerAdapter.setOnItemClickListener((parent, view, position, id) -> {
PlayTabBannerBean picRes = mBannerAdapter.getItem(position);
//showToast("点击了轮播图:" + position);
String type = picRes.getType();
if("web".equals(type)){
// 网页打开
if(getActivity() == null){
return;
}
AppUtils.openWebUrl(getActivity(), picRes.getClick());
}else if("key".equals(type)){
// 搜索关键字
intentPlaySearchResultActivity(picRes.getClick());
}
});
点击事件的处理方法就是RecyclerView的处理方法,毕竟本质还是RecyclerView嘛。
完成
由此就实现了一个项目经常用到的轮播图的功能了。相对于传统的ViewPager的实现方式,用RecyclerView来做的话,在增加、删除、变更条目排序这些需要上,是要更加灵活易用。因此对于轮播图这种需求,我是建议采用RecyclerView来完成的。
Comments | NOTHING