前言: 公司產品須要新增懸浮廣告條的功能,要求是能夠循環滾動,而且點擊相應的浮條會跳轉到相應的界面,在實現這個功能的時候遇到一些坑,幸運的是最後從這些坑中爬了出來。這篇文章的主要內容就是介紹功能的實現以及爬坑的經驗。html
在文章開始前,先看下最後實現的效果,最終的效果以下圖java
咱們已經知道了產品的需求,下面要作的就是分析這個需求應該怎樣實現,首先咱們要實現的功能就是讓廣告條循環滾動,看最終的效果圖能夠發現,滾動的方向是由下往上滾動,平時咱們見的banner圖都是左右滾動的,若是是左右滾動的就好辦了,能夠經過ViewPager
來實現。可是這個上下滾動的應該怎麼實現呢?首先,想到的是利用ViewFlipper
這個系統控件,可是這個控件只能知足循環滾動這個功能,咱們還有一個需求是點擊不一樣的浮條跳轉不一樣的內容呢!這個功能ViewFlipper
就沒法知足了。既要循環滾動又要每一個浮條有相應的點擊事件,天然的就想到了RecyclerView
,下面就利用RecyclerView來實現這些需求。android
RecyclerView的使用相信你們都會的,可是這裏有個問題是怎樣讓RecyclerView循環滾動?再把問題細分一下,首先就是怎樣讓RecyclerView本身滾動,而後是怎樣實現裏面的內容循環。git
怎樣讓RecyclerView本身滾動呢?經過查官方的Api,發現RecyclerView的LayoutManager中有這樣一個方法github
這個方法的說明是異步
使用提供的SmoothScroller開始平滑滾動。ide
好了,如今咱們知道了這個方法的做用是讓RecyclerView平滑滾動的,既然是讓RecyclerView平滑滾動,那麼咱們確定要告訴startSmoothScroll
方法,RecyclerView怎樣滾動,如,滾動的方向、距離、速度等。上面的方法說明也說了根據提供的SmoothScroller
滾動,所以這裏咱們要實現SmoothScroller
類來制定一些滾動的規則,查看源碼能夠發現SmoothScroller
是抽象類,而官方文檔中說它的已知的直接實現類是LinearSmoothScroller,因此這裏直接實例化LinearSmoothScroller
,重寫相應的方法便可。具體代碼以下ui
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的對齊方式,這裏有三種對齊方式,如下是官方文檔中對這三種對齊方式的具體說明this
再看下calculateSpeedPerPixel
這個方法,這個方法是用來計算滾動的速度的,返回值是滾動一個像素花費的毫秒數。displayMetrics.density
這個是1dp對應的像素密度,就是1dp等於多少像素。google
注:SmoothScroller
是將目標item滾動到RecyclerView中,即讓目標item在RecyclerView中可見。
已經設置好滾動的規則了,下面要作的就是讓RecyclerView中的item滾動,而且循環滾動。
在實現循環滾動以前,看下實現滾動的代碼,以下
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會回退回來,而後繼續上次的位置開始滾動。這個問題解決方法有兩種
interval
方法中把滑動的位置設置爲目標位置。由於需求沒有能夠滑動的這個功能,因此這裏採用方法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滑動的方法不熟悉的話,實現起來仍是有點難度的,還有就是咱們在編寫代碼的時候不只要實現功能,還有注意對一些細節的處理,若是細節處理的很差,是很影響用戶體驗的。一些細節方面的問題是很考驗技能的,固然對技能提高的幫助也是很大的。
最後,固然是放出源碼了,點擊這裏獲取源碼
轉載請註明出處:www.wizardev.cn