I'm using RecyclerView to show the list of mp3 songs. Now the problem is with when I try to delete the song. The song is deleted successfully but the item remains in RecyclerView and I think it recreate itself actually I don't understand what it is. Here is the screen shot what happens.
As you can see in the above screen shot. I try to delete a song but the song remains in the list and another list is also creates behind it. I don't know what happens. here is my code SongFragment:-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
activityView = inflater.inflate(R.layout.fragment_song, container, false);
// SongsLibrary songsLibrary = new SongsLibrary();
// songsLibrary.getSongThumnail(getActivity(), songArt);
swipeRefreshLayout = activityView.findViewById(R.id.swiperefresh);
if(arrayList == null){
SongsLibrary songsLibrary = new SongsLibrary();
songsLibrary.getSongs(getActivity());
}
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(deteleSong);
setUpMusic();
return activityView;
}
private void setUpMusic() {
// todo when there is no activity and no song player than arraylist.size on null object
RecyclerView songRecyclerView = activityView.findViewById(R.id.song_list);
songRecyclerView.setNestedScrollingEnabled(false);
songRecyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
songRecyclerView.setLayoutManager(linearLayoutManager);
songRecyclerView.setHasFixedSize(true);
SongAdapter mAdapter = new SongAdapter(getActivity(), getMusic());
songRecyclerView.setAdapter(mAdapter);
}
public List<SongObject> getMusic() {
List<SongObject> recentSongs = new ArrayList<>();
names = new String[arrayList.size()];
names = arrayList.toArray(names);
singer = new String[artistName.size()];
singer = artistName.toArray(singer);
art = new String[songThumb.size()];
art = songThumb.toArray(art);
for(int i = 0; i < arrayList.size(); i++){
recentSongs.add(new SongObject(names[i], singer[i], art[i]));
}
return recentSongs;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action)
{
case deteleSong:
int postion = intent.getIntExtra("position",0); //todo remove item by position
break;
}
}
};
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onPause() {
super.onPause();
if(broadcastReceiver != null)
getActivity().unregisterReceiver(broadcastReceiver);
}
SongAdapter.java
public class SongAdapter extends RecyclerView.Adapter<SongViewHolder>{
private Context context;
private List<SongObject> allSongs;
MyEditText options;
SongViewHolder songViewHolder;
public SongAdapter(Context context, List<SongObject> allSongs) {
this.context = context;
this.allSongs = allSongs;
}
@Override
public SongViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.song_list_layout, parent, false);
options = view.findViewById(R.id.options);
return new SongViewHolder(view);
}
@Override
public void onBindViewHolder(SongViewHolder holder, final int position) {
songViewHolder = holder;
holder.setIsRecyclable(false);
options.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showOptions(view, position);
}
});
SongObject songs = allSongs.get(position);
holder.songTitle.setText(songs.getSongTitle());
holder.songAuthor.setText(songs.getSongAuthor());
Glide.with(context)
.load(songs.getSongCover())
.asBitmap()
.placeholder(R.drawable.player)
.error(R.drawable.player)
.override(200,200)
.fitCenter()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(holder.songImage);
}
private void showOptions(final View v, final int pos) {
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.song_options, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
int id = menuItem.getItemId();
switch (id) {
case R.id.optionPlay:
play(context, pos);
// songFragment.play(context, pos);
break;
case R.id.optionDelete:
showAlert(context, pos);
break;
case R.id.optionDetails:
// getSongDetails(context, pos);
break;
}
return true;
}
});
}
public void play(Context context, int pos){
// Start Service when User Select a song :)
Intent serviceIntent = new Intent(context, NotificationService.class);
serviceIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
serviceIntent.putExtra("pos", pos);
serviceIntent.putExtra("search","");
// Log.d("SendingData","Sended :"+ songIndex);
context.startService(serviceIntent);
//send broadcast for showing slideup panel
//send Broadcast
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(MusicActivity.mMediaStart);
broadcastIntent.putExtra("isStart", 1);
context.sendBroadcast(broadcastIntent);
}
private void showAlert(final Context context, final int position){
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.Theme_AppCompat_Light_Dialog_Alert);
builder.setMessage("Do you want to delete this song?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteSong(context, position);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
// Create the AlertDialog object and return it
builder.show();
}
public void deleteSong(Context ctx, int pos){
ArrayList<String> songList = songPath;
final String songName = songList.get(pos);
final int songPos = pos;
new android.os.Handler().postDelayed(new Runnable(){
@Override
public void run() {
// Set up the projection (we only need the ID)
String[] projection = { MediaStore.Audio.Media._ID };
// Match on the file path
String selection = MediaStore.Audio.Media.DATA + " = ?";
String[] selectionArgs = new String[] { songName };
// Query for the ID of the media matching the file path
Uri queryUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = context.getContentResolver();
Cursor c = contentResolver.query(queryUri, projection, selection, selectionArgs, null);
if (c.moveToFirst()) {
// We found the ID. Deleting the item via the content provider will also remove the file
long id = c.getLong(c.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
Uri deleteUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
contentResolver.delete(deleteUri, null, null);
} else {
// File not found in media store DB
Toast.makeText(context, "No Song Found", Toast.LENGTH_SHORT).show();
}
c.close();
}
}, 0);
File delete = new File(songName);
if(delete.exists()){
if(delete.delete()) {
//send broadcast to SongFragment to remove item from reycler View
arrayList.remove(pos);
songPath.remove(pos);
songThumb.remove(pos);
artistName.remove(pos);
songArt.remove(pos);
notifyItemRemoved(pos); //Remove item from the list
notifyItemRangeChanged(pos, arrayList.size());
// songViewHolder.itemView.setVisibility(View.GONE); //tried it but not working
//Optional
Intent intent = new Intent();
intent.setAction(SongFragment.deteleSong);
intent.putExtra("position",pos);
context.sendBroadcast(intent);
}
else
Toast.makeText(context, "Error while deleting File.", Toast.LENGTH_SHORT).show();
}
}
@Override
public int getItemCount() {
return allSongs.size();
}
}
This is is really confusing code. Why do you have all these lists of simple objects (arrayList, songPath, songThumb, artistName, songArt) and where are they defined and why are some of these lists copied to arrays, while you also have a list with complex SongObject
objects with title, author, cover members?
I believe that the confusing handling of data is at the core of your problem.
What is the list or array your adapter is serving? From the way the constructor of the adapter is designed and definitely from the getItemCount()
, it is clear that you want allSongs
to play the central role in your adapter. However, in the constructor you make a shallow copy of allSongs
. And what is worse and immediately causes the symptoms you are describing is the fact that in your deleteSong
method you do not remove items from allSongs
, but from all other lists. However, your ViewHolder
s hold members of the SongObject
objects in allSongs
...
What is to be done?
Clean up your data. Make an even more complex SongObject
class (with all members necessary) and put your data into a single List
of SongObject
s.
Pass this list to the constructor of the adapter (as you do now) and make a deep copy of the list (as you don't do now).
In your deleteSong
method, delete the selected SongObject
by removing it from the list, like so:
allSongs.remove(pos);
Then call:
notifyDataSetChanged();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With