From 8d4a1ad6a3d625e0ea40a24530ab4775b797dc54 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sun, 8 Nov 2015 01:12:06 +0800 Subject: [PATCH] DropDownPreference --- src/main/res/values/attrs.xml | 10 + src/main/res/xml/pref_all.xml | 23 +-- .../preferences/DropDownPreference.scala | 179 ++++++++++++++++++ .../preferences/SummaryPreference.scala | 45 +++++ 4 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 src/main/res/values/attrs.xml create mode 100644 src/main/scala/com/github/shadowsocks/preferences/DropDownPreference.scala create mode 100644 src/main/scala/com/github/shadowsocks/preferences/SummaryPreference.scala diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml new file mode 100644 index 00000000..ca0adbc5 --- /dev/null +++ b/src/main/res/values/attrs.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml index 95738358..c4df91c0 100644 --- a/src/main/res/xml/pref_all.xml +++ b/src/main/res/xml/pref_all.xml @@ -1,5 +1,6 @@ - + @@ -37,28 +38,28 @@ android:summary="@string/sitekey_summary" android:title="@string/sitekey"> - - + - - + { + mSpinner.setDropDownVerticalOffset(Utils.dpToPx(getContext, 8 - 48 * mSelectedIndex).toInt) // TODO: scrolling? + mSpinner.performClick + true + }) + initSummary(mContext, attrs) + val a: TypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.DropDownPreference) + setEntries(a.getTextArray(R.styleable.DropDownPreference_entries)) + mEntryValues = a.getTextArray(R.styleable.DropDownPreference_entryValues) + a.recycle + + protected override def getSummaryValue = getEntry + + /** + * Sets the human-readable entries to be shown in the list. This will be shown in subsequent dialogs. + * + * Each entry must have a corresponding index in [[setEntryValues(CharSequence[])]]. + * + * @param entries The entries. + * @see [[setEntryValues(CharSequence[])]] + */ + def setEntries(entries: Array[CharSequence]) { + mEntries = entries + mAdapter.clear + if (entries != null) for (entry <- entries) mAdapter.add(entry.toString) + } + /** + * @see #setEntries(CharSequence[]) + * @param entriesResId The entries array as a resource. + */ + def setEntries(entriesResId: Int): Unit = setEntries(getContext.getResources.getTextArray(entriesResId)) + /** + * The list of entries to be shown in the list in subsequent dialogs. + * + * @return The list as an array. + */ + def getEntries = mEntries + + /** + * The array to find the value to save for a preference when an entry from + * entries is selected. If a user clicks on the second item in entries, the + * second item in this array will be saved to the preference. + * + * @param entryValues The array to be used as values to save for the preference. + */ + def setEntryValues(entryValues: Array[CharSequence]) = mEntryValues = entryValues + /** + * @see #setEntryValues(CharSequence[]) + * @param entryValuesResId The entry values array as a resource. + */ + def setEntryValues(entryValuesResId: Int): Unit = + setEntryValues(getContext.getResources.getTextArray(entryValuesResId)) + /** + * Returns the array of values to be saved for the preference. + * + * @return The array of values. + */ + def getEntryValues: Array[CharSequence] = mEntryValues + + /** + * Sets the value of the key. This should be one of the entries in [[getEntryValues]]. + * + * @param value The value to set for the key. + */ + def setValue(value: String) { + val i = findIndexOfValue(value) + if (i > -1) setValueIndex(i) + } + + /** + * Sets the value to the given index from the entry values. + * + * @param index The index of the value to set. + */ + def setValueIndex(index: Int) { + val changed = mSelectedIndex != index + if (changed || !mValueSet) { + mSelectedIndex = index + mValueSet = true + if (mEntryValues != null) { + val value = mEntryValues(index).toString + persistString(value) + setValue(value) + } + if (changed) notifyChanged + } + mSpinner.setSelection(index) + } + + /** + * Returns the value of the key. This should be one of the entries in [[getEntryValues]]. + * + * @return The value of the key. + */ + def getValue = if (mEntryValues == null) null else mEntryValues(mSelectedIndex).toString + + /** + * Returns the entry corresponding to the current value. + * + * @return The entry corresponding to the current value, or null. + */ + def getEntry = { + val index = getValueIndex + if (index >= 0 && mEntries != null) mEntries(index) else null + } + + /** + * Returns the index of the given value (in the entry values array). + * + * @param value The value whose index should be returned. + * @return The index of the value, or -1 if not found. + */ + def findIndexOfValue(value: String): Int = { + if (value != null && mEntryValues != null) { + var i = mEntryValues.length - 1 + while (i >= 0) { + if (mEntryValues(i) == value) return i + i -= 1 + } + } + -1 + } + + def getValueIndex = mSelectedIndex + + protected override def onGetDefaultValue(a: TypedArray, index: Int) = a.getString(index) + protected override def onSetInitialValue (restoreValue: Boolean, defaultValue: AnyRef) = + setValue(if (restoreValue) getPersistedString(getValue) else defaultValue.asInstanceOf[String]) + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + def setDropDownWidth(dimenResId: Int) = + mSpinner.setDropDownWidth(mContext.getResources.getDimensionPixelSize(dimenResId)) + + protected override def onBindView(@NonNull view: View) { + super.onBindView(view) + if (view == mSpinner.getParent) return + if (mSpinner.getParent != null) mSpinner.getParent.asInstanceOf[ViewGroup].removeView(mSpinner) + view.asInstanceOf[ViewGroup].addView(mSpinner, 0) + val lp = mSpinner.getLayoutParams + lp.width = 0 + mSpinner.setLayoutParams(lp) + } +} diff --git a/src/main/scala/com/github/shadowsocks/preferences/SummaryPreference.scala b/src/main/scala/com/github/shadowsocks/preferences/SummaryPreference.scala new file mode 100644 index 00000000..56523494 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/preferences/SummaryPreference.scala @@ -0,0 +1,45 @@ +package com.github.shadowsocks.preferences + +import android.content.Context +import android.preference.Preference +import android.util.AttributeSet +import com.github.shadowsocks.R + +/** + * Make your preference support %s in summary. Override getSummaryValue to customize what to put in. + * Based on: https://github.com/Mygod/mygod-lib-android/blob/eb20c57a0da70bb5dbc27104002f20fb1eb31d76/lib/src/main/scala/tk/mygod/preference/SummaryPreference.scala + * @author Mygod + */ +trait SummaryPreference extends Preference { + private var mSummary: String = _ + + protected def initSummary(context: Context, attrs: AttributeSet) { + val a = context.obtainStyledAttributes(attrs, R.styleable.SummaryPreference) + mSummary = a.getString(R.styleable.SummaryPreference_summary) + a.recycle + } + + protected def getSummaryValue: AnyRef + + /** + * Returns the summary of this SummaryPreference. If the summary has a String formatting marker in it + * (i.e. "%s" or "%1$s"), then the current entry value will be substituted in its place. + * + * @return the summary with appropriate string substitution + */ + override def getSummary = { + val entry = getSummaryValue + if (mSummary == null || entry == null) super.getSummary else String.format(mSummary, entry) + } + /** + * Sets the summary for this Preference with a CharSequence. If the summary has a String formatting marker in it + * (i.e. "%s" or "%1$s"), then the current entry value will be substituted in its place when it's retrieved. + * + * @param summary The summary for the preference. + */ + override def setSummary(summary: CharSequence) { + super.setSummary(summary) + if (summary == null && mSummary != null) mSummary = null + else if (summary != null && summary != mSummary) mSummary = summary.toString + } +}