建立对话以选择铃声

我在这里花了一个项目来与设置中的铃声选择进行对话。 立即有两个原因-首先,支持库中没有RingtonePreference ,因此在PreferenceFragmentCompat使用标准对话框将失败。 其次,除了标准的旋律之外,我还必须从那里的资源中添加一些声音。 因此,决定写您的对话。


我将使用一个简单应用程序的示例演示如何创建此类对话:在一个屏幕上,有一个“播放铃声”按钮,单击该按钮可播放设置中设置的铃声,并链接到设置屏幕:



我不会描述这两个屏幕的创建-一切都一如既往。 以防万一,最后会有一个到存储库的链接,其中包含应用程序代码。


因此,让我们从带有设置屏幕说明的xml文件开始。 将settings.xml文件放入具有以下内容的res/xml


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

现在,将这些设置添加到片段中:


 class SettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.settings, rootKey) } } 

启动,打开设置屏幕,请参阅以下内容:



引言到此结束,我们继续本文的目的。 计划是这样的:单击“铃声”时,将打开一个对话框,其中包含铃声列表以及“确定”和“取消”按钮,当您选择铃声时,它将播放(如标准RingtonePreference ),单击“确定”后,它将保存在设置中。


因此,创建一个对话框片段:


 class RingtonePreferenceDialog : DialogFragment() { private val prefKey: String get() = arguments?.getString(ARG_PREF_KEY) ?: throw IllegalArgumentException("ARG_PREF_KEY not set") companion object { private const val ARG_PREF_KEY = "ARG_PREF_KEY" fun create(prefKey: String): RingtonePreferenceDialog { val fragment = RingtonePreferenceDialog() fragment.arguments = Bundle().apply { putString(ARG_PREF_KEY, prefKey) } return fragment } } } 

prefKey我们传输将提取当前铃声的密钥,然后按OK按钮将其记录下来。


为了进行进一步的工作,我们需要辅助类Ringtone ,在片段中声明它:


 private data class Ringtone(val title: String, val uri: Uri) 

并编写一个辅助函数,该函数将提取Android中所有内置的铃声,并将列表从“ Ringtone返回Ringtone


 private fun getAndroidRingtones(): List<Ringtone> { val ringtoneManager = RingtoneManager(context) val cursor = ringtoneManager.cursor return (0 until cursor.count).map { cursor.moveToPosition(it) Ringtone( title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX), uri = ringtoneManager.getRingtoneUri(it) ) } } 

在这里ringtoneManager.cursor将返回带有所有可用铃声的光标,我们只需遍历所有元素并将它们映射到我们的帮助器类Ringtone (使用它们会更方便)。


首先,让我们使用内置的铃声列表来组织工作-以后添加资源非常简单。 为此,请通过重写onCreateDialog方法创建一个对话框:


 private var ringtones: List<Ringtone> = emptyList() private var currentUri: Uri? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { ringtones = getAndroidRingtones() currentUri = getCurrentRingtoneUri() val currentPosition = ringtones.indexOfFirst { currentUri == it.uri } return AlertDialog.Builder(context!!) .setPositiveButton(android.R.string.ok) { _, _ -> saveCurrentUri() } .setNegativeButton(android.R.string.cancel) { _, _ -> dialog.dismiss() } .setSingleChoiceItems(adapter, currentPosition) { _, which -> currentUri = ringtones[which].uri } .create() } 

需要一个适配器才能在对话框中显示元素列表;可以按以下方式定义它:


 private val adapter by lazy { SimpleAdapter( context, ringtones.map { mapOf("title" to it.title) }, R.layout.simple_list_item_single_choice, arrayOf("title"), intArrayOf(R.id.text1) ) } 

并且您需要另一个帮助器方法来保存所选位置(单击“确定”按钮时将调用该方法):


 private fun saveCurrentUri() { PreferenceManager.getDefaultSharedPreferences(context) .edit() .putString(prefKey, currentUri?.toString()) .apply() } 

仍然需要将元素绑定到对话框,为此,我们使用对话框在文件中定义了一个辅助功能:


 fun Preference.connectRingtoneDialog(fragmentManager: FragmentManager?) = setOnPreferenceClickListener { RingtonePreferenceDialog.create(key).apply { fragmentManager?.let { show(it, "SOUND") } } true } 

并将findPreference("ringtone").connectRingtoneDialog(fragmentManager)到我们的SettingsFragment ,现在看起来应该像这样:


 class SettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.settings, rootKey) findPreference("ringtone").connectRingtoneDialog(fragmentManager) } } 

如果现在转到设置屏幕并单击“铃声”,我们将看到以下内容:



现在,将资源中的铃声添加到我们的对话中。 例如,我们在res/raw文件夹中有一个sample.mp3铃声,我们希望将其显示在列表顶部。 向对话框类添加另一个方法:


 private fun getResourceRingtones(): List<Ringtone> = listOf( Ringtone( title = "Sample ringtone", uri = Uri.parse("${ContentResolver.SCHEME_ANDROID_RESOURCE}://${context!!.packageName}/raw/sample") ) ) 

并更改onCreateDialog方法的第一行:


 ringtones = getResourceRingtones() + getAndroidRingtones() 

我们开始,看,高兴,一切都变得如此简单:



仍然需要为铃声添加“预览”。 为此,请输入其他字段:


 private var playingRingtone: android.media.Ringtone? = null 

并稍微更改setSingleChoiceItems的回调方法:


 playingRingtone?.stop() ringtones[which].also { currentUri = it.uri playingRingtone = it.uri?.let { RingtoneManager.getRingtone(context, it) } playingRingtone?.play() } 

此处发生的情况:我们停止播放当前铃声(如果不为空),将所选铃声设置为当前铃声,然后开始播放。 现在,当您在对话框中选择铃声时,它将播放。 要在对话框关闭时停止播放,请重新定义onPause方法:


 override fun onPause() { super.onPause() playingRingtone?.stop() } 

好吧,剩下的就是将主屏幕上的按钮附加到铃声,例如,如下所示:


 findViewById<Button>(R.id.playRingtone).setOnClickListener { val ringtone = PreferenceManager.getDefaultSharedPreferences(this) .getString("ringtone", null) ?.let { RingtoneManager.getRingtone(this, Uri.parse(it)) } if (ringtone == null) { Toast.makeText(this, "Select ringtone in settings", Toast.LENGTH_SHORT).show() } else { ringtone.play() } } 

仅此而已。 如承诺的那样,可以在此处获取源代码。

Source: https://habr.com/ru/post/zh-CN417883/


All Articles