用RecyclerView打造一個輪播圖

一般Android的輪播圖(俗名:Banner)都是用ViewPager實現的,可是我在實際項目運用中碰到了一些小問題,因而決定另尋思路,採用RecyclerView這個更優雅更強大的控件來實現輪播的功能,順便複習下RecyclerView的相關知識。android

實現

通常輪播圖就兩個重要的部分:能夠無限左右滑動的圖片流和圖片位置的標示點,可能更簡單的連指示點都省略了。主要的難點仍是在前者,由於一個輪播圖要播放的圖片通常也就十來張,不作任何處理直接塞到RecyclerView裏面,不只稍微滑一下就沒了並且開始還不能先往左邊滑,因此咱們須要在設置Adapter的總數時設置成一個比較大的數(能夠是Integer.MAX_VALUE),而後在設置完圖片數據後把RecyclerView的當前位置轉到中間的一個數(爲了保證從第一張開始播放,必須是圖片總數的倍數,好比10000*size),這樣item回收複用的時候,咱們只要取當前位置和圖片數量的餘數,獲得真正的圖片位置。聽起來有點複雜,仍是直接看下代碼吧:git

private class RecyclerAdapter extends RecyclerView.Adapter {
        List<String> urlList;

        public void setData(List<String> urlList) {
            this.urlList = urlList;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new RecyclerView.ViewHolder(new ImageView(getContext())) { };
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            if (urlList == null || urlList.isEmpty())
                return;
            String url = urlList.get(position % bannerSize);
            Glide.with(getContext()).load(url).into((ImageView) holder.itemView);
        }
        @Override
        public int getItemCount() {
          //若是隻有一張圖片就不滑動了
            return bannerSize < 2 ? 1 : Integer.MAX_VALUE;
        }
    }複製代碼

至於自動滑動圖片,就用Handler不斷延遲發送消息就行了:github

private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == WHAT_AUTO_PLAY) {
                    mRecyclerView.smoothScrollToPosition(++currentIndex);
                 refreshIndicator();
                    mHandler.sendEmptyMessageDelayed(WHAT_AUTO_PLAY, autoPlayDuration);

            }
            return false;
        }
    });複製代碼

好了,無限輪播解決了,接下來就是標示點了,既然無限輪播圖都用RecyclerView解決了,那麼標示點也用它來解決吧:bash

private class IndicatorAdapter extends RecyclerView.Adapter {

        int currentPosition = 0;

        public void setPosition(int currentPosition) {
            this.currentPosition = currentPosition;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new RecyclerView.ViewHolder(new ImageView(getContext())) {
            };
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            ImageView bannerPoint = (ImageView) holder.itemView;
            bannerPoint.setImageDrawable(currentPosition == position ? mSelectedDrawable : mUnselectedDrawable);

        }

        @Override
        public int getItemCount() {
            return bannerSize;
        }
    }複製代碼

其實Adapter也很簡單,設置一個當前位置的標識點,而後在圖片改變的時候notifyDataSetChanged()就好了。
好了最後就剩下怎麼監聽RecyclerView的位置改變了(可沒有像Viewpager的addOnPageChangeListener那麼直接的方法),沒辦法直接分析RecyclerView.OnScrollListener中的回調方法吧:ide

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                //解決連續滑動時指示器不更新的問題
                if (bannerSize < 2) return;
                int firstReal = mLinearLayoutManager.findFirstVisibleItemPosition();
                View viewFirst = mLinearLayoutManager.findViewByPosition(firstReal);
                float width = getWidth();
                if (width != 0 && viewFirst != null) {
                    float right = viewFirst.getRight();
                    float ratio = right / width;
                    if (ratio > 0.8) {
                        if (currentIndex != firstReal) {
                            currentIndex = firstReal;
                            refreshIndicator();
                        }
                    } else if (ratio < 0.2) {
                        if (currentIndex != firstReal + 1) {
                            currentIndex = firstReal + 1;
                            refreshIndicator();
                        }
                    }
                }
            }

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            //連續滑動時可能不會回調
                int first = mLinearLayoutManager.findFirstVisibleItemPosition();
                int last = mLinearLayoutManager.findLastVisibleItemPosition();
                if (currentIndex != first && first == last) {
                    currentIndex = first;
                    refreshIndicator();
                }
            }
        });複製代碼

看當作果

好了解決了這些東西,再加些自定義View經常使用的屬性,回調方法,設置的接口,輪播圖就作好了,咱們來看看效果:
工具

gif.gif
gif.gif

嗯,看着還不錯,但是怎麼有點怪?唉,這圖片怎麼滑動的這麼快,並且還能停在中間,這個不是咱們想要的`標準`輪播圖。要解決這個問題就要用到RecyclerView的另外一個功能:SnapHelper。SnapHelper旨在支持RecyclerView的對齊方式,也就是經過計算對齊RecyclerView中TargetView 的指定點或者容器中的任何像素點。自定義一個SnapHelper挺麻煩的,還好android已經爲咱們內置好了兩個實現: LinearSnapHelper & PagerSnapHelper。其中PagerSnapHelper真是咱們須要的能夠把RecyclerView改的像Viewpager的工具。
new PagerSnapHelper().attachToRecyclerView(mRecyclerView);post

gif.gif
gif.gif

如今看着順暢多了:一次只能滑動一張圖片,中止的時候圖片的位置也對了。這樣一個基礎版的輪播圖就作成了。由於本質是一個RecyclerView,咱們能夠RecyclerView.Itemanimator,來作出更多的動畫效果(這個我目前就不太會了(T_T))。最後奉上github地址,裏面有更完整代碼,封裝了不少自定義屬性,歡迎star!動畫

PS:進階版請戳:

用RecyclerView打造一個輪播圖(進階版)ui

相關文章
相關標籤/搜索