467
votes

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).

23

23 Answers

898
votes

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

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

136
votes

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.

43
votes
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);
21
votes

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

10
votes

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

In Kotlin it will look like this:

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

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.

7
votes

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 }

5
votes

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

That how I have done in kotlin

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

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

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);
2
votes

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

1
votes

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

1
votes

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 }
            }
}
1
votes

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

1
votes

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

With Android KTX, you can do something like that:

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

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
0
votes

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

-1
votes
((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.

-1
votes

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.