利用RecyclerView实现无限轮播广告条
前言: 公司产品需要新增悬浮广告条的功能,要求是可以循环滚动,并且点击相应的浮条会跳转到相应的界面,在实现这个功能的时候遇到一些坑,幸运的是最后从这些坑中爬了出来。这篇文章的主要内容就是介绍功能的实现以及爬坑的经验。
效果展示
在文章开始前,先看下最后实现的效果,最终的效果如下图
需求分析
我们已经知道了产品的需求,下面要做的就是分析这个需求应该怎样实现,首先我们要实现的功能就是让广告条循环滚动,看最终的效果图可以发现,滚动的方向是由下往上滚动,平时我们见的banner图都是左右滚动的,如果是左右滚动的就好办了,可以通过ViewPager来实现。但是这个上下滚动的应该怎么实现呢?首先,想到的是利用ViewFlipper这个系统控件,但是这个控件只能满足循环滚动这个功能,我们还有一个需求是点击不同的浮条跳转不同的内容呢!这个功能ViewFlipper就无法满足了。既要循环滚动又要每个浮条有相应的点击事件,自然的就想到了RecyclerView,下面就利用RecyclerView来实现这些需求。
功能实现
RecyclerView的使用相信大家都会的,但是这里有个问题是怎样让RecyclerView循环滚动?再把问题细分一下,首先就是怎样让RecyclerView自己滚动,然后是怎样实现里面的内容循环。
动起来吧!RecyclerView
怎样让RecyclerView自己滚动呢?通过查官方的Api,发现RecyclerView的LayoutManager中有这样一个方法
这个方法的说明是
使用提供的SmoothScroller开始平滑滚动。
好了,现在我们知道了这个方法的作用是让RecyclerView平滑滚动的,既然是让RecyclerView平滑滚动,那么我们肯定要告诉startSmoothScroll方法,RecyclerView怎样滚动,如,滚动的方向、距离、速度等。上面的方法说明也说了根据提供的SmoothScroller滚动,因此这里我们要实现SmoothScroller类来制定一些滚动的规则,查看源码可以发现SmoothScroller是抽象类,而官方文档中说它的已知的直接实现类是LinearSmoothScroller,所以这里直接实例化LinearSmoothScroller,重写相应的方法即可。具体代码如下
mSmoothScroller = new LinearSmoothScroller(this) {
@Override
protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 3f / (displayMetrics.density);
}
};
可以看到这里重写了两个方法。getVerticalSnapPreference这个方法是制定对齐的规则,就是RecyclerView里面的item顶部或底部与RecyclerView的对齐方式,这里有三种对齐方式,以下是官方文档中对这三种对齐方式的具体说明
再看下calculateSpeedPerPixel这个方法,这个方法是用来计算滚动的速度的,返回值是滚动一个像素花费的毫秒数。displayMetrics.density这个是1dp对应的像素密度,就是1dp等于多少像素。
注:SmoothScroller是将目标item滚动到RecyclerView中,即让目标item在RecyclerView中可见。
已经设置好滚动的规则了,下面要做的就是让RecyclerView中的item滚动,并且循环滚动。
实现RecyclerView的循环滚动
在实现循环滚动之前,看下实现滚动的代码,如下
private void startAuto() {
if (mAutoTask != null && !mAutoTask.isDisposed()) {
mAutoTask.dispose();
}
mAutoTask = Observable.interval(1, 2, TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) {
mSmoothScroller.setTargetPosition(aLong.intValue());
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (layoutManager!=null)
layoutManager.startSmoothScroll(mSmoothScroller);
}
});
}
从这段代码中可以看到,用RxJava中的interval方法实现了一个循环数字递增定时器,时间间隔是2s。mSmoothScroller.setTargetPosition(aLong.intValue());这句代码就是设置哪个item出现在RecyclerView中。layoutManager.startSmoothScroll(mSmoothScroller);这句代码实际上调用的就是LinearSmoothScroller类中的start方法,这段代码实现的功能就是每隔两秒,就让设置的目标item平滑滚动到RecyclerView中。
现在已经开始滚动了,那么怎么让目标item重复出现呢?其实这很简单,就是将itemCount设置成无限大,具体代码如下
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
@Override
public void onBindViewHolder(@NonNull final AdViewHolder holder, int position) {
if (mDynamicAdsDetails.size() != 0) {
String media1 = mDynamicAdsDetails.get(position % mDynamicAdsDetails.size());
Picasso.get()
.load(media1)
.error(R.mipmap.ic_launcher)
.placeholder(R.mipmap.ic_launcher)
.into( holder.ivFlipperItem);
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//控制点击频率
if ((System.currentTimeMillis() - between) / 1000 < 1) {
return;
}
between = System.currentTimeMillis();
Toast.makeText(mContext,"点击了第"+holder.getAdapterPosition() % mDynamicAdsDetails.size()+"个",Toast.LENGTH_SHORT).show();
}
});
}
注:将itemCount设置成无限大后,取列表中的值时,不能直接根据相应的position来取值了,应该这样取mDynamicAdsDetails.get(position % mDynamicAdsDetails.size())。
这样就能实现循环滚动了。
开始爬坑
图片错乱问题
这样实现看起来显然没问题,但是项目跑起来后,却发现图片竟然不是循环显示的,而是偶尔会一张图片出现多次,然后才是下一张图片。分析了一下原因,认为是RecyclerView的复用问题,图片异步请求的结果还没有返回回来,复用了上次的控件,所以就出现一个图片显示多次的问题了。解决方法就是给ImageView设置Tag,具体代码如下
if (mDynamicAdsDetails.size() != 0) {
String media1 = mDynamicAdsDetails.get(position % mDynamicAdsDetails.size());
holder.ivFlipperItem.setTag(media1);
Picasso.get()
.load(media1)
.error(R.mipmap.ic_launcher)
.placeholder(R.mipmap.ic_launcher)
.into(new com.squareup.picasso.Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
if (mDynamicAdsDetails.get(holder.getAdapterPosition() % mDynamicAdsDetails.size()).equals(holder.ivFlipperItem.getTag())) {
holder.ivFlipperItem.setScaleType(ImageView.ScaleType.FIT_XY);
holder.ivFlipperItem.setImageBitmap(bitmap);
}
}
@Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
滚动问题
先看下滑动RecyclerView时出现的问题,如图
可以发现,虽然可以滑动RecyclerView,但是滑动过后,item会回退回来,然后继续上次的位置开始滚动。这个问题解决方法有两种
- 记住手动滑动到的item的未知,然后在
interval方法中把滑动的位置设置为目标位置。 - 禁止RecyclerView的滑动。
因为需求没有可以滑动的这个功能,所以这里采用方法2,禁止RecyclerView的滑动,详细代码如下
public class AutoScrollRecyclerView extends RecyclerView {
private int mState;
private OnScrollListener mScrollListener;
public AutoScrollRecyclerView(Context context) {
this(context,null);
}
public AutoScrollRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public AutoScrollRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
return false;
case MotionEvent.ACTION_POINTER_UP:
return false;
}
return true;
}
}
这里重写了RecyclerVieiew的onTouchEvent方法,当滑动式返回false,不消费滑动的动作。
但是,这么做之后会有新的问题,就是当图片在滚动时,我们点击图片,图片会暂停住,这里采用的解决方法是监听RecyclerView 的滚动状态,只有当RecyclerView滑动停止时,才不拦截事件,否则就拦截事件。具体代码如下
public class AutoScrollRecyclerView extends RecyclerView {
private int mState;
private OnScrollListener mScrollListener;
public AutoScrollRecyclerView(Context context) {
this(context,null);
}
public AutoScrollRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public AutoScrollRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
mState = newState;
}
};
//添加RecyclerView的滑动监听
addOnScrollListener(mScrollListener);
}
//判断是否拦截事件
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
return mState != 0;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
return mState == 0;
case MotionEvent.ACTION_MOVE:
return false;
case MotionEvent.ACTION_POINTER_UP:
return false;
}
return true;
}
}
好了,这样就解决了滑动RecyclerView出现的问题。
自定义广告样式
广告的样式是可以自己定义的,不仅仅是图片,还可以实现图文混排等,只需要修改layout文件即可,这里为了方便就直接在layout中放了一张图片。
项目结构
文章中的项目结构如下图所示
结束语
实现的功能挺简单的,但是如果对RecyclerView滑动的方法不熟悉的话,实现起来还是有点难度的,还有就是我们在编写代码的时候不仅要实现功能,还有注意对一些细节的处理,如果细节处理的不好,是很影响用户体验的。一些细节方面的问题是很考验技能的,当然对技能提升的帮助也是很大的。利用RecyclerView实现无限轮播广告条
利用RecyclerView实现无限轮播广告条的更多相关文章
- ViewPaper实现轮播广告条
使用V4包中的viewPaper组件自己定义轮播广告条效果. 实现viewpaper的滑动切换和定时自己主动切换效果. 上效果图 布局文件 <RelativeLayout xmlns:andro ...
- iOS开发UI篇—无限轮播(循环利用)
iOS开发UI篇—无限轮播(循环利用) 一.无限轮播 1.简单说明 在开发中常需要对广告或者是一些图片进行自动的轮播,也就是所谓的无限滚动. 在开发的时候,我们通常的做法是使用一个UIScrollV ...
- (Demo分享)利用原生JavaScript-ScrollLeft-实现做轮播广告通知
轮播广告通知整体思路: 1.首先文字的移动利用了JAVA script 中ScrollLeft的知识点: 2.在设置一条一模一样的新闻,利用无缝轮播图滚动的原理让新闻无缝滚动. 3.使用了自执行匿名函 ...
- Android实现广告页图片无限轮播
一.概述 对于一个联网的Android应用, 首页广告无限轮播基本已经成为标配了. 那么它是怎么实现的呢? 有几种实现方式呢? 二.无限轮播的实现 1.最常规的手段是用 ViewPager来实现 2. ...
- iOS开发UI篇—无限轮播(功能完善)
iOS开发UI篇—无限轮播(功能完善) 一.自动滚动 添加并设置一个定时器,每个2.0秒,就跳转到下一条. 获取当前正在展示的位置. [self addNSTimer]; } -(void)addNS ...
- iOS开发UI篇—无限轮播(新闻数据展示)
iOS开发UI篇—无限轮播(新闻数据展示) 一.实现效果 二.实现步骤 1.前期准备 (1)导入数据转模型的第三方框架MJExtension (2)向项目中添加保存有“新闻”数据的pli ...
- iOS开发UI篇—无限轮播(循环展示)
iOS开发UI篇—无限轮播(循环展示) 一.简单说明 之前的程序还存在一个问题,那就是不能循环展示,因为plist文件中只有五个数组,因此第一个和最后一个之后就没有了,下面介绍处理这种循环展示问题的小 ...
- Swift应用案例 1.无限轮播
从今天开始,我学习的重点开始转向Swift,并且会分享一些自己学习的心得体会,今天给大家带来的的是无限轮播.广告页的无限轮播是非常常见的一个功能,大多数APP都有,大多数程序员也都实现过,今天我们 ...
- ViewPager实现无限轮播踩坑记
最近笔者想通过ViewPager来实现一个广告Banner,并实现无限轮播的效果,但是在这个过程中踩了不少的坑,听我慢慢道来.如果大家有遇到和我一样的情况,可以参考我的解决方法,没有那就更好,如果针对 ...
随机推荐
- 【手机网络游戏 编程】C#异步socketAPI调用 处理数据的流程
之前客户端在网络条件好的时候,运行没问题.但是有时候手机的网络不稳定,接受数据可能不稳定,导致接受数据错误,一直都不知道,原来是接受数据处理的不够好! 现在更改过后的接受数据的逻辑如下: //接收 p ...
- cannot ignore cache if it is not cached [ArcGIS Catalog 10]
由于我把缓存重命名,重启地图服务,再Caching中不再显示地图缓存了,我直接创建新的地图缓存,没想到出现了: 不知道怎么回事. 只好,把缓存 重新改名成原来的名字,然后,删除缓存,再建立缓存.
- Spring(AbstractRoutingDataSource)实现动态数据源切换
转自: http://blog.51cto.com/linhongyu/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直 ...
- [leetcode]Multiply Strings @ Python
原题地址:https://oj.leetcode.com/problems/multiply-strings/ 题意: Given two numbers represented as strings ...
- 配置nginx到后端服务器负载均衡
nginx和haproxy一样也可以做前端请求分发实现负载均衡效果,比如一个tomcat服务如果并发过高会导致处理很慢,新来的请求就会排队,到一定程度时请求就可能会返回错误或者拒绝服务,所以通过负载均 ...
- fastText、TextCNN、TextRNN……这里有一套NLP文本分类深度学习方法库供你选择
https://mp.weixin.qq.com/s/_xILvfEMx3URcB-5C8vfTw 这个库的目的是探索用深度学习进行NLP文本分类的方法. 它具有文本分类的各种基准模型,还支持多标签分 ...
- FPS游戏服务器设计的问题 【转】
一.追溯 去gameloft笔试,有一个题目是说: 叫你去设计一个FPS(第一人称射击游戏),你是要用TCP呢还是要用UDP,说明理由 . 二.学习 这是两篇网上找到的文章,写非常不错. 当时笔试的时 ...
- 使用 HTML5, javascript, webrtc, websockets, Jetty 和 OpenCV 实现基于 Web 的人脸识别
这是一篇国外的文章,介绍如何通过 WebRTC.OpenCV 和 WebSocket 技术实现在 Web 浏览器上的人脸识别,架构在 Jetty 之上. 实现的效果包括: 还能识别眼睛 人脸识别的核心 ...
- Android 事件模型
本文内容 基于监听的事件模型 基于回调的事件模型 Android 支持两种事件模型,基于监听的事件模型和基于回调的事件模型. 基于监听的事件模型 基于监听的事件模型是一种委托式的,更"面向对 ...
- logback-kafka-appender
logback 日志写入kafka队列 logback-kafka-appender Logback incompatibility Warning Due to a bug in logback-c ...