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:
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?
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):
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.
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"
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();
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.
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>
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:
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" />
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);
}
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>
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>
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>
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.
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.
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>
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 :)
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.