持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情
前言
在Android开发中,一般通过布局文件的android:layout_margin属性来为组件设置外边距。如果需要动态调整边距,可以通过LayoutParams来设置。
什么是LayoutParams
LayoutParams翻译过来就是布局参数,这个布局参数只有布局容器(六大布局容器,以Layout结尾)拥有。所以,无论是组件还是布局容器,申请LayoutParams拿到的都是ViewGroup.LayoutParams。
在ViewGroup.LayoutParams中,不支持设置margin,参考源码。
在ViewGroup中,还有一个MarginLayoutParams类,是ViewGroup.LayoutParams的子类,它扩展出了margin属性和方法,支持设置View的外边距margin。参看源码。
在六大布局中,除了已经弃用的绝对布局AbsoluteLayout.LayoutParams是ViewGroup.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
}