Android UI 代码实现:可换行的布局控件

文章目录

ShifterLayout:子控件的宽度不固定

复制代码
/**
 * 可以换行的布局控件,其中的子控件宽度不固定
 */
public class ShifterLayout extends ViewGroup {
    public ShifterLayout(Context context) {
        super(context);
    }


    public ShifterLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childTop = this.getPaddingTop();
        int childLeft = this.getPaddingLeft();
        int childWidth = 0;
        int childHeight  = 0;
        int tempChildLeft = 0;
        for(int i=0; i<getChildCount(); i++) {
            View child = getChildAt(i);
            if(child.getVisibility() == VISIBLE) {
                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                childWidth = child.getMeasuredWidth();
                childHeight  = child.getMeasuredHeight();
                //换行
                tempChildLeft = childLeft +lp.leftMargin + childWidth;
                if(tempChildLeft >= getMeasuredWidth()) {
                    childLeft = this.getPaddingLeft();
                    childTop += childHeight + lp.bottomMargin+lp.topMargin;
                }
                //
                child.layout(childLeft , childTop,
                        childLeft+childWidth, childTop+ childHeight);
                childLeft += childWidth+lp.rightMargin+lp.leftMargin;
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int childTop = this.getPaddingTop();
        int childLeft = this.getPaddingLeft();
        int childWidth = 0;
        int childHeight  = 0;
        int height = 0;
        int tempChildLeft = 0;

        int width = MeasureSpec.getSize(widthMeasureSpec);
        for(int i=0; i<getChildCount(); i++) {
            View child = this.getChildAt(i);
            if(child.getVisibility() == VISIBLE) {
                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                childWidth = child.getMeasuredWidth();
                childHeight  = child.getMeasuredHeight();
                //换行
                tempChildLeft = childLeft +lp.leftMargin + childWidth;
                if(tempChildLeft >= width) {
                    childLeft = this.getPaddingLeft();
                    childTop += childHeight + lp.bottomMargin+lp.topMargin;
                }
                childLeft += childWidth+lp.rightMargin+lp.leftMargin;
             }

        }
        height = childTop + childHeight + this.getPaddingBottom();
        setMeasuredDimension(width, height);

    }

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

ShifterLayout2:子控件的宽度一致且固定

复制代码
/**
 * 可以换行的布局控件,其中的子控件宽度相同且固定
 */
public class ShifterLayout2 extends ViewGroup {
    private int childWidth, childHeight;
    public ShifterLayout2(Context context) {
        super(context);
    }

    public ShifterLayout2(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
//        super.onLayout(changed, l, t, r, b);
        int childTop = this.getPaddingTop();
        int childLeft = this.getPaddingLeft();
        int tempChildLeft = 0;
        for(int i=0; i<getChildCount(); i++) {
            View child = getChildAt(i);
            if(child.getVisibility() == VISIBLE) {
                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
                //换行
                tempChildLeft = childLeft +lp.leftMargin + childWidth;
                if(tempChildLeft >= getMeasuredWidth()) {
                    childLeft = this.getPaddingLeft();
                    childTop += childHeight + lp.bottomMargin+lp.topMargin;
                }
                //
                child.layout(childLeft , childTop,
                        childLeft+childWidth, childTop+ childHeight);
                childLeft += childWidth+lp.rightMargin+lp.leftMargin;
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        View childView = getChildAt(0);
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        //其他类想要获取布局控件中的子控件的宽高:布局控件.getChildAt(0).getMeasuredWidth/Height()
        //不measure其他子控件,目前没有遇到问题
        measureChildWithMargins(childView, widthMeasureSpec, 0, heightMeasureSpec, 0);
//        for(int i=0; i<getChildCount(); i++) {
//            View child = getChildAt(i);
//            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
//        }
        MarginLayoutParams lp =
                (MarginLayoutParams) childView.getLayoutParams();
        int childLeftMargin = lp.leftMargin;
        int childTopMargin = lp.topMargin;
        int childBottomMargin = lp.bottomMargin;
        childWidth = childView.getMeasuredWidth();
        childHeight = childView.getMeasuredHeight();
        int childWidth2 = childWidth + childLeftMargin;
        int childHeight2 = childHeight + childTopMargin + childBottomMargin;;
        int columnNum = (measureWidth)/(childWidth2);
        int height = 0;
        int childCount = getChildCount();
        if(childCount<columnNum) {
            height = childHeight2;
        }
        else {
            height += (childCount/columnNum)*childHeight2;
            if(childCount%columnNum != 0) {
                height += childHeight2;
            }
        }
        height -= childBottomMargin;
        setMeasuredDimension(measureWidth, height);

    }

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

​​​​​​​

相关推荐
冬奇Lab13 小时前
相机拍照流程:从快门按下到JPEG存储的完整旅程
android·音视频开发·源码阅读
alexhilton13 小时前
在Compose中用Shader实现透明的粘稠元球效果
android·kotlin·android jetpack
FlDmr4i2815 小时前
使用Gemini3+ui-ux-pro-max skill开发款查询本地ip插件
tcp/ip·ui·ux
jwn99915 小时前
Laravel5.x核心特性全解析
android·php·laravel
studyForMokey16 小时前
【Android面试】RecylerView专题
android·spring·面试
宇擎智脑科技17 小时前
Claude Code 源码分析(七):终端 UI 工程 —— 用 React Ink 构建工业级命令行界面
前端·人工智能·react.js·ui·claude code
秋雨梧桐叶落莳17 小时前
iOS——UI入门
ui·ios·cocoa
android_cai_niao18 小时前
Android中有什么技术过时了可以直接用新技术的
android·过时技术
lingggggaaaa19 小时前
PHP原生开发篇&文件安全&上传监控&功能定位&关键搜索&1day挖掘
android·学习·安全·web安全·php
我是场19 小时前
我的NPI项目 - OTA upgrade是什么?
android·人工智能