Criando um diálogo para escolher um toque

Me levou aqui para um projeto para fazer meu diálogo com a escolha do toque nas configurações. Imediatamente por 2 motivos - primeiro, não há RingtonePreference na biblioteca de suporte, portanto, o uso da caixa de diálogo padrão em PreferenceFragmentCompat falhará. E segundo, além das melodias padrão, eu tive que adicionar alguns sons dos recursos lá. Por isso, foi decidido escrever seu diálogo.


Demonstrarei a criação desse diálogo usando um exemplo de aplicativo simples: em uma tela, há um botão "Reproduzir toque", clicando no botão que reproduz o toque definido nas configurações e um link para a tela de configurações:



Não vou descrever a criação dessas duas telas - tudo está lá como sempre. Por precaução, no final, haverá um link para o repositório com o código do aplicativo.


Então, vamos começar com o arquivo xml com a descrição da tela de configurações. Coloque o arquivo settings.xml em res/xml com o seguinte conteúdo:


 <?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> 

E agora vamos adicionar essas configurações ao nosso fragmento:


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

Inicie, abra a tela de configurações, veja o seguinte:



A introdução termina aqui, passamos ao objetivo do artigo. O plano é o seguinte: quando você clica em "Toque", uma caixa de diálogo é aberta com uma lista de toques e os botões OK e Cancelar; quando você seleciona um toque, ele é reproduzido (como no caso da Preferência de RingtonePreference padrão); quando você clica em OK, ele é salvo nas configurações.


Então, crie um fragmento de diálogo:


 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 } } } 

No prefKey transferimos a tecla pela qual o toque atual será extraído e, em prefKey será gravado pressionando o botão OK.


Para trabalho adicional, precisamos da classe auxiliar Ringtone , declare-a dentro do nosso fragmento:


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

E escreva uma função auxiliar que retirará todos os toques embutidos no Android e retornará a lista do 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) ) } } 

Aqui o ringtoneManager.cursor retornará o cursor com todos os toques disponíveis, basta percorrer todos os elementos e mapeá-los para nossa classe auxiliar Ringtone (é mais conveniente trabalhar com eles).


Primeiro, vamos organizar o trabalho com a lista interna de toques - adicionar nossos recursos posteriormente será muito simples. Para fazer isso, crie um diálogo substituindo o método 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() } 

É necessário um adaptador para exibir uma lista de elementos em um diálogo; ele pode ser definido da seguinte maneira:


 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) ) } 

E você precisa de outro método auxiliar para salvar a posição selecionada (ela será chamada quando você clicar no botão OK):


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

Resta vincular nosso elemento ao diálogo, para isso definimos uma função auxiliar no arquivo com o diálogo:


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

E adicione findPreference("ringtone").connectRingtoneDialog(fragmentManager) ao nosso SettingsFragment , agora deve ficar assim:


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

Se agora formos para a tela de configurações e clicar em "Toque", veremos algo como isto:



Agora adicione os toques dos recursos ao nosso diálogo. Por exemplo, temos um ringtone sample.mp3 na pasta res/raw e queremos exibi-lo no topo da lista. Adicione outro método à classe de diálogo:


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

E altere a primeira linha no método onCreateDialog :


 ringtones = getResourceRingtones() + getAndroidRingtones() 

Começamos, olhamos, nos alegramos por tudo ser tão simples:



Resta adicionar uma "visualização" dos toques. Para fazer isso, insira um campo adicional:


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

E altere levemente o método de retorno de chamada para setSingleChoiceItems :


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

O que acontece aqui: paramos de tocar o toque atual (se não for nulo), definimos o selecionado como atual e iniciamos a reprodução. Agora, quando você seleciona um toque no diálogo, ele toca. Para parar a reprodução quando a caixa de diálogo estiver fechada, redefina o método onPause :


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

Bem, tudo o que resta é anexar o botão na tela principal ao toque, por exemplo, assim:


 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() } } 

Isso é tudo. Como prometido, o código fonte pode ser obtido aqui .

Source: https://habr.com/ru/post/pt417883/


All Articles