web-dev-qa-db-fra.com

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

Je suis confronté à un problème très étrange dans la conception d'écran de préférence. Bien que je ne donne aucune marge dans la mise en page, cela laisse un peu d’espace dans la 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 des préférences de case à cocher à l'écran? 

31
Dory

J'ai signalé ce problème ici (vous pouvez également consulter mon projet de solution de contournement ici), mais pour le moment, j'ai trouvé un moyen un peu compliqué de résoudre ce problème: 

  • Pour PreferenceCategory, je règle le remplissage de début/de 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. Étendez juste de 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 (un exemple XML peut être trouvé ici de cet exemple Google , que j'ai créé ici à vérifier):

 enter image description here

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

En outre, cela peut ne pas fonctionner correctement dans certains cas particuliers, comme lorsque vous définissez vous-même le Android:layout pour la préférence, de sorte que vous devrez le modifier à cet égard.


Vous avez une meilleure solution, plus officielle:

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

Donc, pour l'instant, vous pouvez utiliser une version mixte de la solution de contournement que j'ai écrite et de cet indicateur.


EDIT: trouvé encore une autre solution. Celui-ci dépassera toutes les préférences et définira isIconSpaceReserved pour chacune. Malheureusement, comme je l'ai écrit ci-dessus, si vous utilisez PreferenceCategory, cela ruine 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: une fois que Google a finalement fixé la bibliothèque (link here ), 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)
                }
            }
}

Étendez-vous, et vous n'aurez pas le rembourrage inutile pour vos préférences. Exemple de projet ici , où je demande également un moyen officiel de l'éviter, au lieu de ces astuces. S'il vous plaît envisager de le mettre en vedette.

36
android developer

Mise à jour pour Android.

Après beaucoup d'expérimentation, j'ai résolu ce problème en ajoutant ceci à chaque préférence avec l'indentation excédentaire:

app:iconSpaceReserved="false"

Bien sûr, vous devrez également ajouter ceci à la déclaration PreferenceScreen elle-même en haut de votre code xml:

xmlns:app="http://schemas.Android.com/apk/res-auto"
26
Alex

Essaye ç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();

6
Andrain

Solution de travail simple de ici :

Créer res/values-sw360dp-v13/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>

Le <bool> corrige la valeur par défaut de iconSpacePreserved pour tout Preference; Le <dimen> corrige la PreferenceCategory.

5
t0m

Si vous utilisez la bibliothèque com.Android.support:preference-v7, assurez-vous que le thème de l'activité hébergeant vos préférences est défini sur preferenceTheme avec la superposition de thèmes de préférence de matière v14:

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

4
mtrewartha

Malheureusement, rien d’en haut n’a fonctionné pour moi. 

Réponse courte :

J'ai résolu ce problème en utilisant un marges négatives sur le conteneur le plus élevé de la présentation des préférences personnalisées.

Pas:

  • Créez une mise en page personnalisée pour la préférence (par exemple, preference_category.xml)
  • spécifiez que votre préférence utilise la présentation personnalisée en ajoutant Android: layout param à une balise de préférence au format XML
  • Appliquer des marges négatives

Réponse détaillée:

La préférence XML: 

    <Android.support.v7.preference.PreferenceScreen
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:title="@string/settings_title" >

    <Android.support.v7.preference.PreferenceCategory
        Android:layout="@layout/preference_category"
        Android:key="settings_list"
        Android:title="@string/pref_category_settings" />
    <Android.support.v7.preference.SwitchPreferenceCompat
        Android:layout="@layout/preference_row"
        Android:icon="@drawable/ic_nav_switch"
        Android:key="pref_switch"
        Android:title="@string/pref_switch_title" />

    </Android.support.v7.preference.PreferenceScreen>

La disposition personnalisée pour la ligne de préférence qui supprime les marges gauche et droite inutiles:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_marginStart="-12dp"
    Android:layout_marginEnd="-8dp"
    Android:minHeight="?android:attr/listPreferredItemHeight"
    Android:gravity="center_vertical">

    <RelativeLayout
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content">

        <ImageView
            Android:id="@Android:id/icon"
            Android:layout_width="58dp"
            Android:layout_height="58dp"
            Android:padding="8dp"
            Android:layout_gravity="center"
            Android:visibility="visible" />

    </RelativeLayout>

    <RelativeLayout
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginStart="15dp"
        Android:layout_marginEnd="6dp"
        Android:layout_marginTop="6dp"
        Android:layout_marginBottom="6dp"
        Android:layout_weight="1">

        <TextView
            Android:id="@Android:id/title"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:singleLine="true"
            Android:textAppearance="?android:attr/textAppearanceListItem"
            Android:ellipsize="Marquee"
            Android:fadingEdge="horizontal" />

        <TextView
            Android:id="@Android:id/summary"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_below="@Android:id/title"
            Android:layout_alignStart="@Android:id/title"
            Android:textAppearance="?android:attr/textAppearanceListItemSecondary"
            Android:textColor="?android:attr/textColorSecondary"
            Android:maxLines="2"/>

    </RelativeLayout>

    <LinearLayout
        Android:id="@Android:id/widget_frame"
        Android:layout_width="wrap_content"
        Android:layout_height="match_parent"
        Android:gravity="end|center_vertical"
        Android:orientation="vertical">

    </LinearLayout>
</LinearLayout>

La disposition personnalisée pour la catégorie de préférence qui supprime les marges gauche et droite inutiles:

<TextView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:textColor="@color/colorAccent"
    Android:textStyle="bold"
    Android:textSize="12sp"
    Android:gravity="center_vertical"
    Android:textAllCaps="true"
    Android:layout_marginStart="-4dp"
    Android:layout_marginEnd="-8dp"
    Android:id="@+Android:id/title" />
3
Gord

Le remplissage est provoqué par le parent ListView dans la PreferenceScreen contenant vos paramètres. Si vous utilisez une PreferenceFragment pour créer vos préférences, accédez simplement à la fonction onActivityCreated de la PreferenceFragement et procédez comme suit pour supprimer le remplissage:

public void onActivityCreated(Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);

  View lv = getView().findViewById(Android.R.id.list);
  if (lv != null) lv.setPadding(0, 0, 0, 0);
}
3
Helmut Schottmüller

Changer le thème de préférence comme ça. Assurez-vous d'utiliser la dernière version de la bibliothèque de préférences androidx.preference 1.1.0-alpha01

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="preferenceTheme">@style/CustomPreferenceTheme</item>
</style>

<style name="CustomPreferenceTheme" parent="@style/PreferenceThemeOverlay">
    <item name="preferenceFragmentCompatStyle">@style/CustomPreferenceFragmentCompatStyle</item>
    <item name="preferenceCategoryStyle">@style/CustomPreferenceCategory</item>
    <item name="preferenceStyle">@style/CustomPreference</item>
    <item name="checkBoxPreferenceStyle">@style/CustomCheckBoxPreference</item>
    <item name="dialogPreferenceStyle">@style/CustomDialogPreference</item>
    <item name="switchPreferenceCompatStyle">@style/CustomSwitchPreferenceCompat</item>  <!-- for pre Lollipop(v21) -->
    <item name="switchPreferenceStyle">@style/CustomSwitchPreference</item>
</style>

 <style name="CustomPreferenceFragmentCompatStyle" parent="@style/PreferenceFragment.Material">
    <item name="Android:layout">@layout/fragment_settings</item>
</style>

<style name="CustomPreferenceCategory" parent="Preference.Category.Material">
    <item name="iconSpaceReserved">false</item>
</style>

<style name="CustomPreference" parent="Preference.Material">
    <item name="iconSpaceReserved">false</item>
</style>

<style name="CustomCheckBoxPreference" parent="Preference.CheckBoxPreference.Material">
    <item name="iconSpaceReserved">false</item>
</style>

<style name="CustomDialogPreference" parent="Preference.DialogPreference.Material">
    <item name="iconSpaceReserved">false</item>
</style>

<style name="CustomSwitchPreferenceCompat" parent="Preference.SwitchPreferenceCompat.Material">
    <item name="iconSpaceReserved">false</item>
</style>

<style name="CustomSwitchPreference" parent="Preference.SwitchPreference.Material">
    <item name="iconSpaceReserved">false</item>
</style>
2
Libin

compiler 'com.Android.support:preference-v7:24.2.1'

Utiliser PreferenceFragmentCompat peut faire ceci:

1.Définissez un PreferenceGroupAdapter:

    static class CustomAdapter extends PreferenceGroupAdapter {

    public CustomAdapter(PreferenceGroup preferenceGroup) {
      super(preferenceGroup);
    }

    @Override
    public PreferenceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
      PreferenceViewHolder preferenceViewHolder = super.onCreateViewHolder(parent, viewType);
      parent.setPadding(0, 0, 0, 0);
      preferenceViewHolder.itemView.setPadding(20, 5, 20, 5);
      return preferenceViewHolder;
    }
  }

2.La méthode onCreateAdapter de PreferenceFragmentCompat de PreferenceFragmentCompat:

   @Override
   protected Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {

      return new CustomAdapter(preferenceScreen);
   }

Utiliser style.xml peut faire ceci:

1.values ​​/ styles.xml:

  <style name="customPreferenceThemeOverlay" parent="@style/PreferenceThemeOverlay">
    <item name="preferenceFragmentListStyle">@style/customPreferenceFragmentList</item>
  </style>
  <style name="customPreferenceFragmentList">
    <item name="Android:paddingLeft">0dp</item>
    <item name="Android:paddingRight">0dp</item>
  </style>

2.values-v17/styles.xml:

  <style name="customPreferenceFragmentList">
    <item name="Android:paddingLeft">0dp</item>
    <item name="Android:paddingRight">0dp</item>
    <item name="Android:paddingStart">0dp</item>
    <item name="Android:paddingEnd">0dp</item>
  </style>

3.value/styles.xml:

  <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    ... ...
    <item name="preferenceTheme">@style/customPreferenceThemeOverlay</item>
  </style>
1
zonda

Aucune des réponses ci-dessus n'a fonctionné, je devais copier la mise en page préférence , créer un nouveau fichier de mise en page tel que custom_preference.xml, remplacer le contenu de la mise en page en définissant la visibilité du cadre de l'icône sur GONE . Et ensuite, dans le fichier pref.xml, définissez la disposition pour que la préférence utilise cette disposition personnalisée.

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    >
    <Android.support.v7.preference.EditTextPreference
        Android:title="@string/pref_label"
        Android:hint="@string/pref_hint"
        Android:key="@string/pref_key"
        Android:inputType="text"
        Android:singleLine="true"
        Android:layout="@layout/custom_preference_layout"
        />
</PreferenceScreen>
1
s-hunter

J'essaie d'utiliser 

Android:icon="@null"

avec CheckBoxPreference dans Android 4.4, mais je ne peux pas utiliser le curseur de gauche . J'ai dupé pour utiliser une petite icône transparente et ajouter 

Android:icon="@drawable/transparent_icon" 

et cela semble fonctionner pour moi. J'espère que ça peut aider. Merci.

0
Bao Doan

Le 'remplissage' est lancé dans PreferenceFrameLayout.Java:

public PreferenceFrameLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    TypedArray a = context.obtainStyledAttributes(attrs,
            com.Android.internal.R.styleable.PreferenceFrameLayout, defStyle, 0);

    float density = context.getResources().getDisplayMetrics().density;
    int defaultBorderTop = (int) (density * DEFAULT_BORDER_TOP + 0.5f);
    int defaultBottomPadding = (int) (density * DEFAULT_BORDER_BOTTOM + 0.5f);
    int defaultLeftPadding = (int) (density * DEFAULT_BORDER_LEFT + 0.5f);
    int defaultRightPadding = (int) (density * DEFAULT_BORDER_RIGHT + 0.5f);

    mBorderTop = a.getDimensionPixelSize(
            com.Android.internal.R.styleable.PreferenceFrameLayout_borderTop,
            defaultBorderTop);
    mBorderBottom = a.getDimensionPixelSize(
            com.Android.internal.R.styleable.PreferenceFrameLayout_borderBottom,
            defaultBottomPadding);
    mBorderLeft = a.getDimensionPixelSize(
            com.Android.internal.R.styleable.PreferenceFrameLayout_borderLeft,
            defaultLeftPadding);
    mBorderRight = a.getDimensionPixelSize(
            com.Android.internal.R.styleable.PreferenceFrameLayout_borderRight,
            defaultRightPadding);

    a.recycle();
}

Si vous savez comment remplacer les styles définis par Android, vous pouvez probablement résoudre ce problème. Ce n'est pas très simple cependant.

0
jayeffkay

J'ai posé la question il y a longtemps, mais ce qui m'a aidé à résoudre ce problème, ce sont les marges négatives, aucun besoin de fichiers personnalisés.

Lorsque vous créez votre classe SettingsActivity (qui contient le composant PreferenceFragment), vous définissez simplement sa présentation comme un fragment et ajoutez une marge négative.

settings_activity:

<?xml version="1.0" encoding="utf-8"?>
<fragment
    Android:layout_marginLeft="-40dp"
    Android:name="com.example.USER.APPNAME.SettingsActivity$PrefsFragment"
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context="com.example.USER.APPNAME.SettingsActivity">
</fragment>
0
Guy

Je ne sais pas s'il est trop tard pour une réponse, mais aujourd'hui, j'avais le même problème et rien de cela ne m'aide.

J'ai donc utilisé cette solution:

Dans ma DisclaimerPreference ce extends Preference, je survoler la méthode onBindView(View view) de cette façon:

    @Override protected void onBindView(View view) {
        super.onBindView(view);
        if(view instanceof ViewGroup){
            ViewGroup vg = (ViewGroup)view;
            View currView;
            for(int i=0; i<vg.getChildCount(); i++){
               currView = vg.getChildAt(i);
               if (currView instanceof RelativeLayout)
                   currView.setVisibility(View.GONE);
            }
         }
    }

La clé consiste à définir la RelativeLayout de la vue principale sur GONE. S'il vous plaît faites le moi savoir si quelque chose ne va pas :)

0
Francesco Florio

Apparemment, ajouter Android:layout="@null"à votre préférence en xml ferait l'affaire. Mais par conséquent, la taille du titre sera plus grande.

0
John