Où est passé le rembourrage lors de la définition de fond Drawable?

86

J'ai ce problème sur mes vues EditTextet Button, où j'ai un joli rembourrage pour qu'ils s'éloignent du texte, mais lorsque je change l'arrière-plan avec setBackgroundDrawableou, setBackgroundResourcece remplissage est perdu à jamais.

Dandre Allison
la source
2
Ce problème a été résolu dans Android 4.4 KitKat
Vladimir
2
Sur mon Nexus 5 avec 4.4.3, ce problème persiste.
Bianca Daniciuc
1
Je crois que cela n'a été corrigé que dans Lollipop (5.0)
Aviv Ben Shabat
J'avais le même problème avec un TextViewet la solution acceptée ci-dessous fonctionnait également pour ce type de vue.
Stonz2
Avoir le problème sur une TextViewAPI 28 (Android 9) également. On dirait que ça n'a jamais été corrigé.
Sdghasemi

Réponses:

97

Ce que j'ai trouvé, c'est l'ajout d'un patch 9 en tant que ressource d'arrière-plan pour réinitialiser le rembourrage - bien que ce soit intéressant si j'ai ajouté une image de couleur ou non-9, ce n'est pas le cas. La solution consistait à enregistrer les valeurs de remplissage avant l'ajout de l'arrière-plan, puis à les redéfinir par la suite.

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
la source
Pour tous ceux qui rencontrent des problèmes avec cette approche, assurez-vous d'appeler getPadding ... AVANT d'appeler setBackgroundResource ().
Chris.Zou
Il semble que cette façon de ne pas garder le rembourrage dessiné?
Thuy Trinh
4
Je tiens à mentionner que vous ne devez pas faire cela ci-dessus API 19 (Kitkat) puisque Google a résolu ce bogue.
ywwynm
@ywwynm ci-dessus ou équivaut à kitkat?
HendraWD
OK, je l'ai essayé moi-même, nous n'avons pas besoin de l'utiliser pour l'API 19 (Kitkat) ou supérieur (> = Kitkat)
HendraWD
14

J'ai pu envelopper l'élément dans une autre mise en page, dans ce cas, un FrameLayout. Cela m'a permis de changer l'arrière-plan sur le FrameLayoutsans détruire le rembourrage, qui est sur le contenu 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" 
     />
...

L'autre option consiste à le définir par programme après avoir défini l'arrière-plan Drawablecomme suggéré ci-dessus. Assurez-vous simplement de calculer les pixels pour corriger la résolution de l'appareil.

Le sale calviniste
la source
12

J'ai eu ce problème dans un TextView, j'ai donc sous-classé TextView et créé une méthode Override de la TextView.setBackgroundResource(int resid)méthode. Comme ça:

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

De cette façon, il obtient le remplissage de l'élément avant de définir la ressource, mais ne gâche pas réellement la fonctionnalité d'origine de la méthode, autre que le maintien du remplissage.

LukeWaggoner
la source
9

Je n'ai pas testé ce super minutieusement, mais cette méthode pourrait être utile:

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

Utilisez ceci au lieu de view.setBackgroundDrawable (Drawable).

cotonBallPaws
la source
3
Si la vue a déjà un remplissage sur un côté, après avoir appelé cette fonction plusieurs fois, l'image d'arrière-plan se déplace sur le côté, car vous incrémentez le remplissage de la vue avec la valeur précédente
iBog
Oui, si le rembourrage doit être changé plusieurs fois, vous voudrez adopter une approche différente. Gardez éventuellement une trace du rembourrage d'origine par rapport au rembourrage pouvant être dessiné.
cottonBallPaws
2

Version rétrocompatible de la réponse de 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
la source
1

Vous pouvez donner un peu de remplissage en utilisant des images à 9 patchs et en définissant la zone de contenu dans le dessin. Vérifie ça

Vous pouvez également définir le remplissage dans votre mise en page à partir de xml ou par programme

balises de remplissage xml

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

Vous pouvez essayer de définir le remplissage manuellement à partir du code après avoir appelé setBackgroundDrawable en appelant setPadding sur votre EditText ou Button Views

réaliser
la source
J'ai un rembourrage défini pour le EditText et le Button en XML, et cela se voit très bien, jusqu'à ce que je change le Drawable, puis je perds le rembourrage.
Dandre Allison
Cela peut changer en fonction du changement dans les zones de contenu entre vos anciens et nouveaux drawables. Avez-vous essayé d'augmenter le rembourrage et voir si cela donne du rembourrage ou non.
atteindre le
1
Ok désolé, j'aurais dû lire votre question complètement. Puisque vous modifiez le backgrounddrawable, il se peut qu'il supprime le remplissage. Je ne suis pas sûr que ce soit le cas. Mais si tel est le cas, vous pouvez essayer de définir le remplissage manuellement à partir du code après avoir appelé setBackgroundDrawable en appelant setPadding sur votre EditText ou Button Views
atteindre le
1

Pour le chercheur commun,

ajoutez simplement setPadding après setBackgroudDrawable. Lorsque vous modifiez votre dessin, vous devez à nouveau appeler setPadding.

Comme:

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

Le moyen le plus propre est de définir vos rembourrages dans un xml-drawable qui pointe vers le drawable-image-file

Grands

Torsten
la source
1

Ma solution était d'étendre la vue (dans mon cas un EditText ) et de remplacer setBackgroundDrawable()et de setBackgroundResource()méthodes:

// 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
la source
1

En combinant les solutions de toutes, j'en ai écrit une à 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)
}
développeur android
la source
1

Juste pour expliquer ce qui se passe:

C'est en fait une fonctionnalité. Les dessinables de mise en page que vous pouvez utiliser comme arrière-plans peuvent définir un remplissage de cette façon:

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

Ce remplissage sera défini avec le nouvel arrière-plan dessinable. Lorsqu'il n'y a pas de remplissage, la valeur par défaut est 0.

Plus d'infos sur un email rédigé par Romain Guy: https://www.mail-archive.com/[email protected]/msg09595.html

Achraf Amil
la source
0

il suffit de changer lib en v7: 22.1.0 dans le studio Android comme cette compilation 'com.android.support:appcompat-v7:22.1.0'

user3104023
la source
0

La plupart des réponses sont correctes mais doivent gérer correctement le paramètre d'arrière-plan.

Obtenez d'abord le remplissage de votre vue

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

Puis définissez l'arrière-plan

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

Ensuite, définissez le remplissage de la vue avant le changement d'arrière-plan

myView.setPadding(padding, padding, padding, padding);
Cutiko
la source
0

J'ai trouvé une autre solution. J'étais confronté au problème similaire avec les boutons. Finalement, j'ai ajouté:

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

cela a fonctionné pour moi. Le remplissage par défaut est presque le même.

Salman Santino
la source
0

Dans mon cas, j'avais un dessinable et j'étais tellement stupide que je n'ai pas vu que les rembourrages étaient tous mis à 0 dans le xml.

Displee
la source
-1

Voici une version améliorée de setBackgroundAndKeepPadding de cottonBallPaws. Cela maintient le remplissage même si vous appelez la méthode plusieurs fois:

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

Vous devez définir un identifiant de ressource via values ​​/ ids.xml:

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

J'utilise cette solution de contournement assez simple, je définis un accentColordans mon style.xmlcomme ci-dessous

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

puis j'utilise l'un des styles suivants dans mes Buttonbalises

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

par exemple :

<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
la source
Ceci n'est pas lié à la question.
Miha_x64