可以滑动流式布局

网上很多流式布局不支持滑动,由于我们的需求要求多的时候必须能滑动,于是我就想到了recyclerview,改造他们manager

public class FlowLayoutManagerextends RecyclerView.LayoutManager {

private static final StringTAG = FlowLayoutManager.class.getSimpleName();

    final FlowLayoutManagerself =this;

    protected int width, height;

    private int left, top, right;

    //最大容器的宽度

    private int usedMaxWidth;

    //竖直方向上的偏移量

    private int verticalScrollOffset =0;

    public int getTotalHeight() {

return totalHeight;

    }

//计算显示的内容的高度

    protected int totalHeight =0;

    private Rowrow =new Row();

    private ListlineRows =new ArrayList<>();

    //保存所有的Item的上下左右的偏移量信息

    private SparseArrayallItemFrames =new SparseArray<>();

    public FlowLayoutManager() {

//设置主动测量规则,适应recyclerView高度为wrap_content

        setAutoMeasureEnabled(true);

    }

//每个item的定义

    public class Item {

int useHeight;

        Viewview;

        public void setRect(Rect rect) {

this.rect = rect;

        }

Rectrect;

        public Item(int useHeight, View view, Rect rect) {

this.useHeight = useHeight;

            this.view = view;

            this.rect = rect;

        }

}

//行信息的定义

    public class Row {

public void setCuTop(float cuTop) {

this.cuTop = cuTop;

        }

public void setMaxHeight(float maxHeight) {

this.maxHeight = maxHeight;

        }

//每一行的头部坐标

        float cuTop;

        //每一行需要占据的最大高度

        float maxHeight;

        //每一行存储的item

        Listviews =new ArrayList<>();

        public void addViews(Item view) {

views.add(view);

        }

}

@Override

    public RecyclerView.LayoutParamsgenerateDefaultLayoutParams() {

return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    }

//该方法主要用来获取每一个item在屏幕上占据的位置

    @Override

    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

Log.d(TAG, "onLayoutChildren");

        totalHeight =0;

        int cuLineTop =top;

        //当前行使用的宽度

        int cuLineWidth =0;

        int itemLeft;

        int itemTop;

        int maxHeightItem =0;

        row =new Row();

        lineRows.clear();

        allItemFrames.clear();

        removeAllViews();

        if (getItemCount() ==0) {

detachAndScrapAttachedViews(recycler);

            verticalScrollOffset =0;

return;

        }

if (getChildCount() ==0 && state.isPreLayout()) {

return;

        }

//onLayoutChildren方法在RecyclerView 初始化时 会执行两遍

        detachAndScrapAttachedViews(recycler);

        if (getChildCount() ==0) {

width = getWidth();

            height = getHeight();

            left = getPaddingLeft();

            right = getPaddingRight();

            top = getPaddingTop();

            usedMaxWidth =width -left -right;

        }

for (int i =0; i < getItemCount(); i++) {

Log.d(TAG, "index:" + i);

            View childAt = recycler.getViewForPosition(i);

            if (View.GONE == childAt.getVisibility()) {

continue;

            }

measureChildWithMargins(childAt, 0, 0);

            int childWidth = getDecoratedMeasuredWidth(childAt);

            int childHeight = getDecoratedMeasuredHeight(childAt);

            int childUseWidth = childWidth;

            int childUseHeight = childHeight;

            //如果加上当前的item还小于最大的宽度的话

            if (cuLineWidth + childUseWidth <=usedMaxWidth) {

itemLeft =left + cuLineWidth;

                itemTop = cuLineTop;

                Rect frame =allItemFrames.get(i);

                if (frame ==null) {

frame =new Rect();

                }

frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);

                allItemFrames.put(i, frame);

                cuLineWidth += childUseWidth;

                maxHeightItem = Math.max(maxHeightItem, childUseHeight);

                row.addViews(new Item(childUseHeight, childAt, frame));

                row.setCuTop(cuLineTop);

                row.setMaxHeight(maxHeightItem);

            }else {

//换行

                formatAboveRow();

                cuLineTop += maxHeightItem;

                totalHeight += maxHeightItem;

                itemTop = cuLineTop;

                itemLeft =left;

                Rect frame =allItemFrames.get(i);

                if (frame ==null) {

frame =new Rect();

                }

frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);

                allItemFrames.put(i, frame);

                cuLineWidth = childUseWidth;

                maxHeightItem = childUseHeight;

                row.addViews(new Item(childUseHeight, childAt, frame));

                row.setCuTop(cuLineTop);

                row.setMaxHeight(maxHeightItem);

            }

//不要忘了最后一行进行刷新下布局

            if (i == getItemCount() -1) {

formatAboveRow();

                totalHeight += maxHeightItem;

            }

}

totalHeight = Math.max(totalHeight, getVerticalSpace());

        Log.d(TAG, "onLayoutChildren totalHeight:" +totalHeight);

        fillLayout(recycler, state);

    }

//对出现在屏幕上的item进行展示,超出屏幕的item回收到缓存中

    private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {

if (state.isPreLayout() || getItemCount() ==0) {// 跳过preLayout,preLayout主要用于支持动画

            return;

        }

// 当前scroll offset状态下的显示区域

        Rect displayFrame =new Rect(getPaddingLeft(), getPaddingTop() +verticalScrollOffset,

                getWidth() - getPaddingRight(), verticalScrollOffset + (getHeight() - getPaddingBottom()));

        //对所有的行信息进行遍历

        for (int j =0; j

Row row =lineRows.get(j);

            float lineTop = row.cuTop;

            float lineBottom = lineTop + row.maxHeight;

            //如果该行在屏幕中,进行放置item

//            if (lineTop < displayFrame.bottom && displayFrame.top < lineBottom) {

            List views = row.views;

            for (int i =0; i < views.size(); i++) {

View scrap = views.get(i).view;

                measureChildWithMargins(scrap, 0, 0);

                addView(scrap);

                Rect frame = views.get(i).rect;

                //将这个item布局出来

                layoutDecoratedWithMargins(scrap,

                        frame.left,

                        frame.top -verticalScrollOffset,

                        frame.right,

                        frame.bottom -verticalScrollOffset);

            }

//            } else {

//                //将不在屏幕中的item放到缓存中

//                List views = row.views;

//                for (int i = 0; i < views.size(); i++) {

//                    View scrap = views.get(i).view;

//                    removeAndRecycleView(scrap, recycler);

//                }

//            }

        }

}

/**

* 计算每一行没有居中的viewgroup,让居中显示

*/

    private void formatAboveRow() {

List views =row.views;

        for (int i =0; i < views.size(); i++) {

Item item = views.get(i);

            View view = item.view;

            int position = getPosition(view);

            //如果该item的位置不在该行中间位置的话,进行重新放置

            if (allItemFrames.get(position).top

Rect frame =allItemFrames.get(position);

                if (frame ==null) {

frame =new Rect();

                }

frame.set(allItemFrames.get(position).left, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) /2),

                        allItemFrames.get(position).right, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) /2 + getDecoratedMeasuredHeight(view)));

                allItemFrames.put(position, frame);

                item.setRect(frame);

                views.set(i, item);

            }

}

row.views = views;

        lineRows.add(row);

        row =new Row();

    }

/**

* 竖直方向需要滑动的条件

*

    * @return

    */

    @Override

    public boolean canScrollVertically() {

return true;

    }

//监听竖直方向滑动的偏移量

    @Override

    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,

                                  RecyclerView.State state) {

Log.d("TAG", "totalHeight:" +totalHeight);

        //实际要滑动的距离

        int travel = dy;

        //如果滑动到最顶部

        if (verticalScrollOffset + dy <0) {//限制滑动到顶部之后,不让继续向上滑动了

            travel = -verticalScrollOffset;//verticalScrollOffset=0

        }else if (verticalScrollOffset + dy >totalHeight - getVerticalSpace()) {//如果滑动到最底部

            travel =totalHeight - getVerticalSpace() -verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()

        }

//将竖直方向的偏移量+travel

        verticalScrollOffset += travel;

        // 平移容器内的item

        offsetChildrenVertical(-travel);

        fillLayout(recycler, state);

        return travel;

    }

private int getVerticalSpace() {

return self.getHeight() -self.getPaddingBottom() -self.getPaddingTop();

    }

public int getHorizontalSpace() {

return self.getWidth() -self.getPaddingLeft() -self.getPaddingRight();

    }

}


就这么一个类搞定,接下来的用法和recyclerview的用法一样了


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353