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);
    }
}

​​​​​​​

相关推荐
hunterandroid1 天前
Android WebView JSBridge 治理实战:从线上白屏崩溃到协议化通信
android·前端
一个用户名i1 天前
【Compose 系列】第 1 篇:认识 Compose,为什么要学它
android·android jetpack
极客猴子1 天前
iPhone实时转写软件推荐:会议录音功能真实体验
android·人工智能·飞书
达令哥1 天前
告别 ARouter!基于 Google 官方 Navigation 3 + KSP 打造 Compose 时代的双轨制路由框架
android·前端
枫叶丹41 天前
MCP、A2A、AG-UI:一篇讲清 Agent 协议栈
人工智能·ui·chatgpt·agent·codex
YF02111 天前
Android App启动与权限管控
android
嘟哩DuliDuli1 天前
AI 短剧生成为什么要有角色库、场景库和镜头卡
android·人工智能·安全·ai·软件工程
NINO1 天前
深度解析 Android ANR:从标准分析流程到 NativePollOnce 疑难破局
android
阿巴斯甜1 天前
AppFunctions 完整介绍
android
蜡台1 天前
Jetpack Compose 从入门到精通
android·前端·kotlin·compose·jepack