简体   繁体   中英

RecyclerView release MediaPlayer when activity onStop is triggered

I'm creating a simple app with one TextView and ImageButton view, I am using RecyclerView to display list of pairs of these so I wrote my custom adapter and view holder. But I have one problem how to release MediaPlayer when a user goes to the home screen. Because I initialize MediaPlayer inside adapter and I don't know how to trigger release if activity is being stopped. Here's my code:

Activity class:

public class NumbersActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_word_list);

        List<Word> words = new ArrayList<>();
        words.add(new Word("one", R.raw.number_one));
        words.add(new Word("two", R.raw.number_two));
        words.add(new Word("three", R.raw.number_three));
        words.add(new Word("four", R.raw.number_four));
        words.add(new Word("five", R.raw.number_five));

        LinearLayoutManager numbersListLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        RecyclerView listView = findViewById(R.id.word_list);
        listView.setLayoutManager(numbersListLayoutManager);
        listView.setAdapter(new WordAdapter(this, words));
    }
}

Adapter class:

public class WordAdapter extends RecyclerView.Adapter<WordAdapter.ViewHolder> {

        private Context context;
        private List<Word> words;
        private MediaPlayer mediaPlayer;

        public WordAdapter(Context context, List<Word> words) {
            this.context = context;
            this.words = words;
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View viewItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
            return new ViewHolder(viewItem);
        }

        @Override
        public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
            super.onViewDetachedFromWindow(holder);
            stopPlaying();
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.englishTranslation.setText(this.words.get(position).getDefaultTranslation());

            holder.itemView.setOnClickListener((view) -> {
                stopPlaying();
                mediaPlayer = MediaPlayer.create(this.context, this.words.get(position).getAudioResourceId());
                mediaPlayer.setOnCompletionListener((mp) -> {
                    stopPlaying();
                });
                mediaPlayer.start();
            });
        }

        @Override
        public int getItemCount() {
            return words.size();
        }

        public class ViewHolder extends RecyclerView.ViewHolder {
            TextView englishTranslation;
            ImageButton playButton;

            public ViewHolder(View wordView) {
                super(wordView);
                this.englishTranslation = wordView.findViewById(R.id.english_text_view);
                this.playButton = wordView.findViewById(R.id.play_button);
            }
        }

        private void stopPlaying() {
            if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
                mediaPlayer.reset();
                mediaPlayer.release();
                mediaPlayer = null;
            }
        }
    }

Should I pass MediaPlayer object to adapter and control it's state from activity class? Or is it possible to do it from inside of adapter?

@Override
    public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
        super.onViewDetachedFromWindow(holder);
        stopPlaying();
    }

Here you are setting to stop your player.

It is triggered when ViewHolder is not visible on screen

You should have your player instance inside Activity and control it by Interface callbacks (listener pattern) from adapter.

Also it is going to be much better for performance to have single media player instead of many inside VH

Callback should look like this:

interface PlayableItemCallback {
 void onItemVisible(int position);

 void onPlay(int position);

 void onStop(int position);
}

Implement it in your activity and use it to control your MediaPlayer with passing it in to adapter. Activity code:

public class NumbersActivity extends AppCompatActivity implements PlayableItemCallback {
    MediaPlayer mediaPlayer;
    Adapter adapter = Adapter(this)

    //    .............
    @Override
    public void onItemVisible(int position) {

        // do stuff with player
    }

    @Override
    public void onPlay(int position) {
        // do stuff with player
    }

    @Override
    public void onStop(int position) {
        // do stuff with player
    }
}

You can stop the media player using stopPlaying method. To make it work you have to create the instance of the adapter and when onStop() call just call the method from the adapter and you are done!!!

Create the instance of the adapter

private WordAdapter mWordAdapter;

 mWordAdapter = new WordAdapter(this, words)
 listView.setAdapter(mWordAdapter)

override the onStop method in your activity

 @Override
    protected void onStop() {
        super.onStop(); 
        if(mWordAdapter!=null){
            mWordAdapter.stopPlaying()
        }
    }

Make stopPlaying method public so you can access it via object.

public void stopPlaying() {
            if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
                mediaPlayer.reset();
                mediaPlayer.release();
                mediaPlayer = null;
            }
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM