代码地址如下:
http://www.demodashi.com/demo/14771.html

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

效果展示

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

需求分析

  我们已经知道了产品的需求,下面要做的就是分析这个需求应该怎样实现,首先我们要实现的功能就是让广告条循环滚动,看最终的效果图可以发现,滚动的方向是由下往上滚动,平时我们见的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会回退回来,然后继续上次的位置开始滚动。这个问题解决方法有两种

  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);
} @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实现无限轮播广告条

代码地址如下:
http://www.demodashi.com/demo/14771.html

利用RecyclerView实现无限轮播广告条的更多相关文章

  1. ViewPaper实现轮播广告条

    使用V4包中的viewPaper组件自己定义轮播广告条效果. 实现viewpaper的滑动切换和定时自己主动切换效果. 上效果图 布局文件 <RelativeLayout xmlns:andro ...

  2. iOS开发UI篇—无限轮播(循环利用)

    iOS开发UI篇—无限轮播(循环利用) 一.无限轮播  1.简单说明 在开发中常需要对广告或者是一些图片进行自动的轮播,也就是所谓的无限滚动. 在开发的时候,我们通常的做法是使用一个UIScrollV ...

  3. (Demo分享)利用原生JavaScript-ScrollLeft-实现做轮播广告通知

    轮播广告通知整体思路: 1.首先文字的移动利用了JAVA script 中ScrollLeft的知识点: 2.在设置一条一模一样的新闻,利用无缝轮播图滚动的原理让新闻无缝滚动. 3.使用了自执行匿名函 ...

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

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

  5. iOS开发UI篇—无限轮播(功能完善)

    iOS开发UI篇—无限轮播(功能完善) 一.自动滚动 添加并设置一个定时器,每个2.0秒,就跳转到下一条. 获取当前正在展示的位置. [self addNSTimer]; } -(void)addNS ...

  6. iOS开发UI篇—无限轮播(新闻数据展示)

    iOS开发UI篇—无限轮播(新闻数据展示) 一.实现效果        二.实现步骤 1.前期准备 (1)导入数据转模型的第三方框架MJExtension (2)向项目中添加保存有“新闻”数据的pli ...

  7. iOS开发UI篇—无限轮播(循环展示)

    iOS开发UI篇—无限轮播(循环展示) 一.简单说明 之前的程序还存在一个问题,那就是不能循环展示,因为plist文件中只有五个数组,因此第一个和最后一个之后就没有了,下面介绍处理这种循环展示问题的小 ...

  8. Swift应用案例 1.无限轮播

      从今天开始,我学习的重点开始转向Swift,并且会分享一些自己学习的心得体会,今天给大家带来的的是无限轮播.广告页的无限轮播是非常常见的一个功能,大多数APP都有,大多数程序员也都实现过,今天我们 ...

  9. ViewPager实现无限轮播踩坑记

    最近笔者想通过ViewPager来实现一个广告Banner,并实现无限轮播的效果,但是在这个过程中踩了不少的坑,听我慢慢道来.如果大家有遇到和我一样的情况,可以参考我的解决方法,没有那就更好,如果针对 ...

随机推荐

  1. DatabaseMirroring搭建

    1.    概述 数据库镜像维护一个数据库的两个副本,这两个副本必须驻留在不同的 SQL Server 数据库引擎 服务器实例上.通常,这些服务器实例驻留在不同位置的计算机上.启动数据库上的数据库镜像 ...

  2. SpringMVC request生命周期

    When the request leaves the browser, it carries information about what the user is asking for. At ve ...

  3. 【转】 Qt如何设置自动补全快捷键

    原文:https://blog.csdn.net/u014597198/article/details/52797435 在用Qt编程的时,它默认是以“CTRL+空格”来作为自动补全的快捷键的,但是这 ...

  4. Go语言之进阶篇爬捧腹网

    1.爬捧腹网 网页规律: https://www.pengfu.com/xiaohua_1.html   下一页 +1 https://www.pengfu.com/xiaohua_2.html 主页 ...

  5. listview加载数据

    首先我们需要理清思路:使用ListView显示数据是很方便的,ListVIew的数据之间通过适配器adapter去作为桥梁连接起来.当我们需要使用listview显示大量数据的时候,我们需要使用到分页 ...

  6. HDU 1234 (浙大计算机研究生复试上机考试-2005年) 开门人和关门人 (水)

    开门人和关门人 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  7. Android -- Dialog动画

    window_in.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android ...

  8. 条件随机场CRF HMM,MEMM的区别

    http://blog.sina.com.cn/s/blog_605f5b4f010109z3.html 首先,CRF,HMM(隐马模型),MEMM(最大熵隐马模型)都常用来做序列标注的建模,像词性标 ...

  9. Window配置Redis环境和简单使用

    一.关于Redis Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理.它支持字符串.哈希表.列表.集合.有序集合,位图,hyperloglogs等数据 ...

  10. Sublime Es6教程1-环境搭建

    因为现在网上的教程都不靠谱,于是决定自己跳坑自己写,分为三块来玩: 一.环境搭建 二.语法讲解 三.项目实战 很多时候,你想搞一个东西,却因为环境没有搭建好,而不能很开森的探索未知的世界,多年的编程经 ...