Search code examples
androidandroid-layoutandroid-viewandroid-preferencesandroid-styles

Custom Preference style padding issue


I am trying to create a Spinner preference like the one over at http://www.hidroh.com/2015/11/30/building-custom-preferences-v7/

So far I've extended Preference like this:

public class SpinnerPreference extends Preference {
    protected String[] mEntries = new String[0];
    protected String[] mEntryValues = new String[0];
    private int mSelection = 0;

    private final LayoutInflater mLayoutInflater;

    public SpinnerPreference(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SpinnerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWidgetLayoutResource(R.layout.spinner_preference);
        mLayoutInflater = LayoutInflater.from(getContext());
        init(context, attrs);

    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SpinnerPreference);
        int entriesResId = ta.getResourceId(R.styleable.SpinnerPreference_entries, 0);
        if (entriesResId != 0) {
            mEntries = context.getResources().getStringArray(entriesResId);
        }
        int valuesResId = ta.getResourceId(R.styleable.SpinnerPreference_entryValues, 0);
        if (valuesResId != 0) {
            mEntryValues = context.getResources().getStringArray(valuesResId);
        }
        ta.recycle();
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) {
        return a.getString(index);
    }

    @Override
    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
        super.onSetInitialValue(restorePersistedValue, defaultValue);
        String value = restorePersistedValue ? getPersistedString(null) : (String) defaultValue;
        for (int i = 0; i < mEntryValues.length; i++) {
            if (TextUtils.equals(mEntryValues[i], value)) {
                mSelection = i;
                break;
            }
        }
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        final Spinner spinner = (Spinner) holder.findViewById(R.id.spinner);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                spinner.performClick();
            }
        });
        spinner.setAdapter(new SpinnerAdapter() {
            @Override
            public View getDropDownView(int position, View convertView, ViewGroup parent) {
                if (convertView == null) {
                    convertView = createDropDownView(position, parent);
                }
                bindDropDownView(position, convertView);
                return convertView;
            }

            @Override
            public void registerDataSetObserver(DataSetObserver observer) {
                // no op
            }

            @Override
            public void unregisterDataSetObserver(DataSetObserver observer) {
                // no op
            }

            @Override
            public int getCount() {
                return mEntries.length;
            }

            @Override
            public Object getItem(int position) {
                return null; // not applicable
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public boolean hasStableIds() {
                return true;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                return getDropDownView(position, convertView, parent);
            }

            @Override
            public int getItemViewType(int position) {
                return 0;
            }

            @Override
            public int getViewTypeCount() {
                return 1;
            }

            @Override
            public boolean isEmpty() {
                return false;
            }
        });
        spinner.setSelection(mSelection);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                mSelection = position;
                persistString(mEntryValues[position]);
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                // no op
            }
        });
    }

    protected View createDropDownView(int position, ViewGroup parent) {
        return mLayoutInflater.inflate(R.layout.spinner_pref_dropdown, parent, false);
    }

    /**
     * Customize dropdown view for given spinner item
     * @param position  item position
     * @param view      item view
     */
    protected void bindDropDownView(int position, View view) {
        TextView textView = (TextView) view.findViewById(android.R.id.text1);
        textView.setText(mEntries[position]);
    }
}

However this results in the following:

As you can see the "Server" title looks different than the rest of Preferences, even though Im not touching its style. Padding is alsow very different. Why is this happening and how can I modify this styling?

Also, in the same image, you can see an icon, is it possible to align this icon to the top?

Preference XML:

<android.support.v7.preference.PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <android.support.v7.preference.ListPreference
        android:key="prefs-preferred-quality"
        android:icon="@drawable/ic_soccer"
        android:summary="Bitrate determines the quality of the stream. Auto is recommended."
        android:title="Streaming bitrate"/>


      <android.support.v7.preference.SwitchPreferenceCompat
        android:defaultValue="false"
        android:key="prefs-record-locally"
        android:summary="Should videos also be saved to the device?"
        android:title="Record Locally"/>


    <tv.mycujoo.mycujoobroadcast.settings.preferences.SpinnerPreference
        android:key="prefs-preferred-server"
        android:icon="@drawable/ic_record_dark"
        app:entries="@array/stream_bitrate"
        app:entryValues="@array/stream_bitrate_values"
        android:summary="Streaming region to stream to"
        android:title="Server"/>


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

EDIT: I posted a sample code with just this screen showcasing both problems:

https://github.com/kelmer44/prefs-test


Solution

  • I had the same problem and posted the answer in this post:

    Custom Preference shows differently on Preference screen than native Preferences

    It will solve your problem.

    In your code just move the code from the 3 argument constructor to the 2 argument constructor (call super(context, attrs);) and delete the 3 argument constructor, the base class Preference will choose the right style for you.