在安卓中,我如何以编程方式设置dp边距?

537
这里, 这里这里 的帖子中,我试图找到如何设置单个视图的边距的答案。然而,我想知道是否有更简单的方法。我会解释为什么我不想使用这种方法:
我有一个自定义按钮,它扩展了按钮。如果将背景设置为默认背景之外的其他内容(通过调用setBackgroundResource(int id)setBackgroundDrawable(Drawable d)中的任意一个),我希望边距为0。如果我调用以下内容:
public void setBackgroundToDefault() {
    backgroundIsDefault = true;
    super.setBackgroundResource(android.R.drawable.btn_default);
    // Set margins somehow
}

我希望边距重置为-3dp(我已经阅读了here如何将像素转换为dp,所以一旦我知道如何设置像素边距,就可以自己进行转换)。但由于这是在CustomButton类中调用的,父项可以从LinearLayout变为TableLayout,我不想让他获取其父项并检查该parent的instanceof。我想象那也会相当低效。

此外,在调用(使用LayoutParams)parentLayout.addView(myCustomButton, newParams)时,我不知道是否将其添加到正确的位置(尚未尝试),例如五个按钮中间的中间按钮。

问题:有没有更容易的方法来编程设置单个按钮的边距而不使用LayoutParams?

编辑:我知道LayoutParams的方法,但我想要一个避免处理每个不同容器类型的解决方案:

ViewGroup.LayoutParams p = this.getLayoutParams();
    if (p instanceof LinearLayout.LayoutParams) {
        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)p;
        if (_default) lp.setMargins(mc.oml, mc.omt, mc.omr, mc.omb);
        else lp.setMargins(mc.ml, mc.mt, mc.mr, mc.mb);
        this.setLayoutParams(lp);
    }
    else if (p instanceof RelativeLayout.LayoutParams) {
        RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)p;
        if (_default) lp.setMargins(mc.oml, mc.omt, mc.omr, mc.omb);
        else lp.setMargins(mc.ml, mc.mt, mc.mr, mc.mb);
        this.setLayoutParams(lp);
    }
    else if (p instanceof TableRow.LayoutParams) {
        TableRow.LayoutParams lp = (TableRow.LayoutParams)p;
        if (_default) lp.setMargins(mc.oml, mc.omt, mc.omr, mc.omb);
        else lp.setMargins(mc.ml, mc.mt, mc.mr, mc.mb);
        this.setLayoutParams(lp);
    }
}

因为this.getLayoutParams();返回的是ViewGroup.LayoutParams,而它并没有topMarginbottomMarginleftMarginrightMargin这些属性。你看到的mc实例只是一个MarginContainer,它包含偏移量(-3dp)的边距,以及原始边距(ml, mr, mt, mb)。
31个回答

967

您应该使用 LayoutParams 来设置按钮的边距:

LayoutParams params = new LayoutParams(
        LayoutParams.WRAP_CONTENT,      
        LayoutParams.WRAP_CONTENT
);
params.setMargins(left, top, right, bottom);
yourbutton.setLayoutParams(params);

根据您使用的布局类型,您应该使用RelativeLayout.LayoutParamsLinearLayout.LayoutParams

要将dp度量单位转换为像素,请尝试以下方法:

Resources r = mContext.getResources();
int px = (int) TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        yourdpmeasure, 
        r.getDisplayMetrics()
);

179
我应该从哪个包中导入特定的LayoutParams? - stealthjong
11
setMargins使用px,而你需要使用dp。我的转换是正确的:将dp转换为px以设置正确的边距值。 - throrin19
6
@ChristiaandeJong RelativeLayout.LayoutParams@ChristiaandeJong 是一个用户名或标识符,RelativeLayout.LayoutParams 是一个布局参数类的名称。 - stoefln
18
这取决于你当前的布局。如果你使用LinearLayout,请使用LinearLayout.LayoutParams。否则,请使用RelativeLayout.LayoutParams。 - throrin19
8
你应该导入父布局的layoutParams。例如,如果你正在使用<LinearLayout><RelativeLayout><GridLayout>,并且你正在处理GridLayout,则需要使用RelativeLayout.LayoutParams。请注意不要改变原本的意思。 - Sruit A.Suk
显示剩余7条评论

292

26
正确,但不需要将其设置回去,更改的参数会自动反映。因此,您可以删除以下行:vector8.setLayoutParams(params); - Wirsing
LayaoutParams 在设置 margin 时通常会引起混淆... 因此,MarginLayoutParams 非常有用。谢谢 - Atul Bhardwaj
11
在更改边距后,您需要调用 setLayoutParams(params) 方法。 - Leo DroidCoder
1
我今天发现了MarginLayoutParams这个新类 #谢谢。 - Tushar Pandey
2
不适用于“Button”视图:ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) button.getLayoutParams()返回null - Konstantin Konopko

155

有史以来最好的方法:

private void setMargins (View view, int left, int top, int right, int bottom) {
    if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
        ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        p.setMargins(left, top, right, bottom);
        view.requestLayout();
    }
}

如何调用方法:

setMargins(mImageView, 50, 50, 50, 50);

希望这能对你有所帮助。


1
当我像这样设置setMargins(holder.vCenter, 0, 20, 0,0)时,我遇到了问题,它会在两侧(顶部和底部)留下边距。上述参数有什么问题吗? - Arbaz.in
4
为什么我们需要 requestLayout()requestLayout() 方法用于请求视图树进行重新布局,当一个视图的尺寸或位置发生改变时,需要调用此方法使系统进行相应的重新布局。这是因为 Android 中的视图树是基于父子关系构建的,如果其中一个视图的尺寸或位置发生了改变,那么它的父容器和兄弟视图都需要进行相应的调整以适应新的布局情况。因此,调用 requestLayout() 方法可以触发整个视图树的重新布局过程,从而保证UI界面的正确性和一致性。 - Rishabh Deep Singh

50
int sizeInDP = 16;

int marginInDp = (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, sizeInDP, getResources()
                    .getDisplayMetrics());

那么

layoutParams = myView.getLayoutParams()
layoutParams.setMargins(marginInDp, marginInDp, marginInDp, marginInDp);
myView.setLayoutParams(layoutParams);

或者

LayoutParams layoutParams = new LayoutParams...
layoutParams.setMargins(marginInDp, marginInDp, marginInDp, marginInDp);
myView.setLayoutParams(layoutParams);

什么是getResources()? - Nurkartiko

49

使用Android KTX,您可以像这样做:

yourView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
   setMargins(0, 0, 0, 0)
}

27

这是最新更新的综合答案:

第一步,更新边距

基本思路是将边距拿出来进行更新。更新会自动应用,您无需将其设置回去。要获取布局参数,只需调用此方法:

LayoutParams layoutParams = (LayoutParams) yourView.findViewById(R.id.THE_ID).getLayoutParams();

LayoutParams是来自于视图的布局。如果该视图来自线性布局,您需要导入LinearLayout.LayoutParams。如果使用相对布局,则导入RelativeLayout.LayoutParams,以此类推。

现在,如果您使用Layout_marginLeftRight等设置边距,您需要按照以下方式更新边距。

layoutParams.setMargins(left, top, right, bottom);

若您使用新的layout_marginStart设置margin,您需要按照以下方式更新margin

layoutParams.setMarginStart(start);
layoutParams.setMarginEnd(end);

第二步,更新dp中的边距

上述两种更新边距的方法都是以像素为单位进行更新。您需要将dp转换为像素。

float dpRatio = context.getResources().getDisplayMetrics().density;
int pixelForDp = (int)dpValue * dpRatio;

现在将计算出来的值放入上面的边距更新函数中,您就可以顺利完成了。


16
在 Kotlin 中,它将看起来像这样:
val layoutParams = (yourView?.layoutParams as? MarginLayoutParams)
layoutParams?.setMargins(40, 40, 40, 40)
yourView?.layoutParams = layoutParams

3
这并没有回答问题,因为setMargins方法只接受像素值而不是dp值,而用户所问的是dp值。 - ChallengeAccepted

10

使用此方法可以设置DP中的Margin

public void setMargin(Context con,ViewGroup.LayoutParams params,int dp) {

        final float scale = con.getResources().getDisplayMetrics().density;
        // convert the DP into pixel
        int pixel =  (int)(dp * scale + 0.5f); 

        ViewGroup.MarginLayoutParams s =(ViewGroup.MarginLayoutParams)params;
        s.setMargins(pixel,pixel,pixel,pixel);

        yourView.setLayoutParams(params);
}

更新

您可以更改适合您需求的参数。


10

layout_margin是一个view子元素告诉其父级的约束条件。然而,是否允许边距是父级的角色。基本上,通过设置android:layout_margin="10dp",子元素请求父级视图组分配比其实际大小大10dp的空间。(另一方面,padding="10dp"意味着子视图将使其内容小10dp)。

因此,并非所有ViewGroups都支持margin。最著名的例子是listview,在其中项目的边距被忽略。在将setMargin()应用于LayoutParam之前,您应始终确保当前视图位于支持margin的ViewGroup中(例如LinearLayout或RelativeLayout),并将getLayoutParams()的结果转换为您想要的特定LayoutParams类型。(甚至ViewGroup.LayoutParams也没有setMargins()方法!)

下面的函数应该可以解决问题。但请确保将RelativeLayout替换为父视图的类型。

private void setMargin(int marginInPx) {
    RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
    lp.setMargins(marginInPx,marginInPx, marginInPx, marginInPx);
    setLayoutParams(lp);
}

9

使用 Kotlin,您可以使用 View.updateLayoutParams 扩展函数:

yourView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
    setMargins(15,15,15,15)
}

// or

yourView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
    topMargin = 15
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接