为什么notifyItemRemoved(getAdapterPosition());无法正常工作?

5
我正在使用RecyclerView展示mp3歌曲列表。现在的问题是当我尝试删除歌曲时,虽然歌曲成功删除,但是项目仍然存在于RecyclerView中,似乎重新创建了一个项目,我不太理解它的原因。
如下图所示,这是发生的情况。 enter image description here 就像您在上面的截图中看到的那样,我尝试删除一首歌曲,但歌曲仍然在列表中,并且在它后面还创建了另一个列表。我不知道发生了什么事情。
这是我的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();
    }
}

为什么不调用notifyDataSetChanged()方法呢? - Mohammad Zarei
@MohammadZarei,我也尝试过了,但它不起作用。 - Sunny Bhadana
但是你的代码中没有使用getAdapterPosition()。你提供给remove方法的那个位置实际上是布局位置。而且你还有holder.setRecyclable(false) - 为什么? - romtsn
我无法在那里访问getAdapterPosition(),并且我使用holder.setRecylable(false),因为我想要该项的确切位置。 - Sunny Bhadana
1个回答

5
这段代码真的很混乱。为什么你有这么多简单对象的列表(arrayList、songPath、songThumb、artistName、songArt),它们在哪里定义,为什么有些列表被复制到数组中,而你还有一个包含标题、作者、封面成员的复杂SongObject对象列表?
我认为数据的混乱处理是你问题的核心。
你的适配器提供的是哪个列表或数组?从适配器的构造函数和getItemCount()方法可以看出,你想让allSongs在适配器中扮演核心角色。然而,在构造函数中,你对allSongs进行了浅拷贝。更糟糕的是,立即导致你描述的症状的事实是,在deleteSong方法中,你没有从allSongs中删除项目,而是从所有其他列表中删除项目。但是,你的ViewHolder持有allSongs中SongObject对象的成员...
该怎么办?
  1. Clean up your data. Make an even more complex SongObject class (with all members necessary) and put your data into a single List of SongObjects.

  2. 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).

  3. In your deleteSong method, delete the selected SongObject by removing it from the list, like so:

    allSongs.remove(pos);
    

    Then call:

    notifyDataSetChanged();
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接