ChatGPT解决这个技术问题 Extra ChatGPT

In Android, how do I set margins in dp programmatically?

In this, this and this thread I tried to find an answer on how to set the margins on a single view. However, I was wondering if there isn't an easier way. I'll explain why I rather wouldn't want to use this approach:

I have a custom Button which extends Button. If the background is set to something else than the default background (by calling either setBackgroundResource(int id) or setBackgroundDrawable(Drawable d)), I want the margins to be 0. If I call this:

public void setBackgroundToDefault() {
    backgroundIsDefault = true;
    super.setBackgroundResource(android.R.drawable.btn_default);
    // Set margins somehow
}

I want the margins to reset to -3dp (I already read here how to convert from pixels to dp, so once I know how to set margins in px, I can manage the conversion myself). But since this is called in the CustomButton class, the parent can vary from LinearLayout to TableLayout, and I'd rather not have him get his parent and check the instanceof that parent. That'll also be quite inperformant, I imagine.

Also, when calling (using LayoutParams) parentLayout.addView(myCustomButton, newParams), I don't know if this adds it to the correct position (haven't tried however), say the middle button of a row of five.

Question: Is there any easier way to set the margin of a single Button programmatically besides using LayoutParams?

EDIT: I know of the LayoutParams way, but I'd like a solution that avoids handling each different container type:

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

Because this.getLayoutParams();returns a ViewGroup.LayoutParams, which do not have the attributes topMargin, bottomMargin, leftMargin, rightMargin. The mc instance you see is just a MarginContainer which contains offset (-3dp) margins and (oml, omr, omt, omb) and the original margins (ml, mr, mt, mb).


s
slhck

You should use LayoutParams to set your button margins:

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

Depending on what layout you're using you should use RelativeLayout.LayoutParams or LinearLayout.LayoutParams.

And to convert your dp measure to pixel, try this:

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

From which package should I import that particular LayoutParams?
setMargins used px and you will use dp, my conversion is right : dp -> px to set correct margin value.
@ChristiaandeJong RelativeLayout.LayoutParams
it depends of your current layout. If you are in LinearLayout, use LinearLayout.LayoutParams. RelativeLayout.LayoutParams otherwise
you should import layoutParams that is your parent layout ex. and you are working with grid layout. then, you need to use relativelayout.layoutparams
M
MC Emperor

LayoutParams - NOT WORKING ! ! !

Need use type of: MarginLayoutParams

MarginLayoutParams params = (MarginLayoutParams) vector8.getLayoutParams();
params.width = 200; params.leftMargin = 100; params.topMargin = 200;

Code Example for MarginLayoutParams:

http://www.codota.com/android/classes/android.view.ViewGroup.MarginLayoutParams


Correct, but no need to set it back, the changed params are automatically reflected. Thus you can remove the line: vector8.setLayoutParams(params);
LayaoutParams usually create confusion while setting margin... So this MarginLayoutParams is very useful. Thanks
you DO need to setLayoutParams(params) after marging changes
i found MarginLayoutParams as new class today #Thanks.
Doesn't work for Button view: ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) button.getLayoutParams() returns null
H
Hiren Patel

Best way ever:

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

How to call method:

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

Hope this will help you.


i am facing issue when i set setMargins(holder.vCenter, 0, 20, 0,0); like this its leave margin both sides (top and bottom) whats wrong with above params?
why do we need requestLayout() ?
k
kenju
int sizeInDP = 16;

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

Then

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

Or

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

what is getResources()?
C
Community

Here is the all-in-one answer with recent updates:

Step 1, to update margin

The basic idea is to get margin out and then update it. The update will be applies automatically and you do not need to set it back. To get the layout parameters, simply call this method:

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

The LayoutParams comes from the layout of your view. If the view is from a linear layout, you need to import LinearLayout.LayoutParams. If you use relative layout, import LinearLayout.LayoutParams , etc.

Now, if you set the margin using Layout_marginLeft, Right, etc, you need to update margin in this way

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

If you set margin using the new layout_marginStart, you need to update margin in this way

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

Step 2, to update margin in dp

All two ways of updating margin above are updating in pixels. You need to do a translation of dp to pixels.

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

Now put the calculated value to the above margin update functions and you should be all set


P
Phil

With Android KTX, you can do something like that:

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

M
Morozov

In Kotlin it will look like this:

val layoutParams = (yourView?.layoutParams as? MarginLayoutParams)
layoutParams?.setMargins(40, 40, 40, 40)
yourView?.layoutParams = layoutParams

This doesn't answer the question as the setMargins method takes in only values in pixels and not dp, which is what the user is asking.
n
neferpitou

This method will let you set the Margin in DP

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

UPDATE

You can change the parameter that suits your need.


I
Ian Wong

layout_margin is a constraint that a view child tell to its parent. However it is the parent's role to choose whether to allow margin or not. Basically by setting android:layout_margin="10dp", the child is pleading the parent view group to allocate space that is 10dp bigger than its actual size. (padding="10dp", on the other hand, means the child view will make its own content 10dp smaller.)

Consequently, not all ViewGroups respect margin. The most notorious example would be listview, where the margins of items are ignored. Before you call setMargin() to a LayoutParam, you should always make sure that the current view is living in a ViewGroup that supports margin (e.g. LinearLayouot or RelativeLayout), and cast the result of getLayoutParams() to the specific LayoutParams you want. (ViewGroup.LayoutParams does not even have setMargins() method!)

The function below should do the trick. However make sure you substitute RelativeLayout to the type of the parent view.

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

D
Dishant Kawatra

You can use this method and put static dimen like 20 it converts according your device

 public static int dpToPx(int dp) 
      {
          float scale = context.getResources().getDisplayMetrics().density;
       return (int) (dp * scale + 0.5f);
  }

G
Gibolt

Simple Kotlin Extension Solutions

Set all/any side independently:

fun View.setMargin(left: Int? = null, top: Int? = null, right: Int? = null, bottom: Int? = null) {
    val params = (layoutParams as? MarginLayoutParams)
    params?.setMargins(
            left ?: params.leftMargin,
            top ?: params.topMargin,
            right ?: params.rightMargin,
            bottom ?: params.bottomMargin)
    layoutParams = params
}

myView.setMargin(10, 5, 10, 5)
// or just any subset
myView.setMargin(right = 10, bottom = 5)

Directly refer to a resource values:

fun View.setMarginRes(@DimenRes left: Int? = null, @DimenRes top: Int? = null, @DimenRes right: Int? = null, @DimenRes bottom: Int? = null) {
    setMargin(
            if (left == null) null else resources.getDimensionPixelSize(left),
            if (top == null) null else resources.getDimensionPixelSize(top),
            if (right == null) null else resources.getDimensionPixelSize(right),
            if (bottom == null) null else resources.getDimensionPixelSize(bottom),
    )
}

myView.setMarginRes(top = R.dimen.my_margin_res)

To directly set all sides equally as a property:

var View.margin: Int
    get() = throw UnsupportedOperationException("No getter for property")
    set(@Px margin) = setMargin(margin, margin, margin, margin)
   
myView.margin = 10 // px

// or as res
var View.marginRes: Int
    get() = throw UnsupportedOperationException("No getter for property")
    set(@DimenRes marginRes) {
        margin = resources.getDimensionPixelSize(marginRes)
    }

myView.marginRes = R.dimen.my_margin_res

To directly set a specific side, you can create a property extension like this:

var View.leftMargin
    get() = marginLeft
    set(@Px leftMargin) = setMargin(left = leftMargin)

var View.leftMarginRes: Int
    get() = throw UnsupportedOperationException("No getter for property")
    set(@DimenRes leftMarginRes) {
        leftMargin = resources.getDimensionPixelSize(leftMarginRes)
    }

This allows you to make horizontal or vertical variants as well:

var View.horizontalMargin
    get() = throw UnsupportedOperationException("No getter for property")
    set(@Px horizontalMargin) = setMargin(left = horizontalMargin, right = horizontalMargin)

var View.horizontalMarginRes: Int
    get() = throw UnsupportedOperationException("No getter for property")
    set(@DimenRes horizontalMarginRes) {
        horizontalMargin = resources.getDimensionPixelSize(horizontalMarginRes)
    }

NOTE: If margin is failing to set, you may too soon before render, meaning params == null. Try wrapping the modification with myView.post{ margin = 10 }


T
Tas

That how I have done in kotlin

fun View.setTopMargin(@DimenRes dimensionResId: Int) {
    (layoutParams as ViewGroup.MarginLayoutParams).topMargin = resources.getDimension(dimensionResId).toInt()
}

This is really a clean solution. It helped me.
A
A S M Sayem

If you want to add a margin to your TextView you will have to LayoutParams:

val params =  LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT)
params.setMargins(int left, int top, int right, int bottom)
your_view.layoutParams = params

LayoutParams can be any layouts like Relative, Linear, View or ViewGroups. Choose the LayoutParams as you need. Thanks


op asked for margins
I mentioned both padding and margin. I think you misunderstood. I hope you read my whole answer properly! @anshsachdeva
yes, my bad. its now 2 days since i commented, so i can't change my -1. kindly make edits to your answer to highlight the actual solution and i will change it :)
@anshsachdeva, I have updated the answer of mine. Let me know if it's helpful or not. Thanks
R
Ridwan Bin Sarwar

Use this method to set margin in dp

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

        final float scale = getBaseContext().getResources().getDisplayMetrics().density;
        // convert the DP into pixel
        int l =  (int)(left * scale + 0.5f);
        int r =  (int)(right * scale + 0.5f);
        int t =  (int)(top * scale + 0.5f);
        int b =  (int)(bottom * scale + 0.5f);

        p.setMargins(l, t, r, b);
        view.requestLayout();
    }
}

call the method :

setMargins(linearLayout,5,0,5,0);

Simply simple!. Thanks
a
aldajo92

When you are in a custom View, you can use getDimensionPixelSize(R.dimen.dimen_value), in my case, I added the margin in LayoutParams created on init method.

In Kotlin

init {
    LayoutInflater.from(context).inflate(R.layout.my_layout, this, true)
    layoutParams = LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
    val margin = resources.getDimensionPixelSize(R.dimen.dimen_value)
    setMargins(0, margin, 0, margin)
}

in Java:

public class CustomView extends LinearLayout {

    //..other constructors

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        int margin = getResources().getDimensionPixelSize(R.dimen.spacing_dime);
        params.setMargins(0, margin, 0, margin);
        setLayoutParams(params);
    }
}

S
Shaon

Using Kotlin,

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

D
Dasser Basyouni

For a quick one-line setup use

((LayoutParams) cvHolder.getLayoutParams()).setMargins(0, 0, 0, 0);

but be carfull for any wrong use to LayoutParams, as this will have no if statment instance chech


D
David Kim

Created a Kotlin Extension function for those of you who might find it handy.

Make sure to pass in pixels not dp. Happy coding :)

fun View.addLayoutMargins(left: Int? = null, top: Int? = null,
                      right: Int? = null, bottom: Int? = null) {
    this.layoutParams = ViewGroup.MarginLayoutParams(this.layoutParams)
            .apply {
                left?.let { leftMargin = it }
                top?.let { topMargin = it }
                right?.let { rightMargin = it }
                bottom?.let { bottomMargin = it }
            }
}

android.view.ViewGroup$MarginLayoutParams cannot be cast to android.widget.RelativeLayout$LayoutParams. :(
R
Rajeev Shetty

In my example i am adding an ImageView to a LinearLayout programatically. I have set top and bottom margins to ImagerView. Then adding the ImageView to the LinearLayout.



        ImageView imageView = new ImageView(mContext);
        imageView.setImageBitmap(bitmap);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
        );
        params.setMargins(0, 20, 0, 40);
        imageView.setLayoutParams(params);
        linearLayout.addView(imageView);


Y
Yogi Bear

Working utils function using DP for those interested:

public static void setMargins(Context context, View view, int left, int top, int right, int bottom) {
    int marginLeft = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, left, context.getResources().getDisplayMetrics());
    int marginTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, top, context.getResources().getDisplayMetrics());
    int marginRight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, right, context.getResources().getDisplayMetrics());
    int marginBottom = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, bottom, context.getResources().getDisplayMetrics());

    if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
        ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        p.setMargins(marginLeft, marginTop, marginRight, marginBottom);
        view.requestLayout();
    }
}

a
ansh sachdeva

Based on other answers, i made a generic extension function, which identifies your parent and uses the params accordingly:

//takes margin values as integer , eg for 12dp top , you will pass 12
fun View?.setMarginFromConstant(mLeft:Int, mTop:Int, mRight:Int, mBottom:Int){
    this?.apply {
        val left = context?.dpToPixel(mLeft)?:0
        val top = context?.dpToPixel(mTop)?:0
        val right = context?.dpToPixel(mRight)?:0
        val bottom = context?.dpToPixel(mBottom)?:0
        when (val params = this.layoutParams) {
            is ConstraintLayout.LayoutParams -> {
                params.marginStart = left
                params.marginEnd = right
                params.topMargin = top
                params.bottomMargin = bottom
            }
            is FrameLayout.LayoutParams -> {
                params.marginStart = left
                params.marginEnd = right
                params.topMargin = top
                params.bottomMargin = bottom
            }
            is RecyclerView.LayoutParams -> {
                params.marginStart = left
                params.marginEnd = right
                params.topMargin = top
                params.bottomMargin = bottom
            }
        }
    }

}

and

fun Context.dpToPixel(dp: Int): Int =
    (dp * applicationContext.resources.displayMetrics.density).toInt()

You can add support for other parent view groups too


M
Mubarrat Hasan

Use this method to you can set dp correctly:

public int dpFormat(int dp) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}

and then call

setMargins(dpFormat(15), dpFormat(15), dpFormat(15), dpFormat(15));

its work for me but maybe there should be getApplicationContext() instead of getContext().
s
sonique

As today, the best is probably to use Paris, a library provided by AirBnB.

Styles can then be applied like this:

Paris.style(myView).apply(R.style.MyStyle);

it also support custom view (if you extend a view) using annotations:

@Styleable and @Style

G
GeneCode

For me I was using ViewGroup.LayoutParams in a themedbutton component. So using ViewGroup.LayoutParams unable to use setMargin.

Instead use MarginLayoutParams as below, it worked for me.

ViewGroup.MarginLayoutParams params =new ViewGroup.MarginLayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );
        params.setMargins(0,8,0,0);

E
Ebin Joy

For me, the following code worked

buttonLinearLayout.layoutParams as MarginLayoutParams).apply 
{
   top.run { 
        topMargin = resources.getDimension(R.dimen.spacing).toInt() 
     }                        
}

G
General Grievance
val params = FrameLayout.LayoutParams(
    ViewGroup.LayoutParams.MATCH_PARENT,
    ViewGroup.LayoutParams.MATCH_PARENT
)
params.setMargins(0, 0, 0, 400)
binding.container.setLayoutParams(params)

d
dpapadopoulos
((FrameLayout.LayoutParams) linearLayout.getLayoutParams()).setMargins(450, 20, 0, 250);
        linearLayout.setBackgroundResource(R.drawable.smartlight_background);

I had to cast mine to FrameLayout for a linearLayout as it inherits from it and set margins there so the activity appears only on part of the screen along with a different background than the original layout params in the setContentView.

LinearLayout linearLayout = (LinearLayout) findViewById(R.id.activity);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(WindowManager.LayoutParams.FILL_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
linearLayout.setBackgroundColor(getResources().getColor(R.color.full_white));
setContentView(linearLayout,layoutParams);

None of the others worked for using the same activity and changing the margins based on opening the activity from a different menu! setLayoutParams never worked for me - the device would crash every single time. Even though these are hardcoded numbers - this is only an example of the code for demonstration purposes only.


m
mmmcho

You can use ViewGroup.MarginLayoutParams to set the width, height and margins

ViewGroup.MarginLayoutParams marginLayoutParams = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
marginLayoutParams.setMargins(0,16,0,16);
linearLayout.setLayoutParams(marginLayoutParams);

Where the method setMargins(); takes in values for left, top, right, bottom respectively. Clockwise!, starting from the left.