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

​​​​​​​

相关推荐
儿歌八万首18 分钟前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
消失的旧时光-19433 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
Jinkxs3 小时前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin
&有梦想的咸鱼&3 小时前
Kotlin委托机制的底层实现深度解析(74)
android·开发语言·kotlin
LDORntKQH3 小时前
基于深度强化学习的混合动力汽车能量管理策略 1.利用DQN算法控制电池和发动机发电机组的功率分配 2
android
冬奇Lab3 小时前
Android 15 ServiceManager与Binder服务注册深度解析
android·源码·源码阅读
2501_916008895 小时前
深入解析iOS机审4.3原理与混淆实战方法
android·java·开发语言·ios·小程序·uni-app·iphone
独行soc6 小时前
2026年渗透测试面试题总结-20(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
会一点设计7 小时前
6个优质春节海报模板网站推荐!轻松设计马年祝福海报
ui·ux
常利兵7 小时前
2026年,Android开发已死?不,它正迎来黄金时代!
android