DropDownPreference
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="SummaryPreference">
|
||||
<attr name="summary" format="string" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="DropDownPreference">
|
||||
<attr name="entries" format="reference" />
|
||||
<attr name="entryValues" format="reference" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/proxy_cat">
|
||||
@@ -37,28 +38,28 @@
|
||||
android:summary="@string/sitekey_summary"
|
||||
android:title="@string/sitekey">
|
||||
</com.github.shadowsocks.preferences.PasswordEditTextPreference>
|
||||
<ListPreference
|
||||
<com.github.shadowsocks.preferences.DropDownPreference
|
||||
android:defaultValue="rc4"
|
||||
android:key="encMethod"
|
||||
android:entries="@array/enc_method_entry"
|
||||
android:entryValues="@array/enc_method_value"
|
||||
android:summary="%s"
|
||||
app:entries="@array/enc_method_entry"
|
||||
app:entryValues="@array/enc_method_value"
|
||||
app:summary="%s"
|
||||
android:title="@string/enc_method">
|
||||
</ListPreference>
|
||||
</com.github.shadowsocks.preferences.DropDownPreference>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/feature_cat">
|
||||
|
||||
<ListPreference
|
||||
<com.github.shadowsocks.preferences.DropDownPreference
|
||||
android:defaultValue="all"
|
||||
android:key="route"
|
||||
android:entries="@array/route_entry"
|
||||
android:entryValues="@array/route_value"
|
||||
android:summary="%s"
|
||||
app:entries="@array/route_entry"
|
||||
app:entryValues="@array/route_value"
|
||||
app:summary="%s"
|
||||
android:title="@string/route_list">
|
||||
</ListPreference>
|
||||
</com.github.shadowsocks.preferences.DropDownPreference>
|
||||
<SwitchPreference
|
||||
android:key="isIpv6"
|
||||
android:defaultValue="false"
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.github.shadowsocks.preferences
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.os.Build
|
||||
import android.preference.Preference
|
||||
import android.support.annotation.NonNull
|
||||
import android.support.v7.widget.AppCompatSpinner
|
||||
import android.util.AttributeSet
|
||||
import android.view.{View, ViewGroup}
|
||||
import android.widget.AdapterView.OnItemSelectedListener
|
||||
import android.widget.{AdapterView, ArrayAdapter}
|
||||
import com.github.shadowsocks.R
|
||||
import com.github.shadowsocks.utils.Utils
|
||||
|
||||
/**
|
||||
* Based on: https://github.com/Mygod/mygod-lib-android/blob/683c41cb86d10e99ab0f457d41a218c6915fccc8/lib/src/main/scala/tk/mygod/preference/DropDownPreference.scala
|
||||
* @author Mygod
|
||||
*/
|
||||
final class DropDownPreference(private val mContext: Context, attrs: AttributeSet = null)
|
||||
extends Preference(mContext, attrs) with SummaryPreference {
|
||||
|
||||
private val mAdapter = new ArrayAdapter[String](mContext, android.R.layout.simple_spinner_dropdown_item)
|
||||
private val mSpinner = new AppCompatSpinner(mContext)
|
||||
private var mEntries: Array[CharSequence] = _
|
||||
private var mEntryValues: Array[CharSequence] = _
|
||||
private var mSelectedIndex: Int = _
|
||||
private var mValueSet: Boolean = _
|
||||
|
||||
mSpinner.setVisibility(View.INVISIBLE)
|
||||
mSpinner.setAdapter(mAdapter)
|
||||
mSpinner.setOnItemSelectedListener(new OnItemSelectedListener {
|
||||
override def onNothingSelected(parent: AdapterView[_]) { }
|
||||
override def onItemSelected(parent: AdapterView[_], view: View, position: Int, id: Long) = setValueIndex(position)
|
||||
})
|
||||
setOnPreferenceClickListener((preference: Preference) => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user