Para onde vai o preenchimento ao definir o Drawable de fundo?

86

Eu tenho esse problema nas visualizações EditTexte Button, onde tenho um bom preenchimento para eles se afastarem do texto, mas quando eu mudo o plano de fundo com setBackgroundDrawableou setBackgroundResourceesse preenchimento é perdido para sempre.

Dandre Allison
fonte
2
Este problema foi corrigido no Android 4.4 KitKat
Vladimir,
2
No meu Nexus 5 com 4.4.3, esse problema ainda está acontecendo.
Bianca Daniciuc
1
Eu acredito que foi corrigido apenas no Lollipop (5.0)
Aviv Ben Shabat
Eu estava tendo o mesmo problema com a TextViewe a solução aceita abaixo também funcionou para esse tipo de visão.
Stonz2
TextViewTambém tenho o problema em um API 28 (Android 9). Parece que nunca foi corrigido.
Sdghasemi

Respostas:

97

O que eu descobri foi adicionar um patch 9 como recurso de fundo para redefinir o preenchimento - embora, curiosamente, se eu adicionasse uma cor ou imagem de patch diferente de 9, isso não acontecesse. A solução era salvar os valores de preenchimento antes que o fundo fosse adicionado e, em seguida, configurá-los novamente.

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);
Matt McMinn
fonte
Para qualquer um que tenha problemas ao usar essa abordagem, certifique-se de chamar getPadding ... ANTES de chamar setBackgroundResource ().
Chris.Zou
Parece que desta forma não manteria o enchimento drawable?
Thuy Trinh
4
Gostaria de mencionar que você não precisa fazer isso acima da API 19 (Kitkat), pois o Google resolveu esse bug.
ywwynm
@ywwynm acima ou igual a kitkat?
HendraWD
OK, eu tentei sozinho, não precisamos usá-lo para API 19 (Kitkat) ou superior (> = Kitkat)
HendraWD
14

Consegui envolver o elemento dentro de outro layout, neste caso, a FrameLayout. Isso me permitiu alterar o fundo do FrameLayoutsem destruir o preenchimento, que está no contido 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" 
     />
...

A outra opção é defini-lo programaticamente após definir o plano de fundo Drawablecomo sugerido acima. Apenas certifique-se de calcular os pixels para corrigir a resolução do dispositivo.

O calvinista sujo
fonte
12

Eu tive esse problema em um TextView, então criei uma subclasse de TextView e criei um método Override do TextView.setBackgroundResource(int resid)método. Como isso:

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

Dessa forma, ele obtém o preenchimento do item antes de definir o recurso, mas na verdade não mexe com a funcionalidade original do método, exceto manter o preenchimento.

Luke Waggoner
fonte
9

Não testei muito bem, mas este método pode ser útil:

    /**
 * 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);
}

Use isso em vez de view.setBackgroundDrawable (Drawable).

CottonBallPaws
fonte
3
Se a visualização já tiver preenchimento em algum lado, depois de chamar essa função várias vezes, a imagem de fundo se move para o lado, porque você incrementa o preenchimento da visualização com o valor anterior
iBog
Sim, se o preenchimento for alterado várias vezes, você deve usar uma abordagem diferente. Possivelmente, acompanhe o preenchimento original em relação ao preenchimento drawable.
cottonBallPaws
2

Versão compatível com versões anteriores da resposta do 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);
}
Andrew G
fonte
1

Você pode fornecer algum preenchimento usando imagens de 9 patch e definindo a área de conteúdo no drawable. Verifique isto

Você também pode definir o preenchimento em seu layout de xml ou programaticamente

tags de preenchimento xml

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

Você pode tentar definir o preenchimento manualmente a partir do código depois de chamar setBackgroundDrawable chamando setPadding em seu EditText ou em Visualizações de botão

achie
fonte
Eu tenho padding definido para EditText e Button em XML, e ele mostra muito bem, até eu mudar o Drawable, aí eu perco o padding.
Dandre Allison,
Pode mudar dependendo da mudança nas áreas de conteúdo entre seus drawables anteriores e novos. Você tentou aumentar o preenchimento e ver se está dando algum preenchimento ou não.
achie de
1
Ok, desculpe, eu deveria ter lido sua pergunta completamente. Como você está alterando o desenho de fundo, pode ser que o preenchimento seja removido. Não tenho certeza se é o caso. Mas se for esse o caso, você pode tentar definir o preenchimento manualmente a partir do código depois de chamar o setBackgroundDrawable chamando setPadding em seu EditText ou em Visualizações de botão
achie
1

Para o pesquisador comum,

basta adicionar setPadding após setBackgroudDrawable. Ao alterar seu drawable, você deve chamar setPadding novamente.

Gostar:

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

A maneira mais limpa é definir seus preenchimentos dentro de um xml-drawable que aponta para o arquivo drawable-image

Greatings

Torsten
fonte
1

Minha solução foi estender a vista (no meu caso um EditText ) e override setBackgroundDrawable()e setBackgroundResource()métodos:

// 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();
}
Juan José Melero Gómez
fonte
1

Combinando as soluções de todas, escrevi uma em Kotlin:

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)
}
desenvolvedor android
fonte
1

Só para explicar o que está acontecendo:

Na verdade, é um recurso. Drawables de layout que você pode usar como plano de fundo podem definir um preenchimento desta forma:

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

Esse preenchimento será definido junto com o novo plano de fundo drawable. Quando não há preenchimento, o valor padrão é 0.

Mais informações em um e-mail escrito por Romain Guy: https://www.mail-archive.com/[email protected]/msg09595.html

Achraf Amil
fonte
0

apenas altere lib para v7: 22.1.0 no android studio como este compilar 'com.android.support:appcompat-v7:22.1.0'

user3104023
fonte
0

A maioria das respostas está correta, mas deve lidar com a configuração de fundo corretamente.

Primeiro obtenha o preenchimento de sua visão

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

Em seguida, defina o fundo

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

Em seguida, defina o preenchimento que a vista tinha antes da mudança de fundo

myView.setPadding(padding, padding, padding, padding);
cutiko
fonte
0

Encontrei outra solução. Eu estava enfrentando um problema semelhante com os botões. Eventualmente, eu adicionei:

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

funcionou para mim. O preenchimento padrão é quase o mesmo.

Salman Santino
fonte
0

No meu caso, eu tinha um drawable e era tão estúpido que não vi que os preenchimentos estavam todos configurados para 0 no xml.

Displee
fonte
-1

Aqui, uma versão aprimorada do setBackgroundAndKeepPadding do cottonBallPaws. Isso mantém o preenchimento, mesmo se você chamar o método várias vezes:

/**
 * 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);
}

Você precisa definir um id de recurso via values ​​/ ids.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="prev_background_padding" type="id"/>
</resources>
Oliver Jonas
fonte
-1

Eu uso esta solução bastante fácil, eu defino um accentColorno meu style.xmlgosto abaixo

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

e eu uso um dos seguintes estilos em minhas Buttontags

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

por exemplo :

<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" />
Anudeep Samaiya
fonte
Isso não está relacionado à pergunta.
Miha_x64