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


代碼地址如下:
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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM