利用RecyclerView實現無限輪播廣告條

前言: 公司產品須要新增懸浮廣告條的功能,要求是能夠循環滾動,而且點擊相應的浮條會跳轉到相應的界面,在實現這個功能的時候遇到一些坑,幸運的是最後從這些坑中爬了出來。這篇文章的主要內容就是介紹功能的實現以及爬坑的經驗。html

效果展現

  在文章開始前,先看下最後實現的效果,最終的效果以下圖java

需求分析

  咱們已經知道了產品的需求,下面要作的就是分析這個需求應該怎樣實現,首先咱們要實現的功能就是讓廣告條循環滾動,看最終的效果圖能夠發現,滾動的方向是由下往上滾動,平時咱們見的banner圖都是左右滾動的,若是是左右滾動的就好辦了,能夠經過ViewPager來實現。可是這個上下滾動的應該怎麼實現呢?首先,想到的是利用ViewFlipper這個系統控件,可是這個控件只能知足循環滾動這個功能,咱們還有一個需求是點擊不一樣的浮條跳轉不一樣的內容呢!這個功能ViewFlipper就沒法知足了。既要循環滾動又要每一個浮條有相應的點擊事件,天然的就想到了RecyclerView,下面就利用RecyclerView來實現這些需求。android

功能實現

  RecyclerView的使用相信你們都會的,可是這裏有個問題是怎樣讓RecyclerView循環滾動?再把問題細分一下,首先就是怎樣讓RecyclerView本身滾動,而後是怎樣實現裏面的內容循環。git

動起來吧!RecyclerView

  怎樣讓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滾動,而且循環滾動。

實現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滑動的方法不熟悉的話,實現起來仍是有點難度的,還有就是咱們在編寫代碼的時候不只要實現功能,還有注意對一些細節的處理,若是細節處理的很差,是很影響用戶體驗的。一些細節方面的問題是很考驗技能的,固然對技能提高的幫助也是很大的。

  最後,固然是放出源碼了,點擊這裏獲取源碼

  轉載請註明出處:www.wizardev.cn

歡迎關注個人公衆號
掃碼關注公衆號,回覆「獲取資料」有驚喜
相關文章
相關標籤/搜索