Android - cada vez que salgo y entro a un fragment que contiene un recyclerview se duplica su lista de datos

 
Vista:

cada vez que salgo y entro a un fragment que contiene un recyclerview se duplica su lista de datos

Publicado por Daniel (1 intervención) el 25/04/2021 19:07:05
Buenos Dias/Tarde/Noche al foro, estoy haciendo un reproductor de musica que tiene 3 pantallas/fragments y cada uno de estos fragments tiene su viewmodel.

El segundo fragment es el que muestra todas las canciones que hay almacenadas en el dispositivo, por lo que este tiene un recyclerview donde se carga la lista de todas las canciones. Toda esta logica, es decir, traer los datos y cargarlos en el recyclerview se realiza en el viewmodel de este fragment.

La llamada al metodo para que traiga la lista y la cargue en el recyclerview se lleva a cabo en el metodo "onCreateView" del fragment. Y aqui viene el problema... Cada vez que salgo y entro a este fragment se carga de nuevo la lista en el recyclerview y se duplica. Esto ocurre cada vez que entro y salgo del fragment, por lo que la lista siempre esta añadiendo nuevas canciones duplicadas y se vuelve infinito.

No entiendo muy bien porque ocurre esto, he estado investigando sobre los ciclos de vida y he realizado todo tipo de cambios en el codigo, pero siempre consigo el mismo resultado.

Como suelo decir, espero que no sea un fallo tonto jeje...


SongListFragment->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class SongListFragment : Fragment(), SearchView.OnQueryTextListener {
 
    private lateinit var songsListViewModel: SongListViewModel
    private lateinit var rootView: View
 
 
    override fun <I : Any?, O : Any?> registerForActivityResult(
            contract: ActivityResultContract<I, O>,
            callback: ActivityResultCallback<O>
    ): ActivityResultLauncher<I> {
        TODO("Not yet implemented")
    }
 
    override fun <I : Any?, O : Any?> registerForActivityResult(
            contract: ActivityResultContract<I, O>,
            registry: ActivityResultRegistry,
            callback: ActivityResultCallback<O>
    ): ActivityResultLauncher<I> {
        TODO("Not yet implemented")
    }
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
 
    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        Log.d("onCreateView", "onCreateView")
        rootView = inflater.inflate(R.layout.fragment_songlist, container, false)
 
        init()
 
        (activity as AppCompatActivity?)!!.setSupportActionBar(rootView.findViewById(R.id.toolbar))
        return rootView
    }
 
    override fun onDestroyView() {
        super.onDestroyView()
    }
 
    override fun onDestroy() {
        super.onDestroy()
    }
 
 
    override fun onQueryTextSubmit(query: String?): Boolean {
        TODO("Not yet implemented")
    }
 
    override fun onQueryTextChange(newText: String?): Boolean {
        TODO("Not yet implemented")
    }
 
 
    private fun init(){
        songsListViewModel = ViewModelProvider(this).get(SongListViewModel::class.java)
        songsListViewModel.SongListViewModel(rootView, this)
        songsListViewModel.showAllDeviceSongs()
 
    }
 
 
}

SongListViewModel->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class SongListViewModel() : ViewModel() {
 
    private lateinit var view: View
    private lateinit var lifecycleOwner: LifecycleOwner
 
    private var listSongs = MutableLiveData<List<Song>>()
    private lateinit var adapter: CustomAdapteSongList
 
    fun SongListViewModel(root: View, lifecycleOwner: LifecycleOwner) {
        view = root
 
        adapter = CustomAdapteSongList(view.context, lifecycleOwner, R.layout.custom_card_song)
        setAdapter()
 
        this.lifecycleOwner = lifecycleOwner
    }
 
    fun showAllDeviceSongs(){
        setSongs()
    }
 
    fun setAllDeviceSongs(listSongs: List<Song>) {
        this.listSongs.value = listSongs
    }
 
    fun getSongsList(): LiveData<List<Song>> {
        return listSongs
    }
 
    private fun getAllDeviceSongs(): MutableLiveData<List<Song>> {
        var songList = MutableLiveData<List<Song>>()
        GlobalScope.launch(Dispatchers.Main) {
            songList.value = LocalSongsProvider.getAllDeviceSongs(view.context)
        }
        return songList
    }
 
    private fun setAdapter() {
        view.findViewById<RecyclerView>(R.id.rv_deviceSongs).layoutManager = LinearLayoutManager(view.context)
        view.findViewById<RecyclerView>(R.id.rv_deviceSongs).adapter = adapter
    }
 
    private fun setSongs() {
        getAllDeviceSongs().observe(lifecycleOwner, Observer { it ->
            it?.let {
                adapter.setSongs(it)
            }
        })
    }
 
 
}

CustomAdapter->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class CustomAdapteSongList(
        val context: Context,
        val lifecycle: LifecycleOwner,
        val layout: Int,
) : RecyclerView.Adapter<CustomAdapteSongList.ViewHolder>() {
 
    private var ListSongs: List<Song> = emptyList()
 
 
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val viewlayout = layoutInflater.inflate(layout, parent, false)
        return ViewHolder(viewlayout, context)
    }
 
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val itemsong = ListSongs[position]
 
        holder.bind(itemsong, lifecycle)
    }
 
    override fun getItemCount(): Int {
        return ListSongs.size
    }
 
    internal fun setSongs(songs: List<Song>) {
        this.ListSongs = songs
        notifyDataSetChanged()
    }
 
    class ViewHolder(viewlayout: View, val context: Context) : RecyclerView.ViewHolder(viewlayout) {
 
        fun bind(dataitem: Song, lifecycle: LifecycleOwner) {
            // itemview es el item de diseño
            // al que hay que poner los datos del objeto dataItem
            itemView.findViewById<TextView>(R.id.title_song).text = dataitem.title
            itemView.findViewById<TextView>(R.id.artist_name).text = dataitem.artistName
 
            var song_artWork = Uri.parse("content://media/external/audio/albumart")
            song_artWork = ContentUris.withAppendedId(song_artWork, dataitem.id)
 
 
            val file = File(song_artWork.getPath())
            Picasso.get()
                    .load(file)
                    .error(R.drawable.ic_standard_image_song)
                    .into(itemView.findViewById<ImageView>(R.id.iv_image_song))
 
            itemView.tag = dataitem
        }
    }
}

LocalSongsProvider->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
object LocalSongsProvider {
 
    private val allDeviceSongs = ArrayList<Song>()
 
    val projection = arrayOf(
            MediaStore.Audio.AudioColumns._ID,
            MediaStore.Audio.AudioColumns.TITLE,
            MediaStore.Audio.AudioColumns.TRACK,
            MediaStore.Audio.AudioColumns.YEAR,
            MediaStore.Audio.AudioColumns.DURATION,
            MediaStore.Audio.AudioColumns.ALBUM,
            MediaStore.Audio.AudioColumns.ARTIST_ID,
            MediaStore.Audio.AudioColumns.ARTIST,
    )
 
 
 
    private fun getSongs(cursor: Cursor?): List<Song> {
        if (cursor != null) {
            // Cache column indices.
            val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)
            val titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)
            val trackColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TRACK)
            val yearColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.YEAR)
            val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)
            val albumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)
            val artistIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID)
            val artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)
 
            while (cursor.moveToNext()) {
                // Get values of columns for a given video.
                val id = cursor.getLong(idColumn)
                val title = cursor.getString(titleColumn)
                val track = cursor.getInt(trackColumn)
                val year = cursor.getInt(yearColumn)
                val duration = cursor.getInt(durationColumn)
                val album = cursor.getString(albumColumn)
                val artistId = cursor.getInt(artistIdColumn)
                val artist = cursor.getString(artistColumn)
 
                val contentUri: Uri = ContentUris.withAppendedId(
                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                        id
                )
 
                // Stores column values and the contentUri in a local object
                // that represents the media file.
                allDeviceSongs.add(Song(id, contentUri, title, track, year, duration, album, artistId, artist))
            }
        }
        return allDeviceSongs.reversed()
    }
 
 
        fun getAllDeviceSongs(context: Context): List<Song> {
            val cursor = BuildSongCursor(context)
            Log.d("lista", getSongs(cursor).toString())
            return getSongs(cursor)
        }
 
 
        private fun BuildSongCursor(context: Context): Cursor? {
            try {
                return context.contentResolver.query(
                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                        projection,
                        null,
                        null,
                        null)
            } catch (e: SecurityException) {
                return null
            }
        }
 
    }
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
0
Responder