HELP PLEASE !!
I'm using media player in recycler view and the problem is when different item's play buttons are clicked they all play at the same time. How can I stop the previous audio and start the new audio ?
Here is the adapter code:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
final MediaPlayer mediaPlayer = new MediaPlayer();
public void onBindViewHolder(#NonNull MyAdapter.ViewHolder viewHolder, int position) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(item.get(position).getAudio());
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
viewHolder.play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
viewHolder.play.setImageResource(R.drawable.play);
} else {
mediaPlayer.start();
viewHolder.play.setImageResource(R.drawable.pause);
}
I suppose one answer is using flag. but I don't know how to implement it.
There are multiple audios that play at the same time because there are multiple instances of the MediaPlayer object, because it's instantiated on every call to the onBindViewHolder(); i.e. each row of the RecyclerView will have a unique object.
To fix this, you need to use only a single object for all the audio, so transfer the
final MediaPlayer mediaPlayer = new MediaPlayer();
To be a direct field to the adapter class.
UPDATE:
Now change the logic of play button click
public void onBindViewHolder(#NonNull MyAdapter.ViewHolder viewHolder, int position) {
viewHolder.play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
mediaPlayer.release();
viewHolder.play.setImageResource(R.drawable.play);
}
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(item.get(position).getAudio());
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
viewHolder.play.setImageResource(R.drawable.pause);
}
}
}
UPDATE 2:
Try to transfer the play button code to the ViewHolder instead of the onBindViewHolder, and instead of the position, use getAbsoluteAdapterPosition()
public static class ViewHolder extends RecyclerView.ViewHolder {
//.....
public ViewHolder(View itemView) {
super(itemView);
play = itemView.findViewById(...);
play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// ..... Add the mediaPlayer code
}
}
}
}
This is a problem of double instantiation of MediaPlayer() class. What you have to do is to make an object class like this:
import android.media.MediaPlayer
import android.net.Uri
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
object MusicObject {
private lateinit var mediaPlayer: MediaPlayer
private val executor: ExecutorService = Executors.newCachedThreadPool()
private var flag = 0
private var musicName = ""
private var musicID = 1
private var musicUri: Uri? = null
// Function to play music
fun playMusic(mp: MediaPlayer,name: String,ID: Int,Uri: Uri) {
if (flag > 0) {
pauseMusic()
}
flag++
musicName = name
musicID = ID
musicUri = Uri
mediaPlayer = mp
executor.execute {
mediaPlayer.start()
}
}
// Function to play currently playing music again
fun playMusicAgain() {
executor.execute {
mediaPlayer.start()
}
}
// Function to pause music
fun pauseMusic() {
mediaPlayer.pause()
}
// Function to stop music
fun stopMusic() {
if (mediaPlayer.isPlaying) {
mediaPlayer.stop()
}
}
fun getMediaPlayer(): MediaPlayer {
return mediaPlayer
}
}
And call this class to play and pause the music.
To play music:
MusicObject.playMusic(MediaPlayer.create(this, songUri), idOfMusic, songID, songUri)
To pause music:
MusicObject.pauseMusic()
Related
hei, can you help me?, i have 2 activity and i want to stop mediaPlayer in another activity, how to use the button in first activity to stop media player in the second activity,
my code in first activity
public class homenor extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_homenor);
Button btmateri = (Button) findViewById(R.id.btmateri);
btmateri.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(homenor.this, home.class);
home.mediaPlayer.stop();
home.mediaPlayer2.stop();
startActivity(intent);
}
});
}
and this code in second activity
public class home extends AppCompatActivity {
int [] sound, soalkuis;
int soundke = 0;
int getNosoal = 0;
public static MediaPlayer mediaPlayer, mediaPlayer2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
sound = new int[] {R.raw.definisiprisma1, R.raw.definisiprisma2, R.raw.definisiprisma3,R.raw.jaringprisma1,R.raw.luasprisma1, R.raw.luasprisma2
,R.raw.luasprisma3,R.raw.luasprisma4,R.raw.luasprisma5,R.raw.luasprisma6,R.raw.contohluasprisma1,R.raw.penyelesaianluasprisma1,R.raw.contohluasprisma2
,R.raw.penyelesaianluasprisma2,R.raw.contohluasprisma3, R.raw.penyelesaianluasprisma3, R.raw.volumeprisma1, R.raw.contohvolume1
, R.raw.penyelesaianvolumeprisma1, R.raw.contohvolume2, R.raw.penyelesaianvolumeprisma2, R.raw.contohvolume3, R.raw.penyelesaianvolumeprisma3
, R.raw.rangkumanprisma1, R.raw.rangkumanprisma2};
soalkuis = new int[] {R.raw.soalprisma1, R.raw.soalprisma2, R.raw.soalprisma3, R.raw.soalprisma4, R.raw.soalprisma5, R.raw.soalprisma6
, R.raw.soalprisma7, R.raw.soalprisma8, R.raw.soalprisma9, R.raw.soalprisma10};
mediaPlayer = new MediaPlayer();
mediaPlayer2 = new MediaPlayer();
mediaPlayer = MediaPlayer.create(home.this, sound[soundke]);
mediaPlayer2 = MediaPlayer.create(home.this, soalkuis[getNosoal]);
mediaPlayer2.setLooping(false);
mediaPlayer.setLooping(false);
mediaPlayer.start();
when i try that, i got error like thisjava.lang.NullPointerException: Attempt to invoke virtual method 'void android.media.MediaPlayer.stop()' on a null object reference
how can i fix that? thank you
create class MyMediaPlayer,
public class MyMediaPlayer {
private static MyMediaPlayer Instance;
MediaPlayer mediaPlayer;
static MyMediaPlayer getMediaPlayerInstance() {
if (Instance == null) {
return Instance = new MyMediaPlayer();
}
return Instance;
}
public void playAudioFile(Context context, int sampleAudio) {
mediaPlayer = MediaPlayer.create(context, sampleAudio);
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
public void stopAudioFile() {
if (mediaPlayer != null) {
mediaPlayer.stop();
}
}}
Now, Call this method to play audio
MyMediaPlayer.getMediaPlayerInstance().playAudioFile(this, R.raw.sampleaudio)
To Stop Audio, Call this method
MyMediaPlayer.getMediaPlayerInstance().stopAudioFile()
Take the media player as public which you are already taking, then from the second activity, stop the media player like this
try {
mp.reset();
mp.prepareAsync();
mp.stop();
mp.release();
mp = null;
} catch (Exception e) {
e.printStackTrace();
}
my suggestion is to create a class like MediaPlayerManager or ...
create MediaPlayer instance inner this class and implement your interface with your method like pause, play, stop, ...
so, when need action MediaPlayer get instance this class and call suitable method
I am using the lists of audio on my Recyclview and it is working correct means I am able to play the audio file on my Recyclview.
As i am using the Seekbar with my audio file on my Recyclview , the problem is generating that when i am scrolling the Recyclview the other items's of Recyclview Seekbar is changing (Seekbar i am using for the progress of the audio file play.)
What i want that other Seekbar item of Recyclview sholud not be change when i scroll the Recyclview
Please check my code for it ,Inside the onBindViewHolder for my Recyclview , i am using the following code, please check it once.
((MyAudioChat) holder).sbMyAudio.setTag(position);
((MyAudioChat) holder).imgPlayAudio.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((MyAudioChat) holder).sbMyAudio.removeCallbacks(null);
((MyAudioChat) holder).sbMyAudio.setProgress(0);
if (mediaPlayer == null) {
mediaPlayer();
((MyAudioChat) holder).sbMyAudio.setMax(mediaPlayer.getDuration());
} else {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer();
((MyAudioChat) holder).sbMyAudio.setMax(mediaPlayer.getDuration());
}
((MyAudioChat) holder).sbMyAudio.getTag();
((MyAudioChat) holder).sbMyAudio.setMax(mediaPlayer.getDuration()); // Set the Maximum range of the
((MyAudioChat) holder).sbMyAudio.setProgress(mediaPlayer.getCurrentPosition());// set current progress to song's
Runnable moveSeekBarThread = new Runnable() {
public void run() {
if (mediaPlayer.isPlaying()) {
int mediaPos_new = mediaPlayer.getCurrentPosition();
int mediaMax_new = mediaPlayer.getDuration();
((MyAudioChat) holder).sbMyAudio.setMax(mediaMax_new);
((MyAudioChat) holder).sbMyAudio.setProgress(mediaPos_new);
((MyAudioChat) holder).sbMyAudio.postDelayed(this, 100); //Looping the thread after 0.1 second
((MyAudioChat) holder).sbMyAudio.getTag();
}
}
};
((MyAudioChat) holder).sbMyAudio.removeCallbacks(moveSeekBarThread);
((MyAudioChat) holder).sbMyAudio.postDelayed(moveSeekBarThread, 100);
}
});
I have visited the following site on SO but did not get the relevant solution
1 .First Link
2. Second Link
Can we use the setTag() and getTag() to get rid of from this problem. I have used the setTag() on my above code. Please check it once.
Although, you have asked only about correct SeekBar updates, I am assuming (because you haven't shard complete source) that soon you will face following issues which are linked to the Adapter implementation:
How do I remove the seekBar updater, when MediaPlayer completes the playback of audio?
How do I release the MediaPlayer when activity is paused?
Anonymous View.OnClickListeners and Runnables are being allocated on every onBindViewHolder call. How do I minimize their unnecessary allocation?
You can find complete working solution here - GitHub
Following source tries to fix all above issues. Certain data structures/classes are assumed based on your nomenclature.
public class MainActivity extends Activity {
private MediaPlayer mediaPlayer;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));
AudioChat audioChats[] = new AudioChat[128];
rv.setAdapter(new MyAdapter(Arrays.asList(audioChats)));
}
#Override
protected void onPause() {
super.onPause();
if (null != mediaPlayer) {
mediaPlayer.release();
mediaPlayer = null;
}
}
private class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyAudioChat> {
private List<AudioChat> audioChats;
private int currentPlayingPosition;
private SeekBarUpdater seekBarUpdater;
MyAdapter(List<AudioChat> audioChats) {
this.audioChats = audioChats;
this.currentPlayingPosition = -1;
seekBarUpdater = new SeekBarUpdater();
}
#Override
public MyAudioChat onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyAudioChat(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));
}
#Override
public void onBindViewHolder(MyAudioChat holder, int position) {
if (position == currentPlayingPosition) {
seekBarUpdater.playingHolder = holder;
holder.sbMyAudio.post(seekBarUpdater);
} else {
holder.sbMyAudio.removeCallbacks(seekBarUpdater);
holder.sbMyAudio.setProgress(0);
}
}
private class SeekBarUpdater implements Runnable {
MyAudioChat playingHolder;
#Override
public void run() {
if (null != mediaPlayer && playingHolder.getAdapterPosition() == currentPlayingPosition) {
playingHolder.sbMyAudio.setMax(mediaPlayer.getDuration());
playingHolder.sbMyAudio.setProgress(mediaPlayer.getCurrentPosition());
playingHolder.sbMyAudio.postDelayed(this, 100);
} else {
playingHolder.sbMyAudio.removeCallbacks(seekBarUpdater);
}
}
}
#Override
public int getItemCount() {
return audioChats.size();
}
class MyAudioChat extends RecyclerView.ViewHolder implements View.OnClickListener {
SeekBar sbMyAudio;
ImageView imgPlayAudio;
MyAudioChat(View itemView) {
super(itemView);
imgPlayAudio = (ImageView) itemView.findViewById(R.id.imgPlayAudio);
imgPlayAudio.setOnClickListener(this);
sbMyAudio = (SeekBar) itemView.findViewById(R.id.sbMyAudio);
}
#Override
public void onClick(View v) {
currentPlayingPosition = getAdapterPosition();
if (mediaPlayer != null) {
if (null != seekBarUpdater.playingHolder) {
seekBarUpdater.playingHolder.sbMyAudio.removeCallbacks(seekBarUpdater);
seekBarUpdater.playingHolder.sbMyAudio.setProgress(0);
}
mediaPlayer.release();
}
seekBarUpdater.playingHolder = this;
startMediaPlayer();
sbMyAudio.setMax(mediaPlayer.getDuration());
sbMyAudio.post(seekBarUpdater);
}
}
private void startMediaPlayer() {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.mp3);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
seekBarUpdater.playingHolder.sbMyAudio.removeCallbacks(seekBarUpdater);
seekBarUpdater.playingHolder.sbMyAudio.setProgress(0);
mediaPlayer.release();
mediaPlayer = null;
currentPlayingPosition = -1;
}
});
mediaPlayer.start();
}
}
private class AudioChat {
}
}
i want to play audio files(MP3) by clicking on Item view , but the problem that i want the previous file to stop automatically when i click on the other one and so on.
public ViewHolder(View itemView) {
super(itemView);
itemImage =(ImageView)itemView.findViewById(R.id.item_image);
itemTitle =(TextView)itemView.findViewById(R.id.item_title);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = getAdapterPosition();
stopPlaying();
mp = MediaPlayer.create(v.getContext(), sounds[position]);
mp.start();
}
});
}
private void stopPlaying() {
if (mp != null&&mp.isPlaying()) {
mp.stop();
mp.release();
mp = null;
}
}
the audio file still running while playing the other one.
Well, according to e.g. MVP pattern, I'd suggest you to move all the sound-playing logic out of the ViewHolder. The simpliest solution is to create listener, passed to Adapter which will notify some sort of manager:
public interface PlayerListener {
void soundChanged(Object sound);
}
Than you can create Adapter like this (whatever your sound object is)
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
(...)
Object[] sounds;
PlayerListener playerListener;
public Adapter(Object[] sounds, PlayerListener playerListener) {
this.sounds = sounds;
this.playerListener = playerListener;
}
#Override
public void onBindViewHolder(ViewHolderholder, int position) {
holder.bind(playerListener, (...) );
}
public ViewHolder(View itemView) {
public void bind(playerListener, (...)) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
soundChanged(sounds[position]);
}
}
}
}
}
Of course while creating Adapter object you have to pass the reference of object implementing PlayerListener. This object can easly handle sound switching simply storing reference to current MediaPlayer object.
public class SoundPlayer implements PlayerListener {
MediaPlayer currentMediaPlayer;
#Override
public void soundChanged(Object sound) {
stopPlaying();
currentMediaPlayer= MediaPlayer.create(context, sound);
currentMediaPlayer.start();
}
private stopPlaying() {
if (currentMediaPlayer!= null && currentMediaPlayer.isPlaying()) {
currentMediaPlayer.stop();
currentMediaPlayer.release();
currentMediaPlayer= null;
}
}
}
Hope this helps! :)
I doubt you have declared MediaPlayer in ViewHolder.
If you declared MediaPlayer in ViewHolder it will create a different instance for each item in your ViewHolder, so when stopPlaying() is called it is accessing new instance which might not be playing so it wont stop.
Declare the MediaPlayer Globally or Activity or in Adapter - pass the instance of MediaPlayer to Viewholder and try the same it should work.
I start mediaplayer like this:
if (mp != null) {
mp.stop();
mp.reset();
mp.release();
}
mp = MediaPlayer.create(this, R.raw.background);
mp.start();
How can I stop in another activity? It continues to play in another activity. How can I use onDestroy in another activity?
Use the Separate class like below in your project.
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
public class AudioPlay {
public static MediaPlayer mediaPlayer;
private static SoundPool soundPool;
public static boolean isplayingAudio=false;
public static void playAudio(Context c,int id){
mediaPlayer = MediaPlayer.create(c,id);
soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
if(!mediaPlayer.isPlaying())
{
isplayingAudio=true;
mediaPlayer.start();
}
}
public static void stopAudio(){
isplayingAudio=false;
mediaPlayer.stop();
}
}
Playing the song
`AudioPlay.playAudio(mContext, R.raw.audiofile);` // play it from your preferred activity. and you can change raw file to your path also its depends upon your requirement.
then
stop the audio using this lines AudioPlay.stopAudio(); from any activity.
hope this helps.
In 1st activity override onPause
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mp.stop();
}
This is my Kotlin solution:
package com.programacionymas.myapp.services
import android.content.Context
import android.media.MediaPlayer
object AudioPlay {
var mediaPlayer: MediaPlayer? = null
var lastResource: Int? = null
fun playAudio(c: Context, id: Int, isLooping: Boolean = true) {
createMediaPlayer(c, id)
mediaPlayer?.let {
it.isLooping = isLooping
if (!it.isPlaying) {
it.start()
}
}
}
private fun createMediaPlayer(c: Context, id: Int) {
// in case it's already playing something
mediaPlayer?.stop()
mediaPlayer = MediaPlayer.create(c, id)
lastResource = id
}
// usually used inside the Activity's onResume method
fun continuePlaying(c: Context, specificResource: Int? = null) {
specificResource?.let {
if (lastResource != specificResource) {
createMediaPlayer(c, specificResource)
}
}
mediaPlayer?.let {
if (!it.isPlaying) {
it.start()
}
}
}
fun pauseAudio() {
mediaPlayer?.pause()
}
}
I started from itsrajesh4uguys's Java answer, and I applied these changes:
Remove the Boolean attribute, since it was not being used
Add an attribute to track the last loaded resource, this way I can continue playing it or replace with other resource
Call the stop function before creating a new instance, to avoid overlapping
Finally I use it this way:
In the onCreate method:
AudioPlay.playAudio(this, R.raw.background_music)
In the onResume method:
AudioPlay.continuePlaying(this, R.raw.background_music)
In my case I had to specify the resource, because some of my Activities start playing another music sounds.
you cannot call stop an activity but from the activity itself to
achive this you can send the media player in a service and bind to the service in the activities you want to access it
As you have started media player in first activity and wanted to stop in another activity, just call your second layout in first activity using layout inflater self instead of creating another activity.. and on second layout file just stop the media player by pressing a button
public class FirstAvtivity extends Activity
{
MediaPlayer mPlayer;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_activity_layoutfile);
Button b=(Button)findViewById(R.id.button1);
//start the media player like how you were starting in your activity
// then after clicking button you will be navigated to new layout , there
// you can stop media player
mPlayer.start();
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateLayout();
}
});
}
private void newUpdateLayout() {
LayoutInflater inflater = LayoutInflater.from(this);
setContentView(inflater.inflate(R.layout.second_disapr_scr, null));
finalDismiss=(Button)findViewById(R.id.final_dismiss);
finalDismiss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(),"welcome to second
avtivity",Toast.LENGTH_SHORT).show();
mPlayer.stop();
finish();
}
});
}
}
I have implemented a class that extends MediaPlayer.
public class AudioPlayer extends MediaPlayer {
private String fileName = null;
FileInputStream fileInputStream = null;
private MediaPlayer mediaPlayer = null;
// Constructor
public AudioPlayer(Context context)
{
// Initialization
}
public void onPlay(boolean start)
{
if(start) {
startPlaying(this.fileName);
}else {
stopPlaying(this.fileName);
}
}
private void startPlaying(String fileName) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(fileInputStream.getFD());
mediaPlayer.prepare();
mediaPlayer.start();
// mediaPlayer.setOnCompletionListener(new CompletionListener());
}
class CompletionListener implements MediaPlayer.OnCompletionListener {
#Override
public void onCompletion(MediaPlayer mp) {
// Do stuff
}
}
When I setOnCompletionListener on MediaPlayer object inside my custom AudioPlayer class it works just fine. However I would like to set this listener on the object created from this class since it's used in multiple activities. Different actions should be taken in onCompletion() method therefore implementing onCompletionListener inside my custom class is pointless.
When I create an instance of my custom class in each activity where I want to use it
AudioPlayer audioPlayer = new AudioPlayer(context);
and set on it onCompletionListener:
audioPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
}
});
it’s never being called. Is there a way to call onCompletionListener on my custom object?
The solution is to write a method that returns MediaPlayer object and then call setOnCompletionListener() on this object:
public MediaPlayer getActiveMediaPlayer()
{
if ( mediaPlayer != null )
return this.mediaPlayer;
else
return null;
}
and then in the activity:
audioRecorder.getActiveMediaPlayer().setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.d("MediaPlayer", "test");
}
});