自定义HorizontalScrollView结合FlowLayout实现流式布局翻页效果

本文介绍如何模仿美团外卖点评页,利用HorizontalScrollView和FlowLayout创建流式布局并实现每两行自动翻页的效果。在实现过程中,通过在FlowLayout上添加调整居中和限制绘制行数的代码,并在数据填充后使用回调通知滚动视图进行下一页的绘制。遇到的一个问题是在处理subList时出现ConcurrentModificationException,解决方法是创建新的List实例。源码可在GitHub的ScrollFlowLayout项目中查看。

模仿美团外卖点评页效果,实现流式布局每两行换页。

如下图

这里写图片描述 这里写图片描述 这里写图片描述

项目使用的是 HorizontalScrollView 结合 FlowLayout实现效果
FlowLayout是在鸿洋的代码上修改FlowLayout

添加了调整居中以及只绘制两行的代码

    //居中对齐
    private void adjust() {
        int count = mAllViews.size();
        if (count == 0) {
            return;
        }
        if (count > 2) {
            count = 2;
        }
        for (int i = 0; i < count; i++) {
            List<View> list = mAllViews.get(i);
            if (list.size() == 0) {
                return;
            }
            View rightView = list.get(list.size() - 1);
            int right = rightView.getRight();
            int padding = (getWidth() - right) / 2;
            for (int j = 0; j < list.size(); j++) {
                View view = list.get(j);
                view.layout(view.getLeft() + padding, view.getTop(), view.getRight() + padding, view.getBottom());
            }
        }

    }

绘制完成通知

        //填充完毕一页
        if (onFillCallback != null) {
            int n = 0;
            for (int i = 0; i < mAllViews.size(); i++) {
                n = n + mAllViews.get(i).size();
            }
            onFillCallback.onFill(n);
        }

调用 setData填充数据,每绘制两行使用回调通知JLHorizontalScrollView 进行下一页的绘制,一直到所有数据绘制结束

    public void setData(final List<String> list, final OnCompleteCallback onCompleteCallback) {
        //延迟以获取距离屏幕左侧的距离用来支持 margin 和 padding
        post(new Runnable() {
            @Override
            public void run() {
                clearLastPage();
                mData.clear();
                if (mContainer != null) {
                    mContainer.removeAllViews();
                    mPageCount = 0;
                }
                mOnCompleteCallback = onCompleteCallback;
                int[] xy = new int[2];
                getLocationOnScreen(xy);
                mMargin = xy[0];
                mRealWidth = mScreenWidth - mMargin * 2;
                if (list == null) {
                    return;
                }
                mList = list;
                JLFlowLayout jlFlowLayout = new JLFlowLayout(mContext);
                jlFlowLayout.setOnFillCallback(onFillCallback);
                mJLFlowLayoutList.add(jlFlowLayout);
                addPage(jlFlowLayout);
                fillData(mList, jlFlowLayout);
            }
        });

    }
   /**
     * 填满一页返回再填下一页
     */
    JLFlowLayout.OnFillCallback onFillCallback = new JLFlowLayout.OnFillCallback() {
        @Override
        public void onFill(final int index) {
            //一个一个加载,否则回调顺序就乱掉了
            post(new Runnable() {
                @Override
                public void run() {
                    int count = mList.size();
                    if (index > count) {
                        return;
                    }
                    //subList生成子列表后,不要试图去操作原列表 解决ConcurrentModificationException
                    List<String> cache = new ArrayList<String>();
                    cache.addAll(mList.subList(index, count));
                    if (cache.isEmpty()) {
                        mOnCompleteCallback.onComplete(mPageCount);
                        return;
                    }
                    mList = cache;
                    JLFlowLayout jlFlowLayout = new JLFlowLayout(mContext);
                    jlFlowLayout.setOnFillCallback(onFillCallback);
                    mJLFlowLayoutList.add(jlFlowLayout);
                    addPage(jlFlowLayout);
                    fillData(cache, jlFlowLayout);
                }
            });
        }
    };

其中有个坑,对subList的返回值进行 subList会报ConcurrentModificationException异常,原因subList的缺陷,解决办法重新创建一个 List 并赋值

使用 post保证绘制按顺序执行

具体细节看源码
GitHub地址ScrollFlowLayout

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值