204

Can this attribute be changed dynamically in Java code?

android:layout_marginRight

I have a TextView, that has to change its position some pixels to the left dynamically.

How to do it programmatically?

Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
NullPointerException
  • 36,107
  • 79
  • 222
  • 382

4 Answers4

520

EDIT: A more generic way of doing this that doesn't rely on the layout type (other than that it is a layout type which supports margins):

public static void setMargins (View v, int l, int t, int r, int b) {
    if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
        ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
        p.setMargins(l, t, r, b);
        v.requestLayout();
    }
}

You should check the docs for TextView. Basically, you'll want to get the TextView's LayoutParams object, and modify the margins, then set it back to the TextView. Assuming it's in a LinearLayout, try something like this:

TextView tv = (TextView)findViewById(R.id.my_text_view);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)tv.getLayoutParams();
params.setMargins(0, 0, 10, 0); //substitute parameters for left, top, right, bottom
tv.setLayoutParams(params);

I can't test it right now, so my casting may be off by a bit, but the LayoutParams are what need to be modified to change the margin.

NOTE

Don't forget that if your TextView is inside, for example, a RelativeLayout, one should use RelativeLayout.LayoutParams instead of LinearLayout.LayoutParams

Community
  • 1
  • 1
Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • 46
    Just to elaborate in your answer in case other people are looking for this. If you are creating the TextView programmatically then you need to create a new LinearLayout.LayoutParams object too, instead of trying to get one out via .getLayoutParams(); – Ares Oct 26 '11 at 15:27
  • This works awesome, and it seems like it sets the margins in pixels. Is it possible to set it in dp? – SirRupertIII Oct 12 '12 at 21:37
  • 3
    @KKendall: Just [convert your DP to PX](http://stackoverflow.com/a/6327095/321697) first. – Kevin Coppock Oct 12 '12 at 22:20
  • As per your NOTE: I have doubt. If the textview or a button is inside a table row? Then what should I use instead of Relative Layout, Table Layout? – tejas Jun 27 '13 at 04:19
  • 2
    Related to the note, you can just cast tv.getLayoutParams() to ViewGroup.MarginLayoutParams. This way it doesn't matter what layout is the parrent, as long as it implements margins – Radu Simionescu Nov 10 '14 at 11:48
  • Now you can define your margin in different dimensions files and read it from there. `getResources().getDimensionPixelOffset(R.dimen.your_margin)` – Ankit Feb 13 '15 at 14:12
  • if you don't know the container layout, how would you proceed? like the container could be LinearLayout, RelativeLayout, ContraintLayout etc... the reason I ask this, is because it could crash if the container layout type changes in the future, it should really not cause problems for children margins. – TatiOverflow Oct 29 '18 at 23:12
  • @TatiOverflow the answer that checks for MarginLayoutParams will not cause a crash. The type of the container doesn't matter, provided it's a layout that supports margins (i.e. its `LayoutParams` extend from `ViewGroup.MarginLayoutParams`). If they do not, then at worst, your margins will not be adjusted. – Kevin Coppock Oct 31 '18 at 15:33
  • Easy way to convert int to dp -> int * resources.displayMetrics.density. Resources class is available everywhere where you also have context. – Chapz Mar 05 '20 at 08:07
  • To set the marginStart and marginEnd use `p.setMarginStart(l);` and `p.setMarginEnd(r);` – Abhilash Maurya Feb 09 '23 at 10:14
51

Update: Android KTX

The Core KTX module provides extensions for common libraries that are part of the Android framework, androidx.core.view among them.

dependencies {
    implementation "androidx.core:core-ktx:{latest-version}"
}

The following extension functions are handy to deal with margins:

Note: they are all extension functions of MarginLayoutParams, so first you need to get and cast the layoutParams of your view:

val params = (myView.layoutParams as ViewGroup.MarginLayoutParams)

Sets the margins of all axes in the ViewGroup's MarginLayoutParams. (The dimension has to be provided in pixels, see the last section if you want to work with dp)

inline fun MarginLayoutParams.setMargins(@Px size: Int): Unit
// E.g. 16px margins
params.setMargins(16)

Updates the margins in the ViewGroup's ViewGroup.MarginLayoutParams.

inline fun MarginLayoutParams.updateMargins(
    @Px left: Int = leftMargin, 
    @Px top: Int = topMargin, 
    @Px right: Int = rightMargin, 
    @Px bottom: Int = bottomMargin
): Unit
// Example: 8px left margin 
params.updateMargins(left = 8)

Updates the relative margins in the ViewGroup's MarginLayoutParams (start/end instead of left/right).

inline fun MarginLayoutParams.updateMarginsRelative(
    @Px start: Int = marginStart, 
    @Px top: Int = topMargin, 
    @Px end: Int = marginEnd, 
    @Px bottom: Int = bottomMargin
): Unit
// E.g: 8px start margin 
params.updateMargins(start = 8)

The following extension properties are handy to get the current margins:

inline val View.marginBottom: Int
inline val View.marginEnd: Int
inline val View.marginLeft: Int
inline val View.marginRight: Int
inline val View.marginStart: Int
inline val View.marginTop: Int
// E.g: get margin bottom
val bottomPx = myView1.marginBottom
  • Using dp instead of px:

If you want to work with dp (density-independent pixels) instead of px, you will need to convert them first. You can easily do that with the following extension property:

val Int.px: Int
    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

Then you can call the previous extension functions like:

params.updateMargins(start = 16.px, end = 16.px, top = 8.px, bottom = 8.px)
val bottomDp = myView1.marginBottom.dp

Old answer:

In Kotlin you can declare an extension function like:

fun View.setMargins(
    leftMarginDp: Int? = null,
    topMarginDp: Int? = null,
    rightMarginDp: Int? = null,
    bottomMarginDp: Int? = null
) {
    if (layoutParams is ViewGroup.MarginLayoutParams) {
        val params = layoutParams as ViewGroup.MarginLayoutParams
        leftMarginDp?.run { params.leftMargin = this.dpToPx(context) }
        topMarginDp?.run { params.topMargin = this.dpToPx(context) }
        rightMarginDp?.run { params.rightMargin = this.dpToPx(context) }
        bottomMarginDp?.run { params.bottomMargin = this.dpToPx(context) }
        requestLayout()
    }
}

fun Int.dpToPx(context: Context): Int {
    val metrics = context.resources.displayMetrics
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), metrics).toInt()
}

Then you can call it like:

myView1.setMargins(8, 16, 34, 42)

Or:

myView2.setMargins(topMarginDp = 8) 
David Miguel
  • 12,154
  • 3
  • 66
  • 68
18

Use LayoutParams (as explained already). However be careful which LayoutParams to choose. According to https://stackoverflow.com/a/11971553/3184778 "you need to use the one that relates to the PARENT of the view you're working on, not the actual view"

If for example the TextView is inside a TableRow, then you need to use TableRow.LayoutParams instead of RelativeLayout or LinearLayout

Community
  • 1
  • 1
kouretinho
  • 2,190
  • 1
  • 23
  • 37
  • 1
    it really sucks that you have to know the textbox destination in order to set the margins... you'd think there would be a solution that is destination independent. – TatiOverflow Oct 29 '18 at 23:11
12

Use This function to set all Type of margins

      public void setViewMargins(Context con, ViewGroup.LayoutParams params,      
       int left, int top , int right, int bottom, View view) {

    final float scale = con.getResources().getDisplayMetrics().density;
    // convert the DP into pixel
    int pixel_left = (int) (left * scale + 0.5f);
    int pixel_top = (int) (top * scale + 0.5f);
    int pixel_right = (int) (right * scale + 0.5f);
    int pixel_bottom = (int) (bottom * scale + 0.5f);

    ViewGroup.MarginLayoutParams s = (ViewGroup.MarginLayoutParams) params;
    s.setMargins(pixel_left, pixel_top, pixel_right, pixel_bottom);

    view.setLayoutParams(params);
}
Rohit Rathore
  • 382
  • 3
  • 9
  • 2
    Why the additional 0.5f? – behelit Dec 10 '18 at 02:58
  • 3
    @behelit I know this is late but for anyone looking... when you cast a float to an int, the float rounds. 0.0 -> 0.4 rounds to 0 while 0.5 -> 0.9 rounds to 1. This can create inconsistencies in the final ints. To prevent this, adding 0.5 ensures all rounding is to 1. Why? say your float is 0.3: 0.3 + 0.5 = 0.8 which rounds UP to 1. Say your float is 0.8: 0.8 + 0.5 = 1.3 which rounds DOWN to 1. Now you can be safe in your knowledge of the final rounding (SIDE NOTE: we add 0.5 rather than subtract to avoid getting a negative int) – Psest328 Apr 12 '19 at 13:32