Как создать одно предпочтение с помощью EditTextPreference и кнопки переключения? - PullRequest
12 голосов
/ 16 марта 2012

То, что я пытаюсь реализовать, это в основном и точная копия изображения ниже (предпочтения, которые я в квадрате). Нажатие чего-либо слева от предпочтения должно открыть диалог. Нажатие на кнопку-переключатель отключит / включит все настройки, которые я установил в этом предпочтении.

Я пытаюсь часами и прихожу с пустыми руками. Как мне реализовать это в PreferenceActivity?

Preference

РЕДАКТИРОВАТЬ: Кажется, люди неправильно понимают мой вопрос. Очень важно выяснить, как решить мою проблему с помощью PreferenceActivity. НЕ деятельность. Мне все равно, нужно ли мне делать это в XML или программно. Просто, пожалуйста, воздержитесь от предоставления мне ответов, которые я не могу использовать в или что-то подобное.

РЕДАКТИРОВАТЬ 2: Добавлена ​​награда - мне действительно нужен ответ на этот вопрос

Ответы [ 6 ]

17 голосов
/ 24 марта 2012

Черт возьми, мне нравится твоя идея: -)

Это то же самое, что и ответ @ MH, но более сжатый.

Я проверил с ToggleButton, а не Switch.

package android.dumdum;

import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ToggleButton;

public class TogglePreference extends Preference {

    public TogglePreference(Context context) {
        super(context);
    }

    public TogglePreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TogglePreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public View getView(View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = new LinearLayout(getContext());
            ((LinearLayout) convertView)
                    .setOrientation(LinearLayout.HORIZONTAL);

            TextView txtInfo = new TextView(getContext());

            txtInfo.setText("Test");
            ((LinearLayout) convertView).addView(txtInfo,
                    new LinearLayout.LayoutParams(
                            LinearLayout.LayoutParams.MATCH_PARENT,
                            LinearLayout.LayoutParams.WRAP_CONTENT, 1));

            ToggleButton btn = new ToggleButton(getContext());
            ((LinearLayout) convertView).addView(btn);
        }

        return convertView;
    }
}

И preferences.xml:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <PreferenceCategory android:title="Test custom preferences" >
        <android.dumdum.EncryptorEditTextPreference />
        <android.dumdum.TogglePreference />
    </PreferenceCategory>

</PreferenceScreen>

EncryptorEditTextPreference не относится к вашему вопросу, но использует ту же технику (расширение EditTextPreference).

9 голосов
/ 23 марта 2012

Просто заметка заранее: это будет довольно длинный ответ, но я намерен предоставить вам хороший ответ, который вы могли бы буквально скопировать и вставить, чтобы начать.

Это на самом деле не так уж сложно сделать. Ваша лучшая отправная точка - поиск реализации SwichPreference на ICS. Вы увидите, что это довольно просто, большая часть работы выполняется суперклассом TwoStatePreference, который, в свою очередь, также доступен только для ICS. К счастью, вы можете буквально скопировать и вставить (см. Весь ответ в этом ответе) этот класс и построить собственный TogglePreference (давайте назовем это для ясности) поверх этого, используя реализацию SwitchPreference как руководство.

То, что вы получите, сделав это, приведено ниже. Я добавил некоторые пояснения к каждому методу, чтобы я мог ограничить свое написание здесь.

TogglePreference.java

package mh.so.pref;

import mh.so.R;
import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.ToggleButton;

/**
 * A {@link Preference} that provides a two-state toggleable option.
 * <p>
 * This preference will store a boolean into the SharedPreferences.
 */
public class TogglePreference extends TwoStatePreference {
    private final Listener mListener = new Listener();
    private ExternalListener mExternalListener;

    /**
     * Construct a new TogglePreference with the given style options.
     *
     * @param context The Context that will style this preference
     * @param attrs Style attributes that differ from the default
     * @param defStyle Theme attribute defining the default style options
     */
    public TogglePreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * Construct a new TogglePreference with the given style options.
     *
     * @param context The Context that will style this preference
     * @param attrs Style attributes that differ from the default
     */
    public TogglePreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * Construct a new TogglePreference with default style options.
     *
     * @param context The Context that will style this preference
     */
    public TogglePreference(Context context) {
        this(context, null);
    }

    /** Inflates a custom layout for this preference, taking advantage of views with ids that are already
     * being used in the Preference base class.
     */
    @Override protected View onCreateView(ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
        return inflater.inflate(R.layout.toggle_preference_layout, parent, false);
    }

    /** Since the Preference base class handles the icon and summary (or summaryOn and summaryOff in TwoStatePreference)
     * we only need to handle the ToggleButton here. Simply get it from the previously created layout, set the data
     * against it and hook up a listener to handle user interaction with the button.
     */
    @Override protected void onBindView(View view) {
        super.onBindView(view);

        ToggleButton toggleButton = (ToggleButton) view.findViewById(R.id.toggle_togglebutton);
        toggleButton.setChecked(isChecked());
        toggleButton.setOnCheckedChangeListener(mListener);
    }

    /** This gets called when the preference (as a whole) is selected by the user. The TwoStatePreference 
     * implementation changes the actual state of this preference, which we don't want, since we're handling
     * preference clicks with our 'external' listener. Hence, don't call super.onClick(), but the onPreferenceClick
     * of our listener. */
    @Override protected void onClick() {
        if (mExternalListener != null) mExternalListener.onPreferenceClick();
    }

    /** Simple interface that defines an external listener that can be notified when the preference has been
     * been clicked. This may be useful e.g. to navigate to a new activity from your PreferenceActivity, or 
     * display a dialog. */
    public static interface ExternalListener {
        void onPreferenceClick();
    }

    /** Sets an external listener for this preference*/
    public void setExternalListener(ExternalListener listener) {
        mExternalListener = listener;
    }

    /** Listener to update the boolean flag that gets stored into the Shared Preferences */
    private class Listener implements CompoundButton.OnCheckedChangeListener {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (!callChangeListener(isChecked)) {
                // Listener didn't like it, change it back.
                // CompoundButton will make sure we don't recurse.
                buttonView.setChecked(!isChecked);
                return;
            }

            TogglePreference.this.setChecked(isChecked);
        }
    }

}

Файл макета для этого примера - просто LinearLayout с тремя элементами в нем, наиболее интересным из которых является ToggleButton. ImageView и TextView используют преимущества работы, уже выполняемой базовым классом Preference, используя соответствующие идентификаторы в пространстве имен Android. Таким образом, нам не нужно беспокоиться об этом. Обратите внимание, что я почти уверен, что опция icon не была добавлена ​​до Honeycomb, поэтому вы можете просто добавить ее в качестве пользовательского атрибута к TogglePreference и вручную установить его, чтобы он всегда был там. Просто щелкните мне комментарий, если вам нужны более конкретные указатели для этого подхода.

В любом случае, очевидно, что вы можете изменить макет в любой степени и применить стиль по своему вкусу. Например, чтобы имитировать ToggleButton a Switch, вы можете изменить фон на какой-нибудь другой StateListDrawable и / или изменить или полностью избавиться от текста включения / выключения.

toggle_preference_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight" >

    <ImageView
        android:id="@android:id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:focusable="false"
        android:focusableInTouchMode="false" />

    <TextView
        android:id="@android:id/summary"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <ToggleButton
        android:id="@+id/toggle_togglebutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:focusable="false"
        android:focusableInTouchMode="false" />

</LinearLayout>

Затем вы можете использовать TogglePreference, как и любой другой Preference в вашем PreferenceActivity. Подключив прослушиватель, вы можете делать все что угодно, когда пользователь выбирает предпочтение, в то же время, щелкая фактическое ToggleButton, вы переключаете логическое значение в SharedPreferences.

DemoPreferenceActivity.java

package mh.so.pref;

import mh.so.R;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.widget.Toast;

public class DemoPreferenceActivity extends PreferenceActivity {

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.prefs);

        TogglePreference toggle = (TogglePreference) findPreference("toggle_preference");
        toggle.setExternalListener(new TogglePreference.ExternalListener() {
            @Override public void onPreferenceClick() { 
                Toast.makeText(DemoPreferenceActivity.this, "You clicked the preference without changing its value", Toast.LENGTH_LONG).show();
            }
        });
    }

}

Prefs.xml - не что иное, как единственное определение выше TogglePreference. Вы можете предоставить все обычные атрибуты в пространстве имен Android. При желании вы также можете объявить некоторые пользовательские атрибуты, чтобы использовать встроенную функциональность TwoStatePreference для работы с текстами summaryOn и summaryOff.

prefs.xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <PreferenceCategory android:title="Toggle preferences" >
        <mh.so.pref.TogglePreference xmlns:app="http://schemas.android.com/apk/res/mh.so"
            android:key="toggle_preference"
            android:summary="Summary"
            android:icon="@drawable/icon" />
    </PreferenceCategory>

</PreferenceScreen>

И наконец, класс TwoStatePreference, созданный с помощью backported из ICS. Он ничем не отличается от исходного, для которого вы можете найти источник здесь .

package mh.so.pref;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;

/**
 * Common base class for preferences that have two selectable states, persist a
 * boolean value in SharedPreferences, and may have dependent preferences that are
 * enabled/disabled based on the current state.
 */
public abstract class TwoStatePreference extends Preference {

    private CharSequence mSummaryOn;
    private CharSequence mSummaryOff;
    private boolean mChecked;
    private boolean mDisableDependentsState;


    public TwoStatePreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

    public TwoStatePreference(Context context) {
        this(context, null);
    }

    @Override
    protected void onClick() {
        super.onClick();

        boolean newValue = !isChecked();

        if (!callChangeListener(newValue)) {
            return;
        }

        setChecked(newValue);
    }

    /**
     * Sets the checked state and saves it to the {@link SharedPreferences}.
     *
     * @param checked The checked state.
     */
    public void setChecked(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;
            persistBoolean(checked);
            notifyDependencyChange(shouldDisableDependents());
            notifyChanged();
        }
    }

    /**
     * Returns the checked state.
     *
     * @return The checked state.
     */
    public boolean isChecked() {
        return mChecked;
    }

    @Override
    public boolean shouldDisableDependents() {
        boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
        return shouldDisable || super.shouldDisableDependents();
    }

    /**
     * Sets the summary to be shown when checked.
     *
     * @param summary The summary to be shown when checked.
     */
    public void setSummaryOn(CharSequence summary) {
        mSummaryOn = summary;
        if (isChecked()) {
            notifyChanged();
        }
    }

    /**
     * @see #setSummaryOn(CharSequence)
     * @param summaryResId The summary as a resource.
     */
    public void setSummaryOn(int summaryResId) {
        setSummaryOn(getContext().getString(summaryResId));
    }

    /**
     * Returns the summary to be shown when checked.
     * @return The summary.
     */
    public CharSequence getSummaryOn() {
        return mSummaryOn;
    }

    /**
     * Sets the summary to be shown when unchecked.
     *
     * @param summary The summary to be shown when unchecked.
     */
    public void setSummaryOff(CharSequence summary) {
        mSummaryOff = summary;
        if (!isChecked()) {
            notifyChanged();
        }
    }

    /**
     * @see #setSummaryOff(CharSequence)
     * @param summaryResId The summary as a resource.
     */
    public void setSummaryOff(int summaryResId) {
        setSummaryOff(getContext().getString(summaryResId));
    }

    /**
     * Returns the summary to be shown when unchecked.
     * @return The summary.
     */
    public CharSequence getSummaryOff() {
        return mSummaryOff;
    }

    /**
     * Returns whether dependents are disabled when this preference is on ({@code true})
     * or when this preference is off ({@code false}).
     *
     * @return Whether dependents are disabled when this preference is on ({@code true})
     *         or when this preference is off ({@code false}).
     */
    public boolean getDisableDependentsState() {
        return mDisableDependentsState;
    }

    /**
     * Sets whether dependents are disabled when this preference is on ({@code true})
     * or when this preference is off ({@code false}).
     *
     * @param disableDependentsState The preference state that should disable dependents.
     */
    public void setDisableDependentsState(boolean disableDependentsState) {
        mDisableDependentsState = disableDependentsState;
    }

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

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        setChecked(restoreValue ? getPersistedBoolean(mChecked)
                : (Boolean) defaultValue);
    }

    /**
     * Sync a summary view contained within view's subhierarchy with the correct summary text.
     * @param view View where a summary should be located
     */
    void syncSummaryView(View view) {
        // Sync the summary view
        TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
        if (summaryView != null) {
            boolean useDefaultSummary = true;
            if (mChecked && mSummaryOn != null) {
                summaryView.setText(mSummaryOn);
                useDefaultSummary = false;
            } else if (!mChecked && mSummaryOff != null) {
                summaryView.setText(mSummaryOff);
                useDefaultSummary = false;
            }

            if (useDefaultSummary) {
                final CharSequence summary = getSummary();
                if (summary != null) {
                    summaryView.setText(summary);
                    useDefaultSummary = false;
                }
            }

            int newVisibility = View.GONE;
            if (!useDefaultSummary) {
                // Someone has written to it
                newVisibility = View.VISIBLE;
            }
            if (newVisibility != summaryView.getVisibility()) {
                summaryView.setVisibility(newVisibility);
            }
        }
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        final Parcelable superState = super.onSaveInstanceState();
        if (isPersistent()) {
            // No need to save instance state since it's persistent
            return superState;
        }

        final SavedState myState = new SavedState(superState);
        myState.checked = isChecked();
        return myState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state == null || !state.getClass().equals(SavedState.class)) {
            // Didn't save state for us in onSaveInstanceState
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState myState = (SavedState) state;
        super.onRestoreInstanceState(myState.getSuperState());
        setChecked(myState.checked);
    }

    static class SavedState extends BaseSavedState {
        boolean checked;

        public SavedState(Parcel source) {
            super(source);
            checked = source.readInt() == 1;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(checked ? 1 : 0);
        }

        public SavedState(Parcelable superState) {
            super(superState);
        }

        public static final Parcelable.Creator<SavedState> CREATOR =
                new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}

TogglePreference without any fancy styling applied

0 голосов
/ 29 марта 2015

Просто используйте SwitchPreference

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
        <SwitchPreference
            android:key="test"
            android:title="This is test toggle switch" />
</PreferenceScreen>

выглядит так (просто пример из моего приложения, не беспокойтесь о других настройках) enter image description here

0 голосов
/ 23 марта 2012

Вы можете использовать этот xml-код в своем xf-файле pref

<PreferenceCategory>
        <EditTextPreference
            android:key="myEditText"
            android:title="Hi"
            android:inputType="Mine"></EditTextPreference>
    </PreferenceCategory>

и вместо кнопки-переключателя можно использовать флажок:

    <CheckBoxPreference
        android:key="testmode"
        android:title="@string/test_mode"></CheckBoxPreference>

, если вы не хотитечтобы использовать флажок вы можете использовать этот код:

0 голосов
/ 16 марта 2012

Я не уверен, с какой проблемой вы столкнулись, я только что создал фиктивное представление, как то, на что вы ссылаетесь, и я не вижу никаких проблем

<TableRow
    android:id="@+id/tableRow1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />


    <Switch
        android:id="@+id/switch1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="Switch" />

</TableRow>
0 голосов
/ 16 марта 2012

Не уверен, если это кнопка переключения, но если это так, вы можете просто сказать android: textOn или android: textoff на .xml. Если он находится в Java-части, то, вероятно, просто что-то вроде setTextOn. Не уверен, что это кнопка переключения, но если это так, вы можете просто сказать android: textOn или android: textoff на .xml. Если он находится в части Java, то, вероятно, просто что-то вроде toggleButton.setChecked.

...