前言: 公司产品需要新增悬浮广告条的功能,要求是可以循环滚动,并且点击相应的浮条会跳转到相应的界面,在实现这个功能的时候遇到一些坑,幸运的是最后从这些坑中爬了出来。这篇文章的主要内容就是介绍功能的实现以及爬坑的经验。
效果展示
在文章开始前,先看下最后实现的效果,最终的效果如下图
需求分析
我们已经知道了产品的需求,下面要做的就是分析这个需求应该怎样实现,首先我们要实现的功能就是让广告条循环滚动,看最终的效果图可以发现,滚动的方向是由下往上滚动,平时我们见的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())
。
这样就能实现循环滚动了。