Изменить правильную границу представления программно?

173

Может ли этот атрибут динамически изменяться в коде Java?

android:layout_marginRight

У меня есть TextView, который должен изменить свою позицию несколько пикселей влево динамически.

Как это сделать программно?

Исключение нулевого указателя
источник

Ответы:

463

РЕДАКТИРОВАТЬ: более общий способ сделать это, который не зависит от типа макета (кроме того, что это тип макета, который поддерживает поля):

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

Вы должны проверить документы для TextView . По сути, вам нужно получить объект LayoutParams в TextView, изменить поля, а затем вернуть его обратно в TextView. Предполагая, что это в LinearLayout, попробуйте что-то вроде этого:

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

Я не могу проверить это прямо сейчас, поэтому мой кастинг может быть немного отключен, но LayoutParams - это то, что нужно изменить, чтобы изменить маржу.

НОТА

Не забывайте, что если ваш TextView находится внутри, например, RelativeLayout , следует использовать RelativeLayout.LayoutParams вместо LinearLayout.LayoutParams

Кевин Коппок
источник
39
Просто уточните ваш ответ на случай, если другие люди ищут это. Если вы создаете TextView программно, то вам также необходимо создать новый объект LinearLayout.LayoutParams вместо того, чтобы пытаться получить его с помощью .getLayoutParams ();
Арес
Это работает потрясающе, и кажется, что устанавливает поля в пикселях. Можно ли установить его в дп?
SirRupertIII
3
@KKendall: просто сначала конвертируй DP в PX .
Кевин Коппок
Согласно вашему ПРИМЕЧАНИЕ: у меня есть сомнения. Если текстовое представление или кнопка находится внутри строки таблицы? Тогда что я должен использовать вместо Relative Layout, Table Layout?
Tejas
1
Что касается примечания, вы можете просто привести tv.getLayoutParams () к ViewGroup.MarginLayoutParams. Таким образом, не имеет значения, какой макет является parrent, если он реализует поля
Раду Симионеску
17

Используйте LayoutParams (как уже объяснялось). Однако будьте осторожны, чтобы выбрать LayoutParams. Согласно https://stackoverflow.com/a/11971553/3184778 «вам нужно использовать тот, который относится к РОДИТЕЛЯм представления, над которым вы работаете, а не к фактическому представлению»

Если, например, TextView находится внутри TableRow, то вам нужно использовать TableRow.LayoutParams вместо RelativeLayout или LinearLayout

kouretinho
источник
Спасибо за разъяснение!
Скотт Биггс
1
это действительно отстой, что вам нужно знать место назначения текстового поля, чтобы установить поля ... вы думаете, что будет решение, которое не зависит от места назначения.
TatiOverflow
10

Используйте эту функцию для установки всех типов полей

      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);
}
Рохит Раторе
источник
2
Почему дополнительные 0,5f?
behelit
2
@behelit Я знаю, что уже поздно, но для всех, кто смотрит ... когда вы бросаете float в int, он плавает. 0,0 -> 0,4 ​​раунда до 0, в то время как 0,5 -> 0,9 раунда до 1. Это может привести к несоответствиям в последних целых числах. Чтобы предотвратить это, добавление 0,5 обеспечивает округление до 1. Почему? допустим, что ваш коэффициент равен 0,3: 0,3 + 0,5 = 0,8, что округляет до 1. Скажем, что ваш коэффициент равен 0,8: 0,8 + 0,5 = 1,3, что округляет до 1. Теперь вы можете быть уверены в своих знаниях об окончательном округлении (ПРИМЕЧАНИЕ: мы добавляем 0.5, а не вычитаем, чтобы избежать получения отрицательного значения int)
Psest328
8

Обновление: Android KTX

Модуль Core KTX предоставляет расширения для общих библиотек, которые являются частью платформы Android, androidx.core.viewсреди них.

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

Следующие функции расширения удобны для работы с полями:

Устанавливают задворки всех осей в ViewGroup«с MarginLayoutParams. (Размерность должна быть представлена ​​в пикселях, см. Последний раздел, если вы хотите работать с dp)

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

Обновления на полях в ViewGroup«с 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)

Обновляет относительные поля в ViewGroup's MarginLayoutParams(начало / конец вместо левого / правого).

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)

Следующие свойства расширения удобны для получения текущих полей:

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
  • Использование dpвместо px:

Если вы хотите работать с dp(независимыми от плотности пикселей) вместо px, вам нужно сначала преобразовать их. Вы можете легко сделать это с помощью следующего свойства extension:

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

Затем вы можете вызвать предыдущие функции расширения, такие как:

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

Старый ответ:

В Kotlin вы можете объявить функцию расширения как:

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

Тогда вы можете назвать это как:

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

Или:

myView2.setMargins(topMarginDp = 8) 
Дэвид Мигель
источник