我在这里花了一个项目来与设置中的铃声选择进行对话。 立即有两个原因-首先,支持库中没有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() } }
仅此而已。 如承诺的那样,可以在此处获取源代码。