自定義瀑布流LayoutManager


瀑布流系統(tǒng)是本身自帶的宇立,但在實際使用中會有諸多問題

比如:在底部刷新了之后,回到頂部步势,就參差不齊了氧猬,空白 item交換等。

造輪子是為了解決這個問題坏瘩,提供實際問題的解決思路盅抚。相比系統(tǒng)自帶完善的layoutManger 還差太遠太遠

效果

測試項目github地址

2017-11-29-10-16-12.gif

實現(xiàn)

  • 自定義一個LayoutManager主要重寫以下方法
  1. generateDefaultLayoutParams()
  2. onLayoutChildren
  3. scrollVerticallyBy canScrollVertically() 當然水平方向也有
  • 核心是onLayoutChildren scrollVerticallyBy

  @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(eachWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (state.getItemCount() == 0) {
            detachAndScrapAttachedViews(recycler);
        }
        if (getChildCount() == 0 && state.isPreLayout()) {
            return;
        }

        if (helper == null) {
            helper = OrientationHelper.createHorizontalHelper(this);
            helper2 = OrientationHelper.createVerticalHelper(this);
        }

        detachAndScrapAttachedViews(recycler);


        /**
         * 預(yù)計算位置
         */
        init(recycler, state);

        layout(recycler, state, 0);

    }

    private void init(final RecyclerView.Recycler recycler, RecyclerView.State state) {
        offsets = new int[count];
        attchedViews.clear();
        eachWidth = helper.getTotalSpace() / count;
    }

    private void caculate(final RecyclerView.Recycler recycler, int dy) {
        long start = System.currentTimeMillis();
        A:
        for (int i = layouts.size(); i < getItemCount(); i++) {
            /**
             * 之測量不同type的大小 計算位置
             */
            View scrap = recycler.getViewForPosition(i);
            addView(scrap);
            measureChildWithMargins(scrap, eachWidth, 0);
            int decoratedMeasuredHeight = getDecoratedMeasuredHeight(scrap);
            removeAndRecycleView(scrap, recycler);
            int rowNumber = getMinIndex();
            Rect rect = layouts.get(i);
            rect.set(rowNumber * eachWidth, offsets[rowNumber], (rowNumber + 1) * eachWidth, offsets[rowNumber] + decoratedMeasuredHeight);
            offsets[rowNumber] = offsets[rowNumber] + rect.height();
            /**
             * 只多加載一屏幕的
             */
            if (offsets[getMinIndex()] > dy + scrolls + getPaddingTop() + helper2.getTotalSpace()) {
                break A;
            }

        }
        maxHeight = getMaxHeight();
    }


    /**
     * 獲取最小的指針位置
     *
     * @return
     */
    private int getMinIndex() {
        int min = 0;
        int minnum = offsets[0];
        for (int i = 1; i < offsets.length; i++) {
            if (minnum > offsets[i]) {
                minnum = offsets[i];
                min = i;
            }
        }
        return min;
    }

    /**
     * 獲取最大的高度
     *
     * @return
     */
    private int getMaxHeight() {
        int max = offsets[0];
        for (int i = 1; i < offsets.length; i++) {
            if (offsets[i] > max) {
                max = offsets[i];
            }
        }
        return max;
    }

    public Rect getRect(RecyclerView.Recycler recycler, int position) {
        Rect rectx = layouts.get(position);
        return rectx;
    }


    /**
     * dy 1 上滑 -1 下滑 0出初始
     *
     * @param recycler
     * @param state
     */
    private void layout(RecyclerView.Recycler recycler, RecyclerView.State state, int dy) {
        Rect layoutRange = new Rect(getPaddingLeft(), getPaddingTop() + scrolls, helper.getTotalSpace() + getPaddingLeft(), helper2.getTotalSpace() + getPaddingTop() + scrolls);
        int itemCount = state.getItemCount();
        if (dy >= 0) {
            dolayoutAndRecycler(recycler, layoutRange, itemCount);
        } else {
            dolayoutAndRecyclerDown(recycler, layoutRange, itemCount);
        }


    }

    private void recyclerViews(RecyclerView.Recycler recycler,Rect layoutrect) {
//        detachAndScrapAttachedViews(recycler);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            int position = getPosition(childAt);
            Rect rect = layouts.get(position);
            if(!Rect.intersects(rect,layoutrect)){
                attchedViews.remove(position);
                removeAndRecycleView(childAt,recycler);
                childCount--;
            }
        }
    }


    private void dolayoutAndRecyclerDown(RecyclerView.Recycler recycler, Rect layoutRange, int itemCount) {
        int childCount = getChildCount();
        int max = getMax(childCount);
        recyclerViews(recycler,layoutRange);
        int xx = 0;
        for (int i = max; i >= 0; i--) {
            Rect layout = getRect(recycler, i);
            if (Rect.intersects(layout, layoutRange)&&attchedViews.get(i)==null) {
                View viewForPosition = recycler.getViewForPosition(i);
                addView(viewForPosition);
                attchedViews.put(i,viewForPosition);
                measureChildWithMargins(viewForPosition, eachWidth, 0);
                layoutDecoratedWithMargins(viewForPosition, layout.left, layout.top - scrolls, layout.right, layout.bottom - scrolls);
            }
            if (layout.bottom <= layoutRange.top) {
                xx++;
                if (xx >= count) {
                    break;
                }
            }
        }


    }

    private int getMax(int childCount) {
        int max = 0;
        if (childCount != 0) {
            max = getPosition(getChildAt(0));
            for (int i = 1; i < childCount; i++) {
                int position = getPosition(getChildAt(i));
                if (position > max) {
                    max = position;
                }
            }
        }
        return max;
    }

    /**
     * 出初始layout
     *
     * @param recycler
     * @param layoutRange
     * @param itemCount
     */
    private void dolayoutAndRecycler(RecyclerView.Recycler recycler, Rect layoutRange, int itemCount) {
        int childCount = getChildCount();
        int min = getMin(childCount);
        recyclerViews(recycler,layoutRange);
        int xx = 0;
        A:
        for (int i = min; i < itemCount; i++) {

            if (getRect(recycler, i).isEmpty()) {
                layouts.getArray().remove(i);
                caculate(recycler, 0);
            }

            final Rect layout = getRect(recycler, i);
            if (Rect.intersects(layout, layoutRange)&&attchedViews.get(i)==null) {
                View viewForPosition = recycler.getViewForPosition(i);
                addView(viewForPosition);
                attchedViews.put(i,viewForPosition);
                measureChildWithMargins(viewForPosition, eachWidth, 0);
                layoutDecoratedWithMargins(viewForPosition, layout.left, layout.top - scrolls, layout.right, layout.bottom - scrolls);
            }
            if (layout.top >= layoutRange.bottom) {
                xx++;
                if (xx >= count) {
                    break;
                }
            }
        }
    }


    private int getMin(int childCount) {
        int min = 0;
        if (childCount != 0) {
            min = getPosition(getChildAt(0));
            for (int i = 1; i < childCount; i++) {
                int position = getPosition(getChildAt(i));
                if (position < min) {
                    min = position;
                }
            }
        }
        return min;
    }


    @Override
    public boolean canScrollVertically() {
        return true;
    }

    @Override
    public boolean canScrollHorizontally() {
        //返回true表示可以橫向滑動
        return false;
    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        System.out.println(getChildCount() + "------------");
        if (layouts.size() < getItemCount() && maxHeight <= dy + scrolls + getPaddingTop() + helper2.getTotalSpace()) {
            caculate(recycler, dy);
        }

        if (maxHeight < helper2.getTotalSpace()) {
            return 0;
        }
        if (scrolls + dy > maxHeight - helper2.getTotalSpace()) {
            dy = maxHeight - helper2.getTotalSpace() - scrolls;
        }

        if (scrolls + dy < 0) {
            dy = -scrolls;
        }
        offsetChildrenVertical(-dy);
        scrolls += dy;
        if (dy > 0) {
            layout(recycler, state, 1);
        } else if (dy < 0) {
            layout(recycler, state, -1);
        }
        return dy;
    }

    @Override
    public void scrollToPosition(int position) {
        int temp = position;
        if (position > getItemCount()) {
            temp = getItemCount();
        } else if (position < 0) {
            temp = 0;
        }
        int top = layouts.get(temp).top;
        if (top > maxHeight - helper2.getTotalSpace()) {
            top = maxHeight - helper2.getTotalSpace();
        }
        scrolls = top;
        requestLayout();
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {//平滑的移動到某一項
        int top = layouts.get(position).top;
        int needscroll = top - scrolls;
        recyclerView.smoothScrollBy(0, needscroll);
    }

    private static class Pool<T> {
        SparseArray<T> array;
        Factory<T> tnew;

        public Pool(Factory<T> tnew) {
            array = new SparseArray<>();
            this.tnew = tnew;
        }

        public int size() {
            return array.size();
        }

        public SparseArray<T> getArray() {
            return array;
        }

        public T get(int key) {
            T t = array.get(key);
            if (t == null) {
                t = tnew.get();
                array.put(key, t);
            }
            return t;
        }

        public interface Factory<T> {
            T get();
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市倔矾,隨后出現(xiàn)的幾起案子妄均,更是在濱河造成了極大的恐慌,老刑警劉巖哪自,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丰包,死亡現(xiàn)場離奇詭異,居然都是意外死亡壤巷,警方通過查閱死者的電腦和手機邑彪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胧华,“玉大人锌蓄,你說我怎么就攤上這事升筏。” “怎么了瘸爽?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵您访,是天一觀的道長。 經(jīng)常有香客問我剪决,道長灵汪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任柑潦,我火速辦了婚禮享言,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渗鬼。我一直安慰自己览露,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布譬胎。 她就那樣靜靜地躺著差牛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪堰乔。 梳的紋絲不亂的頭發(fā)上偏化,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音镐侯,去河邊找鬼侦讨。 笑死,一個胖子當著我的面吹牛苟翻,可吹牛的內(nèi)容都是我干的韵卤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼崇猫,長吁一口氣:“原來是場噩夢啊……” “哼沈条!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起邓尤,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤拍鲤,失蹤者是張志新(化名)和其女友劉穎贴谎,沒想到半個月后汞扎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡擅这,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年澈魄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仲翎。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡痹扇,死狀恐怖铛漓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鲫构,我是刑警寧澤浓恶,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站结笨,受9級特大地震影響包晰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜炕吸,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一伐憾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赫模,春花似錦树肃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至廓脆,卻和暖如春筛谚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背停忿。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工驾讲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人席赂。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓吮铭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親颅停。 傳聞我的和親對象是個殘疾皇子谓晌,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容