如何在PreferenceFragmentCompat中实现RingtonePreference?

13

我正在创建一个应用程序,使用一个AppCompactActivity和所有其他视图都使用Fragment。因此,我想使用PreferenceFragmentCompat。但是,当我创建这个时,我遇到了以下错误。

  android.view.InflateException: Binary XML file line #12: Error inflating class (not found)RingtonePreference
      at android.support.v7.preference.PreferenceInflater.createItemFromTag(PreferenceInflater.java:300)
      at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:358)
      at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:360)
      at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:167)
      at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:117)
      at android.support.v7.preference.PreferenceManager.inflateFromResource(PreferenceManager.java:115)
      at android.support.v7.preference.PreferenceFragmentCompat.addPreferencesFromResource(PreferenceFragmentCompat.java:366)
      at com.example.main.PreferenceFragmentSettings.onCreate(PreferenceFragmentSettings.java:18)
      at android.support.v4.app.Fragment.performCreate(Fragment.java:1939)
      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1029)
      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248)
      at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738)
      at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1613)
      at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:517)
      at android.os.Handler.handleCallback(Handler.java:739)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:135)
      at android.app.ActivityThread.main(ActivityThread.java:5343)
      at java.lang.reflect.Method.invoke(Native Method)
      at java.lang.reflect.Method.invoke(Method.java:372)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)

   Caused by: java.lang.ClassNotFoundException: 
      Didn't find class "android.support.v7.preference.RingtonePreference"
      on path: DexPathList[[zip file "/data/app/com.example.main-1/base.apk"],
      nativeLibraryDirectories=[/vendor/lib, /system/lib]]
      at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
      at android.support.v7.preference.PreferenceInflater.createItem(PreferenceInflater.java:233)
      at android.support.v7.preference.PreferenceInflater.onCreateItem(PreferenceInflater.java:280)
      at android.support.v7.preference.PreferenceInflater.createItemFromTag(PreferenceInflater.java:289)
      at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:358) 
      at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:360) 
      at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:167) 
      at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:117) 
      at android.support.v7.preference.PreferenceManager.inflateFromResource(PreferenceManager.java:115) 
      at android.support.v7.preference.PreferenceFragmentCompat.addPreferencesFromResource(PreferenceFragmentCompat.java:366) 
      at com.example.main.PreferenceFragmentSettings.onCreate(PreferenceFragmentSettings.java:18) 
      at android.support.v4.app.Fragment.performCreate(Fragment.java:1939) 
      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1029) 
      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248) 
      at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738) 
      at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1613) 
      at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:517) 
      at android.os.Handler.handleCallback(Handler.java:739) 
      at android.os.Handler.dispatchMessage(Handler.java:95) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5343) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700) 

    Suppressed: java.lang.ClassNotFoundException: 
          android.support.v7.preference.RingtonePreference
      at java.lang.Class.classForName(Native Method)
      at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
      at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
            ... 25 more
   Caused by: java.lang.NoClassDefFoundError: Class not found using the
   boot class loader; no stack available

请问有没有办法在PreferenceFragmentCompat中实现RingtonePreference


你找到解决办法了吗?我也遇到同样的错误,我必须继续使用支持库,而其中没有 RingtonePreference… - Vucko
1
Android文档也提到了选择铃声作为一个例子。 - JJD
1
你可能想要查看 AndroidX Preference eXtended,该库提供了 RingtonePreference - JJD
4个回答

21

虽然这不是我的解决方案,但我仍然发布它,因为它有效。

在您的偏好设置XML资源中,将RingtonePreference更改为Preference。 然后,在PreferenceFragment的实现中添加:

@Override
public boolean onPreferenceTreeClick(Preference preference) {
    if (preference.getKey().equals(KEY_RINGTONE_PREFERENCE)) {
        Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI);

        String existingValue = getRingtonePreferenceValue(); // TODO
        if (existingValue != null) {
            if (existingValue.length() == 0) {
              // Select "Silent"
                intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, (Uri) null);
            } else {
                intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(existingValue));
            }
        } else {
          // No ringtone has been selected, set to the default
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Settings.System.DEFAULT_NOTIFICATION_URI);
        }

        startActivityForResult(intent, REQUEST_CODE_ALERT_RINGTONE);
        return true;
    } else {
        return super.onPreferenceTreeClick(preference);
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE_ALERT_RINGTONE && data != null) {
        Uri ringtone = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
        if (ringtone != null) {
          setRingtonPreferenceValue(ringtone.toString()); // TODO
        } else {
          // "Silent" was selected
          setRingtonPreferenceValue(""); // TODO
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

来源


1
最好同时提供源链接。 - Sufian
运行良好。除此答案之外,我将添加XML代码:在settings.xml中,您的首选项元素可能如下所示:其中“settings_notification_ringtone”是KEY_RINGTONE_PREFERENCE。 - Kirill Karmazin
但是如何获取所选标题的值,以便将其添加到摘要中呢? - has19
实现这个功能后,我的设置屏幕不再加载了。因为RingtonePreference是PreferenceScreen、PreferenceCategory的子类,这意味着它是一个单独的屏幕。我只会得到一个弹出窗口,选择铃声。 - Jim Clermonts

1

目前支持库中不存在铃声偏好设置。您需要使用框架版本或创建自己的版本。
我认为它很快就会出现。


0

这是基于Dmitrijs的回答 的 Kotlin 实现,使用了 androidx.preference:preference-ktx:1.1.1。我使用铃声作为闹钟提示音 - 因此使用不同的词语,TYPE_ALARMDEFAULT_ALARM_ALERT_URI

class AppSettingsFragment : PreferenceFragmentCompat() {

    // ...

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (REQUEST_CODE_ALARM_TONE == requestCode && data != null) {
            val alarmToneUri = data.getParcelableExtra<Uri>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
            onAlarmTonePicked(alarmToneUri)
        } else {
            super.onActivityResult(requestCode, resultCode, data)
        }
    }
    
    private fun onAlarmTonePicked(alarmToneUri: Uri?) {
        repository.updateAlarmToneUri(alarmToneUri)
    }
    
    override fun onPreferenceTreeClick(preference: Preference?): Boolean {
        return if (getString(R.string.preference_key_alarm_tone) == preference?.key) {
            onAlarmTonePreferenceClick()
        } else {
            super.onPreferenceTreeClick(preference)
        }
    }
    
    private fun onAlarmTonePreferenceClick(): Boolean {
        val alarmToneUri = repository.readAlarmToneUri()
        val extras = bundleOf(
                RingtoneManager.EXTRA_RINGTONE_TYPE to RingtoneManager.TYPE_ALARM,
                RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT to true,
                RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT to true,
                RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI to Settings.System.DEFAULT_ALARM_ALERT_URI,
                RingtoneManager.EXTRA_RINGTONE_EXISTING_URI to alarmToneUri
        )
        val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { putExtras(extras) }
        startActivityForResult(intent, REQUEST_CODE_ALARM_TONE)
        return true
    }

}

0
如果您需要一个 XAMARIN.Droid 版本:
        public override void OnActivityResult(int requestCode, int resultCode, Intent data)
        {
            if (requestCode == REQUEST_CODE_ALERT_RINGTONE && data != null)
            {
                var ringtone = data.GetParcelableExtra(RingtoneManager.ExtraRingtonePickedUri);
                if (ringtone != null)
                {
                    SetRingtonePreferenceValue(ringtone.ToString());
                }
                else
                {
                    // "Silent" was selected
                    SetRingtonePreferenceValue("");
                }
            }
            else
            {
                base.OnActivityResult(requestCode, resultCode, data);
            }
        }

        public override bool OnPreferenceTreeClick(Android.Support.V7.Preferences.Preference preference)
        {
            if (preference.Key == KEY_RINGTONE_PREFERENCE)
            {
                Intent intent = new Intent(RingtoneManager.ActionRingtonePicker);

                intent.PutExtra(RingtoneManager.ExtraRingtoneType, (int)RingtoneType.Notification);
                intent.PutExtra(RingtoneManager.ExtraRingtoneShowDefault, true);
                intent.PutExtra(RingtoneManager.ExtraRingtoneShowSilent, true);
                intent.PutExtra(RingtoneManager.ExtraRingtoneDefaultUri, Android.Provider.Settings.System.DefaultRingtoneUri);

                string existingValue = GetRingtonePreferenceValue();
                if (existingValue != null)
                {
                    if (existingValue.Length == 0)
                    {
                        // Select "Silent"
                        intent.PutExtra(RingtoneManager.ExtraRingtoneExistingUri, (Android.Net.Uri)null);
                    }
                    else
                    {
                        intent.PutExtra(RingtoneManager.ExtraRingtoneExistingUri, Android.Net.Uri.Parse(existingValue));
                    }
                }
                else
                {
                    // No ringtone has been selected, set to the default
                    intent.PutExtra(RingtoneManager.ExtraRingtoneExistingUri, Android.Provider.Settings.System.DefaultRingtoneUri);
                }

                StartActivityForResult(intent, REQUEST_CODE_ALERT_RINGTONE);

                return true;
            }

            return base.OnPreferenceTreeClick(preference);

        }

        void SetRingtonePreferenceValue(string ringtone)
        {
            var editor = prefs.Edit();
            editor.PutString(KEY_RINGTONE_PREFERENCE, ringtone);
            editor.Commit();
        }

        string GetRingtonePreferenceValue()
        {
            return prefs.GetString(KEY_RINGTONE_PREFERENCE, "content://settings/system/notification_sound");
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接