问题
I am facing very weird problem in designing preference screen. Though I am not giving any margin in layout,it is leaving some space in left.
As you can see in image below:
XML:
<PreferenceScreen android:title=\"demo\" >
<CheckBoxPreference
android:defaultValue=\"false\"
android:key=\"prefSync\"`
android:title=\"Auto Sync\" />
</PreferenceScreen>
Am I doing something wrong in adding check-box preference in screen?
回答1:
Updating this for androidx.
After a lot of experimentation, I resolved this issue by adding this to each preference that had the excess indentation:
app:iconSpaceReserved="false"
Of course, you'll also need to add this to the PreferenceScreen declaration itself at the top of your xml:
xmlns:app="http://schemas.android.com/apk/res-auto"
回答2:
I've reported about this issue here (you can also check my workaround project there), but for now, I've found a bit hack-y, yet easy way to overcome this:
For
PreferenceCategory
, I set the start/left padding of its layouts to 0.For other types of preferences, I choose to hide the
icon_frame
in case they don't have an icon.
Here's the code. Just extend from this class and the rest is automatic :
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());
}
}
}
And the result (XML sample can be found here of this Google sample, which I've created here to check out) :
This code is a bit dangerous, so make sure that upon each update of the library, you check that it works fine.
Also, it might not work well on some special cases, such as when you define android:layout
of your own for the preference, so you will have to modify it for this matter.
Got a better, more official solution:
For each preference, use app:iconSpaceReserved="false"
. This should work fine, but for some reason there is a (known) bug that it doesn't work for PreferenceCategory. It was reported here, and should be fixed in the near future.
So for now you can use a mix version of the workaround I wrote and this flag.
EDIT: Found yet another solution. This one will get over all of the preferences, and set isIconSpaceReserved
for each. Sadly, as I've written above, if you use PreferenceCategory, it ruins it, but it should work fine if you don't use it:
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: after Google finally fixed the library (link here), you can set the flag for each preference, or use this solution which does it for all, for you :
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)
}
}
}
Just extend from it, and you won't have the useless padding for your preferences. Sample project here, where I also request to have an official way to avoid it, instead of those tricks. Please consider starring it.
回答3:
Simple working solution from here:
Create 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>
The <bool>
fixes the default value of iconSpacePreserved
for all Preference
; The <dimen>
fixes the PreferenceCategory.
回答4:
Try this:
@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;
}
You can set padding by using: setPadding();
回答5:
If you're using the com.android.support:preference-v7
library, make sure the theme for the activity that's hosting your preferences has a preferenceTheme
set to the v14 Material preference theme overlay:
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
回答6:
Unfortunately, nothing from above worked for me.
Short answer:
I solved this by using a negative margins on the uppermost container of the custom preference layout.
Steps:
- Create a custom layout for the preference (e.g. preference_category.xml)
- specify that your preference uses the custom layout by adding android:layout param to a preference tag in XML
- Apply negative margins
Detailed answer:
The preference 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>
The custom layout for preference row which removes unnecessary left and right margins:
<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>
The custom layout for preference category which removes unnecessary left and right margins:
<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" />
回答7:
The padding is caused by the parent ListView
in the PreferenceScreen
containing your settings. If you are using a PreferenceFragment
to create your Preferences, just go to the onActivityCreated
function of the PreferenceFragement
and do the following to remove the padding:
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);
}
回答8:
Change the preference theme like this . Make sure to use the latest version of the preference library 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>
回答9:
compile 'com.android.support:preference-v7:24.2.1'
Use PreferenceFragmentCompat can do this:
1.Define a 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.Override PreferenceFragmentCompat's onCreateAdapter method:
@Override
protected Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
return new CustomAdapter(preferenceScreen);
}
Use style.xml can do this:
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>
回答10:
None of the above answer worked, I had to copy the preference layout, create a new layout file such as custom_preference.xml, override the layout content by setting the visibility of the icon frame to GONE. And then in the pref.xml, set the layout for the preference to use this custom layout.
<?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>
回答11:
The 'padding' is initiated in 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();
}
If you know how to override styles defined by Android, you could probably solve this. It is not very straight forward however.
回答12:
I try using
android:icon="@null"
with CheckBoxPreference
in Android 4.4 but can't padding from left.
I tricked use a small transparent icon and add
android:icon="@drawable/transparent_icon"
and it seems work for me. I hope it can help. Thanks.
回答13:
I don't know if it's too late for an answer but today I had the same problem and nothing of this answers help me.
So I used this solution:
In my DisclaimerPreference
that extends Preference
, I ovveride the onBindView(View view)
method in this way:
@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);
}
}
}
The key is to set the RelativeLayout
inside the main View as GONE
.
Please let me know if something is wrong :)
回答14:
Apparently, adding android:layout="@null"
to your preference in xml would do the trick. But consequently, it will also make title size larger..
回答15:
Asked quite a while ago, but what helped me solve this was negative margins, no need for custom files.
When you create your SettingsActivity class (which has the PreferenceFragment inside), you simply set it's layout to be a fragment and add a negative margin.
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>
回答16:
Add android:defaultValue="false"
to your <PreferenceScreen [...]
Tag:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:defaultValue="false">
来源:https://stackoverflow.com/questions/18509369/android-how-to-remove-margin-padding-in-preference-screen