自定义ViewGroup 流式布局

此博客展示了 Android 中自定义视图组 FlowLayout 的代码实现。包含 onMeasure 方法测量子视图宽高以确定总高度和行宽,onLayout 方法对子视图进行布局,还有 addPlaybill 方法添加数据并设置子视图属性,如文本、点击事件等。
public class FlowLayout extends ViewGroup {
    private final String TAG = FlowLayout.class.getSimpleName();
    private int dp8 = ScreenAdapter.dp2px(8);
    private int dp2 = ScreenAdapter.dp2px(2);
    private int dp12 = ScreenAdapter.dp2px(12);

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        //当前ViewGroup的总高度
        int totalHeight = 0;

        //当前行的总宽度
        int currentLineWidth = 0;

        //每个childView所占用的宽度
        int childViewWidthSpace = 0;
        //每个childView所占用的高度
        int childViewHeightSpace = 0;

        int count = getChildCount();
        MarginLayoutParams layoutParams;

        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);

            if (child.getVisibility() != View.GONE) {//只有当这个View能够显示的时候才去测量
                //测量每个子View,以获取子View的宽和高
                measureChild(child, widthMeasureSpec, heightMeasureSpec);

                layoutParams = (MarginLayoutParams) child.getLayoutParams();

                // 一个view真实占用的宽高
                childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
                childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
                if (totalHeight == 0) {
                    totalHeight = childViewHeightSpace;
                }
                //表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行
                if (currentLineWidth + childViewWidthSpace > widthSize) {
                    totalHeight += childViewHeightSpace;
                    //另起一行后,需要重置当前行宽
                    currentLineWidth = childViewWidthSpace;
                } else {//表示当前行可以继续添加子元素
                    currentLineWidth += childViewWidthSpace;
                }
            }
        }

        setMeasuredDimension(widthSize, totalHeight);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //每个childView所占用的宽度
        int childViewWidthSpace = 0;
        //每个childView所占用的高度
        int childViewHeightSpace = 0;

        //当前行的总宽度
        int currentLineWidth = 0;

        int currentTop = 0;

        int count = getChildCount();
        MarginLayoutParams layoutParams;
        LogUtil.i(TAG, "onLayout count " + count);
        for (int i = 0; i < count; i++) {
            int left = 0, top = 0, right = 0, bottom = 0;
            View child = getChildAt(i);
            //只有当这个View能够显示的时候才去测量
            if (child.getVisibility() != View.GONE) {
                layoutParams = (MarginLayoutParams) child.getLayoutParams();
                childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
                childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;

                //表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行
                if (currentLineWidth + childViewWidthSpace > getWidth()) {
                    // 新的一行 当前行宽就是当前view的宽
                    currentTop += childViewHeightSpace;
                    currentLineWidth = childViewWidthSpace;

                    left = layoutParams.leftMargin;
                } else {//表示当前行可以继续添加子元素
                    left = currentLineWidth + layoutParams.leftMargin;
                    // 当前宽度要加上新的view了
                    currentLineWidth += childViewWidthSpace;
                }
                top = (currentTop + layoutParams.topMargin);
                right = left + child.getMeasuredWidth();
                bottom = top + child.getMeasuredHeight();

                child.layout(left, top, right, bottom);
                LogUtil.i(TAG, "layout " + left + " " + top + " " + right + " " + bottom);

            }
        }
    }

    /**
     * 对外数据接口,不同数据可自己创建新的方法
     *
     * @param playbills
     */
    public void addPlaybill(Activity activity, ArrayList<ThemePlaybillBean> playbills) {
        if (playbills == null) {
            return;
        }
        removeAllViews();
        for (int i = 0; i < playbills.size(); i++) {
            TextView tv = new TextView(getContext());
            MarginLayoutParams lp = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT);
            lp.setMargins(0, 0, dp12 , dp12);
            tv.setIncludeFontPadding(false);
            tv.setLayoutParams(lp);
            tv.setBackgroundResource(R.drawable.shape_1afff_round);
            /*
             * setPadding一定要在setBackgroundResource后面使用才有效!!!
             * http://stackoverflow.com/questions/18327498/setting-padding-for-textview-not-working
             */
            tv.setPadding(dp8, dp2, dp8, dp2);
            tv.setTextColor(0xffffffff);
            if(playbills.get(i).title.length() > 8) {
                tv.setText(playbills.get(i).title.substring(0, 8) + "...");
            } else {
                tv.setText(playbills.get(i).title);
            }
            tv.setTextSize(12);
            long id = playbills.get(i).id;
            tv.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    MainJump.toThemePlaybillPage(activity, id);
                }
            });
            addView(tv);
        }
        requestLayout();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值