动态设置布局组件的外边距margin

1,597 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

前言

在Android开发中,一般通过布局文件的android:layout_margin属性来为组件设置外边距。如果需要动态调整边距,可以通过LayoutParams来设置。

什么是LayoutParams

LayoutParams翻译过来就是布局参数,这个布局参数只有布局容器(六大布局容器,以Layout结尾)拥有。所以,无论是组件还是布局容器,申请LayoutParams拿到的都是ViewGroup.LayoutParams

ViewGroup.LayoutParams中,不支持设置margin,参考源码。

在ViewGroup中,还有一个MarginLayoutParams类,是ViewGroup.LayoutParams的子类,它扩展出了margin属性和方法,支持设置View的外边距margin。参看源码。

在六大布局中,除了已经弃用的绝对布局AbsoluteLayout.LayoutParamsViewGroup.LayoutParams,其他布局的LayoutParams均是ViewGroup.MarginLayoutParams(TableLayout.LayoutParams继承自LinearLayout.LayoutParams)。在获得View的LayoutParams后,MarginLayoutParams,便可对其margin进行设置。

ViewGroup.LayoutParams源码,该类中并没有margin相关的属性,不支持设置margin:

public static class LayoutParams {
        /** @deprecated */
        @Deprecated
        public static final int FILL_PARENT = -1;
        public static final int MATCH_PARENT = -1;
        public static final int WRAP_CONTENT = -2;
        @ExportedProperty(
            category = "layout",
            mapping = {@IntToString(
    from = -1,
    to = "MATCH_PARENT"
), @IntToString(
    from = -2,
    to = "WRAP_CONTENT"
)}
        )
        public int height;
        public AnimationParameters layoutAnimationParameters;
        @ExportedProperty(
            category = "layout",
            mapping = {@IntToString(
    from = -1,
    to = "MATCH_PARENT"
), @IntToString(
    from = -2,
    to = "WRAP_CONTENT"
)}
        )
        public int width;

        public LayoutParams(Context c, AttributeSet attrs) {
            throw new RuntimeException("Stub!");
        }

        public LayoutParams(int width, int height) {
            throw new RuntimeException("Stub!");
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            throw new RuntimeException("Stub!");
        }

        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
            throw new RuntimeException("Stub!");
        }

        public void resolveLayoutDirection(int layoutDirection) {
            throw new RuntimeException("Stub!");
        }
    }

ViewGroup.MarginLayoutParams源码,支持设置margin:

public static class MarginLayoutParams extends ViewGroup.LayoutParams {
    @ExportedProperty(
        category = "layout"
    )
    public int bottomMargin;
    @ExportedProperty(
        category = "layout"
    )
    public int leftMargin;
    @ExportedProperty(
        category = "layout"
    )
    public int rightMargin;
    @ExportedProperty(
        category = "layout"
    )
    public int topMargin;

    public MarginLayoutParams(Context c, AttributeSet attrs) {
        super((ViewGroup.LayoutParams)null);
        throw new RuntimeException("Stub!");
    }

    public MarginLayoutParams(int width, int height) {
        super((ViewGroup.LayoutParams)null);
        throw new RuntimeException("Stub!");
    }

    public MarginLayoutParams(ViewGroup.MarginLayoutParams source) {
        super((ViewGroup.LayoutParams)null);
        throw new RuntimeException("Stub!");
    }

    public MarginLayoutParams(ViewGroup.LayoutParams source) {
        super((ViewGroup.LayoutParams)null);
        throw new RuntimeException("Stub!");
    }

    public void setMargins(int left, int top, int right, int bottom) {
        throw new RuntimeException("Stub!");
    }

    public void setMarginStart(int start) {
        throw new RuntimeException("Stub!");
    }

    public int getMarginStart() {
        throw new RuntimeException("Stub!");
    }

    public void setMarginEnd(int end) {
        throw new RuntimeException("Stub!");
    }

    public int getMarginEnd() {
        throw new RuntimeException("Stub!");
    }

    public boolean isMarginRelative() {
        throw new RuntimeException("Stub!");
    }

    public void setLayoutDirection(int layoutDirection) {
        throw new RuntimeException("Stub!");
    }

    public int getLayoutDirection() {
        throw new RuntimeException("Stub!");
    }

    public void resolveLayoutDirection(int layoutDirection) {
        throw new RuntimeException("Stub!");
    }
}
通过getLayoutParams()设置 margin

获得LayoutParams后强制转换类型,一般转换为FrameLayout.LayoutParams是不会错的,转成其他类型可能会报类型转换错误(还没搞懂为什么)。之后可以通过marginStart、topMargin等设置单个属性,也可通过setMargins(start,top,end,bottom)顺时针顺序对多个属性进行设置,还可以通过setMargins(margin)对所有属性进行统一设置。

private val tv: TextView
    get() = viewBinding.contentTv
    
fun setMargin(marginStart: Int, marginTop: Int, marginEnd: Int, marginBottom: Int) {
    val tvLp = tv.layoutParams as FrameLayout.LayoutParams
    //单独设置
    tvLp.run {
        this.marginStart = marginStart
        this.topMargin = marginTop
        this.marginEnd = marginEnd
        this.bottomMargin = marginBottom
    }
    //统一设置
    tvLp.setMargins(marginStart, marginTop, marginEnd, marginBottom)
    tv.layoutParams=tvLp
}
通过新建一个LayoutParams设置margin

一般通过新建一个FrameLayout.LayoutParams,参数填入组件的width和height。设置margin后,再将它设置给view来实现动态设置margin。但这样做有一个弊端,view原先的LayoutParams属性会被覆盖掉。

fun setMargin(margin: Int) {
    val lp=FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,FrameLayout.LayoutParams.WRAP_CONTENT)
    lp.setMargins(margin)
    tv.layoutParams=lp
}