Ich brauchte hier für ein Projekt, um meinen Dialog mit der Wahl des Klingeltons in den Einstellungen zu führen. Aus zwei Gründen sofort: Erstens gibt es keine RingtonePreference
in der Support-Bibliothek, sodass die Verwendung des Standarddialogs in PreferenceFragmentCompat
fehlschlägt. Und zweitens musste ich zusätzlich zu den Standardmelodien einige Sounds aus den dortigen Ressourcen hinzufügen. Also wurde beschlossen, Ihren Dialog zu schreiben.
Ich werde die Erstellung eines solchen Dialogs anhand eines Beispiels einer einfachen Anwendung demonstrieren: Auf einem Bildschirm befindet sich eine Schaltfläche "Klingelton abspielen", auf die der in den Einstellungen festgelegte Klingelton abgespielt wird, und ein Link zum Einstellungsbildschirm:
Ich werde die Erstellung dieser beiden Bildschirme nicht beschreiben - alles ist wie immer da. Nur für den Fall, dass am Ende ein Link zum Repository mit dem Anwendungscode angezeigt wird.
Beginnen wir also mit der XML-Datei mit der Beschreibung des Einstellungsbildschirms. Platzieren Sie die Datei settings.xml
in res/xml
mit dem folgenden Inhalt:
<?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>
Und jetzt fügen wir diese Einstellungen zu unserem Fragment hinzu:
class SettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.settings, rootKey) } }
Starten Sie, öffnen Sie den Einstellungsbildschirm, siehe Folgendes:
Die Einführung endet hier, wir gehen zum Zweck des Artikels über. Der Plan lautet wie folgt: Wenn Sie auf "Klingelton" klicken, wird ein Dialogfeld mit einer Liste von Klingeltönen und den Schaltflächen OK und Abbrechen geöffnet. Wenn Sie einen Klingelton auswählen, wird dieser abgespielt (wie im Fall der Standard- RingtonePreference
). Wenn Sie auf OK klicken, wird er in den Einstellungen gespeichert.
Erstellen Sie also ein Dialogfragment:
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 } } }
In prefKey
wir die Taste, mit der der aktuelle Klingelton extrahiert wird, und dort wird er durch Drücken der OK-Taste aufgezeichnet.
Für die weitere Arbeit benötigen wir den Ringtone
der Hilfsklasse, deklarieren Sie ihn in unserem Fragment:
private data class Ringtone(val title: String, val uri: Uri)
Schreiben Sie eine Hilfsfunktion, mit der alle in Android integrierten Klingeltöne abgerufen werden, und senden Sie die Liste von 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) ) } }
Hier gibt ringtoneManager.cursor
den Cursor mit allen verfügbaren Klingeltönen zurück. Wir gehen einfach alle Elemente durch und ordnen sie unserer Hilfsklasse Ringtone
(es ist bequemer, mit ihnen zu arbeiten).
Lassen Sie uns zunächst die Arbeit mit der integrierten Liste der Klingeltöne organisieren. Das spätere Hinzufügen unserer Ressourcen ist sehr einfach. Erstellen Sie dazu einen Dialog, indem Sie die onCreateDialog
Methode überschreiben:
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() }
Ein Adapter wird benötigt, um eine Liste von Elementen in einem Dialogfeld anzuzeigen. Er kann wie folgt definiert werden:
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) ) }
Und Sie benötigen eine andere Hilfsmethode, um die ausgewählte Position zu speichern (sie wird aufgerufen, wenn Sie auf die Schaltfläche OK klicken):
private fun saveCurrentUri() { PreferenceManager.getDefaultSharedPreferences(context) .edit() .putString(prefKey, currentUri?.toString()) .apply() }
Es bleibt unser Element an den Dialog zu binden, dafür definieren wir eine Hilfsfunktion in der Datei mit dem Dialog:
fun Preference.connectRingtoneDialog(fragmentManager: FragmentManager?) = setOnPreferenceClickListener { RingtonePreferenceDialog.create(key).apply { fragmentManager?.let { show(it, "SOUND") } } true }
Und füge findPreference("ringtone").connectRingtoneDialog(fragmentManager)
zu unserem SettingsFragment
, jetzt sollte es so aussehen:
class SettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.settings, rootKey) findPreference("ringtone").connectRingtoneDialog(fragmentManager) } }
Wenn wir jetzt zum Einstellungsbildschirm gehen und auf "Klingelton" klicken, sehen wir ungefähr Folgendes:
Fügen Sie nun die Klingeltöne aus den Ressourcen zu unserem Dialog hinzu. Zum Beispiel haben wir einen Klingelton sample.mp3
im Ordner res/raw
und möchten ihn oben in der Liste anzeigen. Fügen Sie der Dialogklasse eine weitere Methode hinzu:
private fun getResourceRingtones(): List<Ringtone> = listOf( Ringtone( title = "Sample ringtone", uri = Uri.parse("${ContentResolver.SCHEME_ANDROID_RESOURCE}://${context!!.packageName}/raw/sample") ) )
Ändern Sie die erste Zeile in der onCreateDialog
Methode:
ringtones = getResourceRingtones() + getAndroidRingtones()
Wir fangen an, schauen, freuen uns, dass alles so einfach ist:
Es bleibt eine "Vorschau" für die Klingeltöne hinzuzufügen. Geben Sie dazu ein zusätzliches Feld ein:
private var playingRingtone: android.media.Ringtone? = null
Ändern Sie die setSingleChoiceItems
für setSingleChoiceItems
:
playingRingtone?.stop() ringtones[which].also { currentUri = it.uri playingRingtone = it.uri?.let { RingtoneManager.getRingtone(context, it) } playingRingtone?.play() }
Was hier passiert: Wir hören auf, den aktuellen Klingelton abzuspielen (wenn er nicht null ist), stellen den ausgewählten als aktuellen ein und starten die Wiedergabe. Wenn Sie nun im Dialog einen Klingelton auswählen, wird dieser abgespielt. Um die Wiedergabe zu stoppen, wenn der Dialog geschlossen wird, definieren Sie die onPause
Methode neu:
override fun onPause() { super.onPause() playingRingtone?.stop() }
Nun, alles, was bleibt, ist, die Schaltfläche auf dem Hauptbildschirm an den Klingelton anzuhängen, zum Beispiel wie folgt:
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() } }
Das ist alles. Wie versprochen kann der Quellcode hier übernommen werden .