Zmienić prawy margines widoku programowo?

172

Czy ten atrybut można zmieniać dynamicznie w kodzie Java?

android:layout_marginRight

Mam element TextView, który musi dynamicznie zmieniać swoje położenie o kilka pikseli w lewo.

Jak to zrobić programowo?

NullPointerException
źródło

Odpowiedzi:

462

EDYCJA: Bardziej ogólny sposób, który nie zależy od typu układu (poza tym, że jest to typ układu, który obsługuje marginesy):

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

Powinieneś sprawdzić dokumenty pod kątem TextView . Zasadniczo będziesz chciał pobrać obiekt LayoutParams TextView i zmodyfikować marginesy, a następnie ustawić go z powrotem na TextView. Zakładając, że jest w LinearLayout, spróbuj czegoś takiego:

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

Nie mogę tego teraz przetestować, więc rzutowanie może być trochę wyłączone, ale LayoutParams są tym, co należy zmodyfikować, aby zmienić margines.

UWAGA

Nie zapominaj, że jeśli twój TextView znajduje się wewnątrz, na przykład RelativeLayout , należy użyć RelativeLayout.LayoutParams zamiast LinearLayout.LayoutParams

Kevin Coppock
źródło
39
Po prostu rozwinąć swoją odpowiedź na wypadek, gdyby szukali tego inni ludzie. Jeśli tworzysz TextView programistycznie, musisz również utworzyć nowy obiekt LinearLayout.LayoutParams, zamiast próbować uzyskać go za pomocą .getLayoutParams ();
Ares
To działa świetnie i wygląda na to, że ustawia marginesy w pikselach. Czy można to ustawić w dp?
SirRupertIII
3
@KKendall: Najpierw po prostu przekonwertuj swoje DP na PX .
Kevin Coppock
Jak na twoją UWAGA: mam wątpliwości. Jeśli widok tekstu lub przycisk znajduje się wewnątrz wiersza tabeli? W takim razie czego należy używać zamiast układu względnego, układu tabeli?
tejas
1
W związku z notatką możesz po prostu przesłać tv.getLayoutParams () do ViewGroup.MarginLayoutParams. W ten sposób nie ma znaczenia, jaki układ jest równoległy, o ile implementuje marginesy
Radu Simionescu,
17

Użyj LayoutParams (jak już wyjaśniono). Uważaj jednak, które LayoutParams wybrać. Według https://stackoverflow.com/a/11971553/3184778 „musisz użyć widoku, który odnosi się do RODZICY widoku, nad którym pracujesz, a nie do rzeczywistego widoku”

Jeśli na przykład TextView znajduje się wewnątrz TableRow, musisz użyć TableRow.LayoutParams zamiast RelativeLayout lub LinearLayout

kouretinho
źródło
Dzięki za wytłumaczenie!
Scott Biggs,
1
to naprawdę do bani, że musisz znać miejsce docelowe pola tekstowego, aby ustawić marginesy ... można by pomyśleć, że istnieje rozwiązanie, które jest niezależne od miejsca docelowego.
TatiOverflow
10

Użyj tej funkcji, aby ustawić wszystkie typy marginesów

      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
źródło
2
Dlaczego dodatkowe 0,5f?
behelit
2
@behelit Wiem, że jest późno, ale dla każdego, kto szuka ... kiedy rzucisz spławik na int, spławik się obraca. 0,0 -> 0,4 ​​zaokrągla do 0, podczas gdy 0,5 -> 0,9 zaokrągla do 1. Może to powodować niespójności w końcowych liczbach całkowitych. Aby temu zapobiec, dodanie 0,5 zapewnia zaokrąglenie do 1. Dlaczego? powiedz, że Twój zmiennoprzecinkowy wynosi 0,3: 0,3 + 0,5 = 0,8, co zaokrągla W GÓRĘ do 1. Powiedz, że Twój zmiennoprzecinkowy wynosi 0,8: 0,8 + 0,5 = 1,3, co zaokrągla W DÓŁ do 1. Teraz możesz być bezpieczny ze znajomością końcowego zaokrąglenia (UWAGA BOCZNA: dodajemy 0,5 zamiast odejmować, aby uniknąć ujemnego int)
Psest328,
7

Aktualizacja: Android KTX

Moduł Core KTX zawiera rozszerzenia dla popularnych bibliotek, które są częścią struktury systemu Android, androidx.core.viewmiędzy innymi.

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

Do obsługi marginesów przydatne są następujące funkcje rozszerzające:

Ustawia marginesy wszystkich osi w ViewGroupplikach MarginLayoutParams. (Wymiar należy podać w pikselach, patrz ostatnia sekcja, jeśli chcesz pracować z dp)

inline fun MarginLayoutParams.setMargins(@Px size: Int): Unit
// E.g. 16px margins
val params = (myView.layoutParams as ViewGroup.MarginLayoutParams)
params.setMargins(16)

Aktualizuje marginesy w ViewGrouppliku 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)

Aktualizacje względne marginesy w ViewGroup„s MarginLayoutParams(start / koniec zamiast w lewo / prawo).

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)

Poniższe właściwości rozszerzenia są przydatne, aby uzyskać aktualne marginesy:

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
  • Używanie dpzamiast px:

Jeśli chcesz pracować z dp(pikselami niezależnymi od gęstości) zamiast z px, musisz najpierw je przekonwertować. Możesz to łatwo zrobić za pomocą następującej właściwości rozszerzenia:

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

Następnie możesz wywołać poprzednie funkcje rozszerzeń, takie jak:

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

Stara odpowiedź:

W Kotlinie możesz zadeklarować funkcję rozszerzającą taką jak:

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

Następnie możesz to nazwać tak:

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

Lub:

myView2.setMargins(topMarginDp = 8) 
David Miguel
źródło