前言: 公司产品需要新增悬浮广告条的功能,要求是可以循环滚动,并且点击相应的浮条会跳转到相应的界面,在实现这个功能的时候遇到一些坑,幸运的是最后从这些坑中爬了出来。这篇文章的主要内容就是介绍功能的实现以及爬坑的经验。

效果展示

  在文章开始前,先看下最后实现的效果,最终的效果如下图

需求分析

  我们已经知道了产品的需求,下面要做的就是分析这个需求应该怎样实现,首先我们要实现的功能就是让广告条循环滚动,看最终的效果图可以发现,滚动的方向是由下往上滚动,平时我们见的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) {@Overrideprotected int getVerticalSnapPreference() {return LinearSmoothScroller.SNAP_TO_START;}@Overrideprotected 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>() {@Overridepublic 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设置成无限大,具体代码如下

@Overridepublic int getItemCount() {return Integer.MAX_VALUE;}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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);}}@Overridepublic void onBitmapFailed(Exception e, Drawable errorDrawable) {}@Overridepublic void onPrepareLoad(Drawable placeHolderDrawable) {}});}

滚动问题

  先看下滑动RecyclerView时出现的问题,如图

可以发现,虽然可以滑动RecyclerView,但是滑动过后,item会回退回来,然后继续上次的位置开始滚动。这个问题解决方法有两种

  1. 记住手动滑动到的item的未知,然后在interval方法中把滑动的位置设置为目标位置。
  2. 禁止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);}@Overridepublic 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() {@Overridepublic void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {super.onScrollStateChanged(recyclerView, newState);mState = newState;}};//添加RecyclerView的滑动监听addOnScrollListener(mScrollListener);}//判断是否拦截事件@Overridepublic boolean onInterceptTouchEvent(MotionEvent e) {return mState != 0;}@Overridepublic 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滑动的方法不熟悉的话,实现起来还是有点难度的,还有就是我们在编写代码的时候不仅要实现功能,还有注意对一些细节的处理,如果细节处理的不好,是很影响用户体验的。一些细节方面的问题是很考验技能的,当然对技能提升的帮助也是很大的。

  最后,当然是放出源码了,点击这里获取源码

本文已由公众号“AndroidShared”首发

实现无限轮播广告条如此简单相关推荐

  1. 利用RecyclerView实现无限轮播广告条

    代码地址如下: http://www.demodashi.com/demo/14771.html 前言: 公司产品需要新增悬浮广告条的功能,要求是可以循环滚动,并且点击相应的浮条会跳转到相应的界面,在 ...

  2. 自定义控件——轮播广告条

    1. 效果图 2. 布局文件 <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" ...

  3. Android实现广告页图片无限轮播

    一.概述 对于一个联网的Android应用, 首页广告无限轮播基本已经成为标配了. 那么它是怎么实现的呢? 有几种实现方式呢? 二.无限轮播的实现 1.最常规的手段是用 ViewPager来实现 2. ...

  4. ViewPager两种方式实现无限轮播

    给自己的忠告:虽然轮子很好用,但是使用轮子的前提是:如果不去封装一些复杂的功能,自己会用最基本的方法写一个,不然再好的轮子那也是别人的,当自己项目遇到和轮子不一样的地方,那就只能束手无策或者改人家的源 ...

  5. 引导界面滑动导航 + 大于等于1页时无限轮播 + 各种切换动画轮播效果

    参考地址: https://github.com/bingoogolapple/BGABanner-Android#%E6%95%88%E6%9E%9C%E5%9B%BE%E4%B8%8E%E7%A4 ...

  6. iOS无限轮播图片的两种方式

    2019独角兽企业重金招聘Python工程师标准>>> 1 使用UIScrollview实现无限轮播原理 在开发中常需要对广告或者是一些图片进行自动的轮播,也就是所谓的无限滚动. 在 ...

  7. 广告轮播java_[springboot 开发单体web shop] 6. 商品分类和轮播广告展示

    商品分类&轮播广告 因最近又被困在了OSGI技术POC,更新进度有点慢,希望大家不要怪罪哦. 上节 我们实现了登录之后前端的展示,如: 接着,我们来实现左侧分类栏目的功能. 商品分类|Prod ...

  8. 安卓实现广告栏图片无限轮播播放效果

    //经常在安卓app中页面上方放置一个广告栏,用到的无限轮播代码: public class MainActivity extends Activity { // 广告控件 private MyPag ...

  9. iOS开发之ImageView复用实现图片无限轮播

    在上篇博客中iOS开发之多图片无缝滚动组件封装与使用给出了图片无限轮播的实现方案之一,下面在给出另一种解决方案.今天博客中要说的就是在ScrollView上贴两个ImageView, 把ImageVi ...

最新文章

  1. 你应该知道的缓存进化史
  2. linux 百度地图离线sdk,Android开放百度地图集成
  3. js如何在当前页面加载springmvc返回的页面_手写SpringMVC学习
  4. 星露谷物语json_《星露谷物语》1.5版本更新细节公开 已上线PC
  5. fastclick源码简析
  6. 【报告分享】2020金融兴趣人群内容消费及理财意识洞察报告.pdf(附下载链接)...
  7. 1196971406
  8. 静态HTML网页作业模板 华为企业网站设计作品 静态学生网页设计作业简单网页制作
  9. 华为交换机CSS集群
  10. taobao API open淘宝 错误码一览表
  11. SPSS软件应用于因子分析/相关性分析等统计分析方法解读
  12. OSTU 最佳全局阈值处理-最大类间方差法
  13. DNA拷贝数变异CNV检测——基础概念篇
  14. android 软件调用c库,Android调用第三方C++算法库
  15. Excel中身份证号码验证,那些不得不说的事
  16. linux系统ubuntu简介
  17. 什么是 Power BI Desktop?
  18. 51单片机 c语言小数计算,51单片机怎么实现浮点运算
  19. c51语言 库函数,C51库函数-xuanying_china-ChinaUnix博客
  20. 用java编写一个微博登陆页面

热门文章

  1. Solidity 基础知识
  2. 校园疫情防控系统毕业设计,校园疫情防控管理系统设计与实现,校园疫情防控系统论文毕设作品参考
  3. 分布式事务(Seata) 四大模式详解
  4. UglifyJS和Uglifycss使用
  5. 牛客网 - [牛客OI周赛8-提高组]用水填坑(优先队列)
  6. 渣渣做后端真题-- 快手2020校园招聘秋招笔试--工程A试卷 选择题
  7. Proxmox VE 6.0管理指南——5.图形用户界面
  8. Erlang/OTP 构建 Application
  9. 将PDF转换为SWF文件
  10. matlab画图环形颜色渐变,Core Animation实战之环形颜色渐变进度条