93 votes

Android : Comment supprimer la marge/le bourrelet dans l'écran des préférences ?

Je suis confronté à un problème très étrange dans la conception de l'écran des préférences. Bien que je ne donne aucune marge dans la mise en page, il laisse un certain espace à gauche.

Comme vous pouvez le voir dans l'image ci-dessous : enter image description here

XML :

   <PreferenceScreen android:title="demo" >
       <CheckBoxPreference
           android:defaultValue="false"
            android:key="prefSync"`
            android:title="Auto Sync" />
    </PreferenceScreen>

Est-ce que je fais quelque chose de mal en ajoutant une case à cocher de préférence dans l'écran ?

174voto

usr55410 Points 1748

Mise à jour pour androidx.

Après de nombreuses expérimentations, j'ai résolu ce problème en ajoutant ceci à chaque préférence qui avait l'excès d'indentation :

app:iconSpaceReserved="false"

Bien entendu, vous devrez également ajouter cette information à la déclaration PreferenceScreen elle-même, en haut de votre fichier xml :

xmlns:app="http://schemas.android.com/apk/res-auto"

Suivi des préférences personnalisées

J'ai remarqué que dans le cas des préférences personnalisées, notamment sur les appareils plus anciens, cette solution ne fonctionnait pas toujours. Par exemple, une préférence comme celle-ci pouvait toujours être mise en retrait :

com.example.preference.SomeCustomPreference
    android:id="@+id/some_custom_preference"
    android:name="Custom Preference"
    android:key="@string/custom_pref_key"
    android:summary="@string/custom_pref_summary"
    android:title="@string/preference_custom_title"
    app:iconSpaceReserved="false"

Le problème est lié à l'utilisation du troisième paramètre du constructeur dans la classe de préférence personnalisée. Si vous passez ce troisième paramètre, l'élément de liste sera indenté. Si vous l'omettez, la liste sera alignée correctement :

class SomeCustomPreference
@JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = android.R.attr.someCustomPreferenceStyle
) : DialogPreference(context, attrs, defStyle) {

    override fun getDialogLayoutResource(): Int {
        return R.layout.my_layout
    }
}

Au lieu de cela, utilisez ceci :

class SomeCustomPreference
@JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : DialogPreference(context, attrs) {

    override fun getDialogLayoutResource(): Int {
        return R.layout.my_layout
    }
}

Le mérite en revient à @CommonsWare dans ce très vieux poste .

54voto

android developer Points 20939

J'ai signalé ce problème aquí (vous pouvez également consulter mon projet de contournement), mais pour l'instant, j'ai trouvé un moyen un peu bricolé, mais facile, de surmonter ce problème :

  • Pour PreferenceCategory j'ai réglé le padding de début/gauche de ses mises en page sur 0.

  • Pour les autres types de préférences, je choisis de masquer le icon_frame au cas où ils n'auraient pas d'icône.

Voici le code. Il suffit d'étendre cette classe et le reste est automatique :

Kotlin

abstract class BasePreferenceFragment : PreferenceFragmentCompat() {

    override fun onCreateAdapter(preferenceScreen: PreferenceScreen?): RecyclerView.Adapter<*> {
        return object : PreferenceGroupAdapter(preferenceScreen) {
            override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
                super.onBindViewHolder(holder, position)
                val preference = getItem(position)
                if (preference is PreferenceCategory)
                    setZeroPaddingToLayoutChildren(holder.itemView)
                else
                    holder.itemView.findViewById<View?>(R.id.icon_frame)?.visibility = if (preference.icon == null) View.GONE else View.VISIBLE
            }
        }
    }

    private fun setZeroPaddingToLayoutChildren(view: View) {
        if (view !is ViewGroup)
            return
        val childCount = view.childCount
        for (i in 0 until childCount) {
            setZeroPaddingToLayoutChildren(view.getChildAt(i))
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
                view.setPaddingRelative(0, view.paddingTop, view.paddingEnd, view.paddingBottom)
            else
                view.setPadding(0, view.paddingTop, view.paddingRight, view.paddingBottom)
        }
    }
}

Java

public abstract class BasePreferenceFragmentCompat extends PreferenceFragmentCompat {
    @Override
    protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
        return new PreferenceGroupAdapter(preferenceScreen) {
            @SuppressLint("RestrictedApi")
            @Override
            public void onBindViewHolder(PreferenceViewHolder holder, int position) {
                super.onBindViewHolder(holder, position);
                Preference preference = getItem(position);
                if (preference instanceof PreferenceCategory)
                    setZeroPaddingToLayoutChildren(holder.itemView);
                else {
                    View iconFrame = holder.itemView.findViewById(R.id.icon_frame);
                    if (iconFrame != null) {
                        iconFrame.setVisibility(preference.getIcon() == null ? View.GONE : View.VISIBLE);
                    }
                }
            }
        };
    }

    private void setZeroPaddingToLayoutChildren(View view) {
        if (!(view instanceof ViewGroup))
            return;
        ViewGroup viewGroup = (ViewGroup) view;
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
            setZeroPaddingToLayoutChildren(viewGroup.getChildAt(i));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
                viewGroup.setPaddingRelative(0, viewGroup.getPaddingTop(), viewGroup.getPaddingEnd(), viewGroup.getPaddingBottom());
            else
                viewGroup.setPadding(0, viewGroup.getPaddingTop(), viewGroup.getPaddingRight(), viewGroup.getPaddingBottom());
        }
    }
}

Et le résultat (échantillon XML) peut être trouvé aquí de cet échantillon Google que j'ai créé aquí à vérifier) :

enter image description here

Ce code est un peu dangereux, aussi assurez-vous qu'à chaque mise à jour de la bibliothèque, vous vérifiez qu'il fonctionne bien.

En outre, elle peut ne pas fonctionner correctement dans certains cas particuliers, par exemple lorsque vous définissez android:layout de votre propre préférence, vous devrez donc le modifier pour cette question.


J'ai une meilleure solution, plus officielle :

Pour chaque préférence, utilisez app:iconSpaceReserved="false" . Cela devrait fonctionner correctement, mais pour une raison quelconque, il y a un bogue (connu) qui fait que cela ne fonctionne pas pour PreferenceCategory. Il a été signalé aquí et devrait être corrigé dans un avenir proche.

Pour l'instant, vous pouvez donc utiliser une version mixte de la solution de contournement que j'ai écrite et de ce drapeau.


EDIT : J'ai trouvé une autre solution. Celle-ci permet de passer outre toutes les préférences, et de définir isIconSpaceReserved pour chacun. Malheureusement, comme je l'ai écrit ci-dessus, si vous utilisez PreferenceCategory, cela gâche tout, mais cela devrait fonctionner correctement si vous ne l'utilisez pas :

Kotlin

abstract class BasePreferenceFragment : PreferenceFragmentCompat() {

    override fun setPreferenceScreen(preferenceScreen: PreferenceScreen?) {
        super.setPreferenceScreen(preferenceScreen)
        if (preferenceScreen != null) {
            val count = preferenceScreen.preferenceCount
            for (i in 0 until count)
                preferenceScreen.getPreference(i)!!.isIconSpaceReserved = false
        }
    }

Java

public class BasePreferenceFragment extends PreferenceFragmentCompat {

    @Override
    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
        super.setPreferenceScreen(preferenceScreen);
        if (preferenceScreen != null) {
            int count = preferenceScreen.getPreferenceCount();
            for (int i = 0; i < count; i++)
                preferenceScreen.getPreference(i).setIconSpaceReserved(false);
        }
    }
}

EDIT : après que Google ait finalement corrigé la bibliothèque (lien aquí ), vous pouvez définir le drapeau pour chaque préférence, ou utiliser cette solution qui le fait pour tous, pour vous :

abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
    private fun setAllPreferencesToAvoidHavingExtraSpace(preference: Preference) {
        preference.isIconSpaceReserved = false
        if (preference is PreferenceGroup)
            for (i in 0 until preference.preferenceCount)
                setAllPreferencesToAvoidHavingExtraSpace(preference.getPreference(i))
    }

    override fun setPreferenceScreen(preferenceScreen: PreferenceScreen?) {
        if (preferenceScreen != null)
            setAllPreferencesToAvoidHavingExtraSpace(preferenceScreen)
        super.setPreferenceScreen(preferenceScreen)

    }

    override fun onCreateAdapter(preferenceScreen: PreferenceScreen?): RecyclerView.Adapter<*> =
            object : PreferenceGroupAdapter(preferenceScreen) {
                @SuppressLint("RestrictedApi")
                override fun onPreferenceHierarchyChange(preference: Preference?) {
                    if (preference != null)
                        setAllPreferencesToAvoidHavingExtraSpace(preference)
                    super.onPreferenceHierarchyChange(preference)
                }
            }
}

Il suffit de s'en éloigner, et vous n'aurez pas de rembourrage inutile pour vos préférences. Exemple de projet aquí Je demande également qu'il existe un moyen officiel de l'éviter, au lieu de ces astuces. S'il vous plaît envisager de l'étoiler.

36voto

t0m Points 931

Une solution simple et efficace de aquí .
Il fonctionne dans toutes les préférences sans avoir besoin d'écrire à toutes les préférences app:iconSpaceReserved="false"

Créer res/values-sw360dp/values-preference.xml :

<resources xmlns:tools="http://schemas.android.com/tools">
    <bool name="config_materialPreferenceIconSpaceReserved" tools:ignore="MissingDefaultResource,PrivateResource">false</bool>
    <dimen name="preference_category_padding_start" tools:ignore="MissingDefaultResource,PrivateResource">0dp</dimen>
</resources>

En <bool> fixe la valeur par défaut de iconSpacePreserved pour tous Preference ; Le <dimen> fixe la PreferenceCategory.

EDIT : Si vous utilisez androidx.preference:preference:1.1.1 vous n'aurez pas besoin des dimensions preference_category_padding_start . Testé sur Android 6-10.

7voto

Sanket Shah Points 4448

Essayez ça :

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View v = super.onCreateView(inflater, container, savedInstanceState);
    if(v != null) {
        ListView lv = (ListView) v.findViewById(android.R.id.list);
        lv.setPadding(10, 10, 10, 10);
    }
    return v;
}

Vous pouvez définir le rembourrage en utilisant : setPadding();

5voto

mtrewartha Points 569

Si vous utilisez le com.android.support:preference-v7 bibliothèque, assurez-vous que le thème de l'activité qui héberge vos préférences dispose d'une preferenceTheme défini sur la superposition du thème des préférences matérielles de la v14 :

<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X