Куда делись отступы при установке фона Drawable?

86

У меня есть эта проблема с моими представлениями EditTextи Button, где у меня есть хороший отступ, чтобы они располагались вдали от текста, но когда я меняю фон с помощью setBackgroundDrawableили setBackgroundResourceэто отступы теряются навсегда.

Дандре Эллисон
источник
2
Эта проблема была исправлена ​​в Android 4.4 KitKat
Владимир
2
На моем Nexus 5 с 4.4.3 эта проблема все еще возникает.
Bianca Daniciuc
1
Я считаю, что это было исправлено только в Lollipop (5.0)
Авив Бен Шабат
У меня была такая же проблема с a, TextViewи принятое ниже решение также работало для этого типа представления.
Stonz2
Имеется проблема с TextViewAPI 28 (Android 9). Похоже, это так и не было исправлено.
Sdghasemi

Ответы:

97

Я обнаружил, что добавление патча 9 в качестве фонового ресурса сбрасывает отступы - хотя, что интересно, если я добавил цветное изображение или изображение патча, отличного от 9, этого не произошло. Решение заключалось в том, чтобы сохранить значения заполнения до добавления фона, а затем снова установить их.

private EditText value = (EditText) findViewById(R.id.value);

int pL = value.getPaddingLeft();
int pT = value.getPaddingTop();
int pR = value.getPaddingRight();
int pB = value.getPaddingBottom();

value.setBackgroundResource(R.drawable.bkg);
value.setPadding(pL, pT, pR, pB);
Мэтт Макминн
источник
Тем, кто сталкивается с некоторыми проблемами при использовании этого подхода, обязательно вызовите getPadding ... ПЕРЕД вызовом setBackgroundResource ().
Chris.Zou
Кажется, этот способ не сохранил бы рисованные отступы?
Thuy Trinh
4
Я хотел бы упомянуть, что вам не нужно делать это выше API 19 (Kitkat), поскольку Google исправил эту ошибку.
ywwynm
@ywwynm выше или равно kitkat?
HendraWD 03 окт.2016,
Хорошо, я сам пробовал, нам не нужно использовать его для API 19 (Kitkat) или выше (> = Kitkat)
HendraWD 03
14

Мне удалось обернуть элемент внутри другого макета, в данном случае - файла FrameLayout. Это позволило мне изменить фон, FrameLayoutне разрушая прокладку, которая находится внутри RelativeLayout.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/commentCell"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:background="@drawable/comment_cell_bg_single" >

    <RelativeLayout android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    android:padding="20dp" >

    <ImageView android:id="@+id/sourcePic"
               android:layout_height="75dp"
               android:layout_width="75dp"
               android:padding="5dp"
               android:background="@drawable/photoframe" 
     />
...

Другой вариант - установить его программно после установки фона, Drawableкак было предложено выше. Просто убедитесь, что вычислили количество пикселей с поправкой на разрешение устройства.

Грязный кальвинист
источник
12

У меня была эта проблема в TextView, поэтому я создал подкласс TextView и создал метод Override для этого TextView.setBackgroundResource(int resid)метода. Как это:

@Override
public void setBackgroundResource(int resid) {
    int pl = getPaddingLeft();
    int pt = getPaddingTop();
    int pr = getPaddingRight();
    int pb = getPaddingBottom();

    super.setBackgroundResource(resid);

    this.setPadding(pl, pt, pr, pb);
}

Таким образом, он получает заполнение элемента до того, как он устанавливает ресурс, но на самом деле не вмешивается в исходную функциональность метода, кроме сохранения заполнения.

Люк Ваггонер
источник
9

Не тестировал это очень тщательно, но этот метод может пригодиться:

    /**
 * Sets the background for a view while preserving its current padding. If the background drawable
 * has its own padding, that padding will be added to the current padding.
 * 
 * @param view View to receive the new background.
 * @param backgroundDrawable Drawable to set as new background.
 */
public static void setBackgroundAndKeepPadding(View view, Drawable backgroundDrawable) {
    Rect drawablePadding = new Rect();
    backgroundDrawable.getPadding(drawablePadding);
    int top = view.getPaddingTop() + drawablePadding.top;
    int left = view.getPaddingLeft() + drawablePadding.left;
    int right = view.getPaddingRight() + drawablePadding.right;
    int bottom = view.getPaddingBottom() + drawablePadding.bottom;

    view.setBackgroundDrawable(backgroundDrawable);
    view.setPadding(left, top, right, bottom);
}

Используйте это вместо view.setBackgroundDrawable (Drawable).

хлопокШарикЛапы
источник
3
Если у представления уже есть отступы с какой-либо стороны, после многократного вызова этой функции фоновое изображение перемещается в сторону, потому что вы увеличиваете заполнение представления с предыдущим значением
iBog
Да, если заполнение будет изменяться несколько раз, вам может потребоваться другой подход. Возможно, отслеживайте исходное заполнение по сравнению с переносимым заполнением.
cottonBallPaws
2

Обратная совместимая версия ответа cottonBallPaws

/** 
  * Sets the background for a view while preserving its current     padding. If the background drawable 
  * has its own padding, that padding will be added to the current padding. 
 *  
 * @param view View to receive the new background. 
 * @param backgroundDrawable Drawable to set as new background. 
 */ 
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@SuppressWarnings("deprecation")
public static void setBackgroundAndKeepPadding(View view, Drawable backgroundDrawable) {
    Rect drawablePadding = new Rect();
    backgroundDrawable.getPadding(drawablePadding);
    int top = view.getPaddingTop() + drawablePadding.top;
    int left = view.getPaddingLeft() + drawablePadding.left;
    int right = view.getPaddingRight() + drawablePadding.right;
    int bottom = view.getPaddingBottom() + drawablePadding.bottom;

    int sdk = android.os.Build.VERSION.SDK_INT;
    if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
        view.setBackgroundDrawable(backgroundDrawable);
    } else {
        view.setBackground(backgroundDrawable);
    }
    view.setPadding(left, top, right, bottom);
}
Эндрю Г
источник
1

Вы можете добавить отступ, используя изображения с 9 патчами и определив область содержимого в Drawable. Проверь это

Вы также можете установить отступы в своем макете из xml или программно

теги заполнения xml

android:padding
android:paddingLeft
android:paddingRight
android:paddingTop
android:paddingBottom

Вы можете попробовать установить заполнение вручную из кода после вызова setBackgroundDrawable, вызвав setPadding в EditText или в представлениях кнопок.

Достичь
источник
У меня есть заполнение, определенное для EditText и Button в XML, и оно отлично работает, пока я не изменю Drawable, а затем я потеряю заполнение.
Дандре Эллисон
Он может измениться в зависимости от изменения областей содержимого между вашими предыдущими и новыми чертежами. Вы пытались увеличить отступ и посмотреть, дает ли он какое-либо заполнение или нет.
achie
1
Хорошо, извините, я должен был полностью прочитать ваш вопрос. Поскольку вы изменяете фоновый рисунок, возможно, происходит удаление отступов. Я не уверен, что это так. Но если это так, вы можете попробовать установить отступы вручную из кода после того, как вы вызовете setBackgroundDrawable, вызвав setPadding в вашем EditText или Button Views
достичь
1

Для обычного поисковика,

просто добавьте setPadding после setBackgroudDrawable. Когда вы меняете свой drawable, вам нужно снова вызвать setPadding.

Подобно:

view.setBackgroundDrawable(backgroundDrawable);
view.setPadding(x, x, x, x);

Самый чистый способ - определить ваши отступы внутри xml-drawable, который указывает на файл drawable-image

Приветствия

Торстен
источник
1

Мое решение состояло в том, чтобы расширить представление (в моем случае EditText ) и переопределить setBackgroundDrawable()и setBackgroundResource()методы:

// Stores padding to avoid padding removed on background change issue
public void storePadding(){
    mPaddingLeft = getPaddingLeft();
    mPaddingBottom = getPaddingTop();
    mPaddingRight = getPaddingRight();
    mPaddingTop = getPaddingBottom();
}

// Restores padding to avoid padding removed on background change issue
private void restorePadding() {
    this.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
}

@Override
public void setBackgroundResource(@DrawableRes int resId) {
    storePadding();
    super.setBackgroundResource(resId);
    restorePadding();
}

@Override
public void setBackgroundDrawable(Drawable background) {
    storePadding();
    super.setBackgroundDrawable(background);
    restorePadding();
}
Хуан Хосе Мелеро Гомес
источник
1

Объединив все решения, я написал одно на Котлине:

fun View.setViewBackgroundWithoutResettingPadding(@DrawableRes backgroundResId: Int) {
    val paddingBottom = this.paddingBottom
    val paddingStart = ViewCompat.getPaddingStart(this)
    val paddingEnd = ViewCompat.getPaddingEnd(this)
    val paddingTop = this.paddingTop
    setBackgroundResource(backgroundResId)
    ViewCompat.setPaddingRelative(this, paddingStart, paddingTop, paddingEnd, paddingBottom)
}

fun View.setViewBackgroundWithoutResettingPadding(background: Drawable?) {
    val paddingBottom = this.paddingBottom
    val paddingStart = ViewCompat.getPaddingStart(this)
    val paddingEnd = ViewCompat.getPaddingEnd(this)
    val paddingTop = this.paddingTop
    ViewCompat.setBackground(this, background)
    ViewCompat.setPaddingRelative(this, paddingStart, paddingTop, paddingEnd, paddingBottom)
}
разработчик Android
источник
1

Просто чтобы объяснить, что происходит:

На самом деле это особенность. Чертежи макета, которые вы можете использовать в качестве фона, могут определять отступы следующим образом:

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:paddingRight="8dp"
    >
    ...
</layer-list>

Этот отступ будет установлен вместе с новым рисованным фоном. Когда нет отступов, значение по умолчанию - 0.

Дополнительная информация из электронного письма, написанного Роменом Гаем: https://www.mail-archive.com/android-developers@googlegroups.com/msg09595.html

Ахраф Амил
источник
0

просто измените lib на v7: 22.1.0 в студии Android, например, скомпилируйте com.android.support:appcompat-v7:22.1.0 '

user3104023
источник
0

Большинство ответов верны, но они должны правильно обрабатывать настройку фона.

Сначала получите отступ вашего представления

//Here my view has the same padding in all directions so I need to get just 1 padding
int padding = myView.getPaddingTop();

Затем установите фон

//If your are supporting lower OS versions make sure to verify the version
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
    //getDrawable was deprecated so use ContextCompat                            
    myView.setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.bg_accent_underlined_white));
} else {
    myView.setBackground(ContextCompat.getDrawable(context, R.drawable.bg_accent_underlined_white));
}

Затем установите отступ, который был у представления до изменения фона.

myView.setPadding(padding, padding, padding, padding);
кутико
источник
0

Нашел другое решение. Я столкнулся с аналогичной проблемой с кнопками. В конце концов, я добавил:

android:scaleX= "0.85"
android:scaleY= "0.85"

у меня это сработало. Заполнение по умолчанию почти такое же.

Салман Сантино
источник
0

В моем случае у меня была возможность рисования, и я был настолько глуп, что не видел, чтобы все отступы были установлены на 0 в xml.

Displee
источник
-1

Здесь улучшенная версия setBackgroundAndKeepPadding от cottonBallPaws. Это поддерживает заполнение, даже если вы вызываете метод несколько раз:

/**
 * Sets the background for a view while preserving its current padding. If the background drawable
 * has its own padding, that padding will be added to the current padding.
 */
public static void setBackgroundAndKeepPadding(View view, Drawable backgroundDrawable) {

    Rect drawablePadding = new Rect();
    backgroundDrawable.getPadding(drawablePadding);

    // Add background padding to view padding and subtract any previous background padding
    Rect prevBackgroundPadding = (Rect) view.getTag(R.id.prev_background_padding);
    int left = view.getPaddingLeft() + drawablePadding.left -
            (prevBackgroundPadding == null ? 0 : prevBackgroundPadding.left);
    int top = view.getPaddingTop() + drawablePadding.top -
            (prevBackgroundPadding == null ? 0 : prevBackgroundPadding.top);
    int right = view.getPaddingRight() + drawablePadding.right -
            (prevBackgroundPadding == null ? 0 : prevBackgroundPadding.right);
    int bottom = view.getPaddingBottom() + drawablePadding.bottom -
            (prevBackgroundPadding == null ? 0 : prevBackgroundPadding.bottom);
    view.setTag(R.id.prev_background_padding, drawablePadding);

    view.setBackgroundDrawable(backgroundDrawable);
    view.setPadding(left, top, right, bottom);
}

Вам необходимо определить идентификатор ресурса через values ​​/ ids.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="prev_background_padding" type="id"/>
</resources>
Оливер Джонас
источник
-1

Я использую это довольно легко обойти я определяю accentColorпо моему , style.xmlкак показано ниже

<item name="colorAccent">#0288D1</item>

а затем я использую один из следующих стилей в своих Buttonтегах

style="@style/Base.Widget.AppCompat.Button.Colored"
style="@style/Base.Widget.AppCompat.Button.Small"

например :

<Button
    android:id="@+id/btnLink"
    style="@style/Base.Widget.AppCompat.Button.Colored"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/tvDescription"
    android:textColor="@color/textColorPrimary"
    android:text="Visit Website" />

<Button
    android:id="@+id/btnSave"
    style="@style/Base.Widget.AppCompat.Button.Small"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/tvDescription"
    android:layout_toRightOf="@id/btnLink"
    android:textColor="@color/textColorPrimaryInverse"
    android:text="Save" />
Анудип Самайя
источник
Это не связано с вопросом.
Miha_x64