带有数字选择器的 Android PreferenceActivity 对话框

Posted

技术标签:

【中文标题】带有数字选择器的 Android PreferenceActivity 对话框【英文标题】:Android PreferenceActivity dialog with number picker 【发布时间】:2014-01-12 14:00:36 【问题描述】:

我已经看到了许多针对这个问题的定制解决方案和答案。我需要一些非常简单的东西,我有一个偏好活动,我所需要的只是其中一个选项将打开带有数字选择器的对话框并保存结果。你能指导我一步一步地完成这个吗?

public class SettingsActivity extends PreferenceActivity

    @Override
    protected void onCreate(final Bundle savedInstanceState)
    
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
        //requestWindowFeature(Window.FEATURE_NO_TITLE);    
    

    public static class MyPreferenceFragment extends PreferenceFragment
    
        @Override
        public void onCreate(final Bundle savedInstanceState)
        
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.prefs);

        
    


XML:

    <SwitchPreference
        android:key="cross"
        android:summaryOff="Cross is invisible"
        android:summaryOn="Cross is visible"
        android:switchTextOff="OFF"
        android:switchTextOn="ON"
        android:title="Cross" 
        android:defaultValue="true"/>

    <SwitchPreference
        android:key="autoP"
        android:summaryOff="App will go to sleep"
        android:summaryOn="App will not go to sleep"
        android:switchTextOff="OFF"
        android:switchTextOn="ON"
        android:title="Always On" 
        android:defaultValue="true"/>

    <SwitchPreference
        android:key="tempD"
        android:summaryOff="Temprature not displayed"
        android:summaryOn="Temprature displayed"
        android:switchTextOff="OFF"
        android:switchTextOn="ON"
        android:title="Tempature Display" 
        android:defaultValue="true"/>

    <ListPreference
        android:entries="@array/units"
        android:entryValues="@array/lunits"
        android:key="listUnits"
        android:summary="Units schosssing"
        android:title="Units" android:defaultValue="C"/>

     <!--Need to add button to open dialog-->       

</PreferenceScreen>

数字选择器 XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_ >

    <NumberPicker
        android:id="@+id/numberPicker1"
        android:layout_
        android:layout_
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="64dp" />

    <Button
        android:id="@+id/button2"
        android:layout_
        android:layout_
        android:layout_below="@+id/numberPicker1"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="98dp"
        android:layout_toRightOf="@+id/numberPicker1"
        android:text="Cancel" />

    <Button
        android:id="@+id/button1"
        android:layout_
        android:layout_
        android:layout_alignBaseline="@+id/button2"
        android:layout_alignBottom="@+id/button2"
        android:layout_marginRight="16dp"
        android:layout_toLeftOf="@+id/numberPicker1"
        android:text="Set" />

</RelativeLayout>

【问题讨论】:

【参考方案1】:

实现DialogPreference是一个解决方案:

NumberPicker Preference Dialog for Android 真的很简单。 或者查看这个已解决的问题:Create NumberPicker dialog in preference

【讨论】:

【参考方案2】:

子类DialogPreference 来构建您自己的NumberPickerPreference

我在下面为您实现了一个。它工作得很好,但功能不完整。例如,最小值和最大值是硬编码的常量。这些实际上应该是首选项 xml 声明中的属性。要让它工作,您需要添加一个 attrs.xml 文件来指定您的自定义属性。

有关在库项目中支持自定义 xml 属性的 NumberPicker 首选项小部件的完整实现以及展示如何使用它的演示应用,请参阅 GitHub:https://github.com/Alobar/AndroidPreferenceTest

您可以将小部件用作任何其他首选项小部件,除非您必须完全限定名称:

preferences.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <com.example.preference.NumberPickerPreference
        android:key="key_number"
        android:title="Give me a number"
        android:defaultValue="55" />

</PreferenceScreen>

NumberPickerPreference.java

package com.example.preference;

import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.NumberPicker;

/**
 * A @link android.preference.Preference that displays a number picker as a dialog.
 */
public class NumberPickerPreference extends DialogPreference 

    // allowed range
    public static final int MAX_VALUE = 100;
    public static final int MIN_VALUE = 0;
    // enable or disable the 'circular behavior'
    public static final boolean WRAP_SELECTOR_WHEEL = true; 

    private NumberPicker picker;
    private int value;

    public NumberPickerPreference(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
    

    @Override
    protected View onCreateDialogView() 
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.CENTER;

        picker = new NumberPicker(getContext());
        picker.setLayoutParams(layoutParams);

        FrameLayout dialogView = new FrameLayout(getContext());
        dialogView.addView(picker);

        return dialogView;
    

    @Override
    protected void onBindDialogView(View view) 
        super.onBindDialogView(view);
        picker.setMinValue(MIN_VALUE);
        picker.setMaxValue(MAX_VALUE);
        picker.setWrapSelectorWheel(WRAP_SELECTOR_WHEEL);
        picker.setValue(getValue());
    

    @Override
    protected void onDialogClosed(boolean positiveResult) 
        if (positiveResult) 
            picker.clearFocus();
            int newValue = picker.getValue();
            if (callChangeListener(newValue)) 
                setValue(newValue);
            
        
    

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) 
        return a.getInt(index, MIN_VALUE);
    

    @Override
    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) 
        setValue(restorePersistedValue ? getPersistedInt(MIN_VALUE) : (Integer) defaultValue);
    

    public void setValue(int value) 
        this.value = value;
        persistInt(this.value);
    

    public int getValue() 
        return this.value;
    

【讨论】:

onDialogClosed 应该调用 if (callChangeListener(value)) setValue(picker.getValue());为了获得正确的更改通知(请注意,Andoird Studio 中的 stock Preferences 实现依赖于此来更新偏好摘要)。 @RobinDavies 谢谢,我完全错过了那一点基础设施,我已经更新了代码。 有没有办法让这个选择器“非圆形”,即它应该停在最大值。 @SrujanBarai,是的,您可以在号码选择器上使用setWrapSelectorWheel()。我已更新代码以包含此选项。看看,你会想要将它设置为 false。 如何更改此设置以在首选项列表中显示当前选择的值?【参考方案3】:

一个基于 ListPreference 的简单解决方案,动态添加值/条目:

root_preferences.xml:

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

    <ListPreference
        app:key="myNumber"
        app:title="my title"
        app:useSimpleSummaryProvider="true"/>

SettingsActivity.java:

public  class SettingsFragment extends PreferenceFragmentCompat 

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) 
        setPreferencesFromResource(R.xml.root_preferences, rootKey);

        ListPreference e = findPreference("myNumber");
        if (e != null) 
            String[] vals = new String[100];
            for (int i = 0; i < vals.length; i++)
                vals[i] = String.valueOf(i + 1);
            e.setEntries(vals);
            e.setEntryValues(vals);
            e.setDefaultValue("1");
        
    
...

【讨论】:

【参考方案4】:

这是我在 androidx 和 kotlin 中的做法:

NumberPickerPreference.kt

import android.content.Context
import android.util.AttributeSet
import androidx.preference.DialogPreference

class NumberPickerPreference(context: Context?, attrs: AttributeSet?) :
    DialogPreference(context, attrs) 

    override fun getSummary(): CharSequence 
        return getPersistedInt(INITIAL_VALUE).toString()
    

    fun getPersistedInt() = super.getPersistedInt(INITIAL_VALUE)

    fun doPersistInt(value: Int) 
        super.persistInt(value)
        notifyChanged()
    

    companion object 
        // allowed range
        const val INITIAL_VALUE = 50
        const val MIN_VALUE = 12
        const val MAX_VALUE = 100
    

NumberPickerPreferenceDialog.kt

import android.content.Context
import android.os.Bundle
import android.view.View
import android.widget.NumberPicker
import androidx.preference.PreferenceDialogFragmentCompat


class NumberPickerPreferenceDialog : PreferenceDialogFragmentCompat() 
    lateinit var numberPicker: NumberPicker

    override fun onCreateDialogView(context: Context?): View 
        numberPicker = NumberPicker(context)
        numberPicker.minValue = NumberPickerPreference.MIN_VALUE
        numberPicker.maxValue = NumberPickerPreference.MAX_VALUE

        return numberPicker
    

    override fun onBindDialogView(view: View?) 
        super.onBindDialogView(view)
        numberPicker.value = (preference as NumberPickerPreference).getPersistedInt()
    

    override fun onDialogClosed(positiveResult: Boolean) 
        if (positiveResult) 
            numberPicker.clearFocus()
            val newValue: Int = numberPicker.value
            if (preference.callChangeListener(newValue)) 
                (preference as NumberPickerPreference).doPersistInt(newValue)
                preference.summary
            
        
    

    companion object 
        fun newInstance(key: String): NumberPickerPreferenceDialog 
            val fragment = NumberPickerPreferenceDialog()
            val bundle = Bundle(1)
            bundle.putString(ARG_KEY, key)
            fragment.arguments = bundle

            return fragment
        
    

SettingsFragment.kt

import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat

class SettingsFragment : PreferenceFragmentCompat() 
    private val DIALOG_FRAGMENT_TAG = "NumberPickerDialog"

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) 
        setPreferencesFromResource(R.xml.settings, rootKey)
    

    override fun onDisplayPreferenceDialog(preference: Preference?) 
        if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) 
            return
        
        if (preference is NumberPickerPreference) 
            val dialog = NumberPickerPreferenceDialog.newInstance(preference.key)
            dialog.setTargetFragment(this, 0)
            dialog.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
         else
            super.onDisplayPreferenceDialog(preference)
    

settings.xml

<your.package.NumberPickerPreference
        app:key="your_pref_key"
        app:title="@string/your_pref_title" />

希望这会有所帮助。

【讨论】:

嘿,伙计,这段代码在我的 android 模拟器上运行良好,但在我的手机上却没有,我的 android 模拟器正在使用 android 7 和我的手机 android 11,我得到 java.lang.ClassCastException: java.lang。 String 不能在此行上转换为 java.lang.Integer return getPersistedInt(INITIAL_VALUE).toString()...对此有任何修复吗?似乎我必须将该行的值更改为整数?

以上是关于带有数字选择器的 Android PreferenceActivity 对话框的主要内容,如果未能解决你的问题,请参考以下文章

带有身份检查器的自定义 Xcode 选择器

ImageSource.Gallery 上带有抖动的图像选择器的错误

果冻豆和冰淇淋三明治中的android数字选择器默认设计更改

带有结果选择器的扫描运算符

使用带有 jquery 选择器的变量

带有引导日期选择器的 Laravel 4