I am trying to implement a RecyclerView with audio messages and playing those audio files. Previously I was stuck on a problem when I was changing play and pause image on ImageButton, so I found this solution and changed my code accordingly. But now I am facing another issue. Whenever I play the audio , the image changes to pause and when I click this button again, it gives me an error: pause called in state 8. I know what is causing the problem, but cannot figure out the solution.
Here is my onBindViewHolder :
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyChatMediaViewHolder) holder).bindData(mChats.get(position), position);
}
Here is my MyChatMediaViewHolder class :
private class MyChatMediaViewHolder extends RecyclerView.ViewHolder {
private TextView txtUserAlphabet, timer;
private ImageView play;
private SeekBar seekBar;
ProgressBar progressBar;
RelativeLayout media_chat;
Chat chat = null;
public MyChatMediaViewHolder(View itemView) {
super(itemView);
play = (ImageView) itemView.findViewById(R.id.play);
seekBar = (SeekBar) itemView.findViewById(R.id.seekbar);
txtUserAlphabet = (TextView) itemView.findViewById(R.id.text_view_user_alphabet);
progressBar = (ProgressBar) itemView.findViewById(R.id.progressUpdate);
media_chat = (RelativeLayout) itemView.findViewById(R.id.chat_media);
timer = (TextView) itemView.findViewById(R.id.timer);
Log.e("TAG111", "bindData: ");
play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
isPlay = !isPlay;
//Handling for background selection state changed
int previousSelectState=mSelectedItemPosition;
mSelectedItemPosition = getAdapterPosition();
//notify previous selected item
notifyItemChanged(previousSelectState);
//notify new selected Item
notifyItemChanged(mSelectedItemPosition);
}
});
}
public void bindData(Chat chat, int currentPosition) {
this.chat = chat;
MediaPlayer mediaPlayer = new MediaPlayer();
MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
try {
mediaPlayer.setDataSource(chat.mediaUrlLocal);
metaRetriever.setDataSource(chat.mediaUrlLocal);
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
String duration =
metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
long dur = Long.parseLong(duration);
String seconds = String.valueOf((dur % 60000) / 1000);
String minutes = String.valueOf(dur / 60000);
String out = minutes + ":" + seconds;
timer.setText(out);
if(currentPosition == mSelectedItemPosition) {
Log.e("pause", "bindData: " + mediaPlayer.isPlaying());
if(isPlay) {
mediaPlayer.start();
play.setImageResource(android.R.drawable.ic_media_pause);
} else {
mediaPlayer.pause();
play.setImageResource(android.R.drawable.ic_media_play);
}
} else {
play.setImageResource(android.R.drawable.ic_media_play);
}
}
}
The problem here I think is : new MediaPlayer instance gets created every time
play button is clicked, so when I try to pause , a new media player is again created which was never started.
So the question is : Where should I create the new MediaPlayer instance?
First of all your RecyclerView.Adapter's purpose is to inflate your view, you shouldn't write your business logic here.
Second you should use only one instance of media player. You should initialize your media player inside your Activity or Fragment.
Create an interface implement in your Activity or Fragment and pass its reference to your adapter. Whenever you want to play audio you will use your interface to delegate your audio file path or stream to Activity or Fragment, reset or release your media player and play your audio.
I think the best solution (if you need to play only one audio at a time) would be to keep only one instance of MediaPlayer in RecyclerView.Adapter and also you can keep there a map where the key is position and value would be simple class containing: flag - isPlaying, duration (when you played your song and paused it, you wouldn't like to start it from beginning but from where you paused it), etc. So when you are scrolling up and down you will be able to retain state of views that were played/paused.
Related
Im a newbie to android studio (programming at general) but i want to build a mediaplayer for learning purposes.
I have a a list of local music in a listView that contains 2 images 'play, pause and stop button'.
This is how my app works:
Click on playbutton -> start music
Click on pausebutton -> pause music
Click on stopbutton -> stop music
Very simple. BUT! the thing is -> when i pause a song and want to play another song in my list then it just resume the first song i clicked on.
I want it to release the first song and start the other one i click on.
This is my code:
// play music
viewHolder.playB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (flag) {
//get song you clicked on
mediaPlayer = MediaPlayer.create(context, song.getSong());
//set boolean to false so it can get ANOTHER song when
//clicked
flag = false;
Toast.makeText(context, "Playing" + song.getName(),
Toast.LENGTH_LONG).show();
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
viewHolder.playB.setImageResource(R.drawable.play);
} else {
mediaPlayer.start();
viewHolder.playB.setImageResource(R.drawable.pause);
}
}
});
// stop
viewHolder.stopB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!flag) {
mediaPlayer.stop();
mediaPlayer.release();
flag = true;
}
viewHolder.playB.setImageResource(R.drawable.play);
}
});
Your logic is a little bit wrong. When you create the MediaPlayer you set flag to false and as long as you do not stop playback flag doesn't change. But your MediaPlayer creation depends on it.
For future improvements (maybe when you're more confident in working with MediaPlayer and Android) you should take a look at a more "self-made" approach instead of MediaPlayer.create(...) cause this method is calling MediaPlayer's prepare() method that is going to eventually make your app extremely slow or crash it when loading big files.
Example according to comments
I assumed songis going to be the class object.
// class variable will hold the currently loaded song
private Song mCurrentSong;
[...]
viewHolder.playB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// MediaPlayer has not been initialized OR clicked song is not the currently loaded song
if (mCurrentSong == null || song != mCurrentSong) {
// Sets the currently loaded song
mCurrentSong = song;
mediaPlayer = MediaPlayer.create(context, song.getSong());
Toast.makeText(context, "Playing" + song.getName(),
Toast.LENGTH_LONG).show();
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
viewHolder.playB.setImageResource(R.drawable.play);
} else {
mediaPlayer.start();
viewHolder.playB.setImageResource(R.drawable.pause);
}
}
});
viewHolder.stopB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// If currently loaded song is set the MediaPlayer must be initialized
if (mCurrentSong != null) {
mediaPlayer.stop();
mediaPlayer.release();
mCurrentSong = null; // Set back currently loaded song
}
viewHolder.playB.setImageResource(R.drawable.play);
}
});
I have a list of songs i put manually inside my app and it currently have 3 functions: start, pause and stop (using ImageViews).
The problem is that i can play multiple songs at the same time that is not supposed to be like that. I really cant find the issue here and hope someone can help.
This is a demonstration on my problem
I want it to STOP currently playing song when another song is being clicked on.
Here is my code for my play button:
viewholder.playB.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (currentSong == null || song != currentSong) {
currentSong = song;
mediaPlayer = MediaPlayer.create(context, song.getSong());
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
viewholder.playB.setImageResource(R.drawable.play_black);
} else {
mediaPlayer.start();
viewholder.playB.setImageResource(R.drawable.pause_black);
}
}
});
Instead of always creating a new instance of MediaPlayer every time the event onClick is called, you should create a single instance and reused it.
To play a new song don't forget to reset your player first. Here is an example:
mPlayer.reset();
mPlayer.setDataSource(context, song.getSong());
I'm working on an app in which there is a grid layout with 8 buttons and each attached to a sound. Now I'm able to play the audio on the click of a button but when I press the same button again the audio doesn't stop.
All my buttons are attached to a common onClick method and the class file retrieves the id of the button and matches with the sound file present in the raw folder.
I'm using a flag for this but don't know where I'm going wrong.
My Code
boolean play = true;
MediaPlayer mediaPlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void buttonTapped(View view)
{
int id = view.getId();
String ourID = "";
ourID = view.getResources().getResourceEntryName(id);
int resourceID = getResources().getIdentifier(ourID, "raw", "com.starprojects.gridlayoutdemo");
mediaPlayer = MediaPlayer.create(this,resourceID);
if(play)
{
mediaPlayer.start();
play = false;
}
else {
// mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
// play = true;
}
// mediaPlayer = null;
Log.i("button tapped",ourID);
}
}
define mediaPlayer outsite of function, then do that
public void buttonTapped(View view) {
int id = view.getId();
String ourID = "";
ourID = view.getResources().getResourceEntryName(id);
int resourceID = getResources().getIdentifier(ourID, "raw", "com.starprojects.gridlayoutdemo");
stopPlayer();
mediaPlayer = MediaPlayer.create(this,resourceID);
if (mediaPlayer != null)
mediaPlayer .start();
}
public void stopPlayer() {
if (mediaPlayer != null) {
mediaPlayer .stop();
mediaPlayer .release();
}
mediaPlayer = null;
}
thats all :)
This may or may not be related to your problem, but don't forget to call
mediaPlayer.release();
mediaPlayer = null;
when you are finished. If you do not, resources get built up and start affecting sound outside of the app. Keep in mind a new MediaPlayer is being allocated every time you press the button. I have done this in a previous app and the sound stopped working after a few minutes.
Let me know if this changes anything.
As a matter of fact, you can try making your MediaPlayer a member of the class (defined outside the function) since you only need one sound playing at once. If it isn't null or .isPlaying(), release it. Otherwise, create and play.
Im trying to get the seekbar to update and show progress whenever I play an mp3 with mediaPlayer. Music plays fine everytime, seekbar will always snap to 0 position when mp3 is playing.
I was trying to follow this answer but it just wont work... SeekBar and media player in android
I have this code in main activity
private Handler mHandler = new Handler();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
if (Assets.mediaPlayer != null) {
int mCurrentPosition = Assets.mediaPlayer.getCurrentPosition() / 1000;
if(OnionFragment.seekBar!= null) {
OnionFragment.seekBar.setProgress(mCurrentPosition);
}
}
mHandler.postDelayed(this, 1000);
}
});
in OnionFragment I have
public static SeekBar seekBar;
seekBar = (SeekBar) rootView.findViewById(R.id.seekBar);
and in OnionFragments onclick (the play button)I have
Assets.playMusicFile(MainActivity.items.get(MainActivity.selectedItem).getSongId(), true);
if(Assets.mediaPlayer!=null) {
seekBar.setMax(Assets.mediaPlayer.getDuration());
}
OnionFragment is loaded into MainActivity right away and looks like this
P.S. If anyone has extra time, how do i change size of seekbar ball and color
Change
seekBar.setMax(Assets.mediaPlayer.getDuration());
to
seekBar.setMax(Assets.mediaPlayer.getDuration()/1000);
I'm trying to make a soundboard app, but I'm running into a problem with handling the sound files. More specifically I do not know how to get it so when a button is pressed it plays a corresponding sound back.
I've tried using this way
Play sound on button click android
I tried implementing a media player for each sound file and button but the app crashes on start up.
I have 10 buttons with 10 corresponding sound files that are located in the raw file in src/res/raw/
Any idea on how to make it work?
//this is a breaking bad soundboard app, so please excuse the language. Sorry if you find it offensive
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
whatup = (Button)this.findViewById(R.id.button);
haveatit = (Button)this.findViewById(R.id.button2);
hello = (Button)this.findViewById(R.id.button3);
whining = (Button)this.findViewById(R.id.button7);
money = (Button)this.findViewById(R.id.button4);
yeah= (Button)this.findViewById(R.id.button8);
miserable = (Button)this.findViewById(R.id.button5);
mother = (Button)this.findViewById(R.id.button9);
gatorade = (Button)this.findViewById(R.id.button6);
science = (Button)this.findViewById(R.id.button10);
whatup.setOnClickListener(this);
haveatit.setOnClickListener(this);
hello.setOnClickListener(this);
whining.setOnClickListener(this);
money.setOnClickListener(this);
yeah.setOnClickListener(this);
miserable.setOnClickListener(this);
mother.setOnClickListener(this);
gatorade.setOnClickListener(this);
science.setOnClickListener(this);
WhatUp = MediaPlayer.create(MainActivity.this, R.raw.whatupb);
HaveAtIt = MediaPlayer.create(MainActivity.this, R.raw.haveatit);
Hello = MediaPlayer.create(MainActivity.this, R.raw.hello);
Whining = MediaPlayer.create(MainActivity.this, R.raw.stopwhining);
Money = MediaPlayer.create(MainActivity.this, R.raw.wheresmymoney);
Yeah = MediaPlayer.create(MainActivity.this, R.raw.yeahb);
Miserable = MediaPlayer.create(MainActivity.this, R.raw.miserableb);
Mother = MediaPlayer.create(MainActivity.this, R.raw.motherofgod);
Gatorade = MediaPlayer.create(MainActivity.this, R.raw.gatorade);
Science = MediaPlayer.create(MainActivity.this, R.raw.yeahscience);
}
this is for handling when the button is pressed. Obviously there needs to be more, but I was just testing 1 button and it crashes when I try starting it.
#Override
public void onClick(View view) {
if(view==whatup)
{
WhatUp.start();
}
}
Log Cat Errors:
http://imgur.com/ZWYsLl7
I assume your sounds might be sound_1, sound_2, sound_3, etc. and your buttons are button_1, button_2 and so on..
You should create a loop to get the ids in onCreate method:
for (int i = 0; i < 10; i++) {
// get the id for buttons
int btnId = getResources().getIdentifier("button_"+i, "id", getPackageName());
// get the res for sounds
int rawId = getResources().getIdentifier("sound_"+i, "raw", getPackageName());
// set a click listener to all your buttons
button[i] = (Button) findViewById(id);
button[i].setOnClickListener(new OnClickListener() {
public void onClick(View view) {
// create the media player with the raw id
mp = MediaPlayer.create(MainActivity.this, rawId);
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
// avoid NullPointerException see the links below
mp.release();
}
});
// play the sound
mp.start();
}
});
}
Read the topic reference: Using MediaPlayer. You could also get identifer with the context: How to dynamically generate the raw resource identifier in android? and this might help you for explanation of release() method: Play sound on button click - Null pointer exception and read the Releasing the MediaPlayer part from Using MediaPlayer. I'm not sure but it should do the trick..
Good luck.
UPDATE
Change this check if(view==whatup) to if(view.getId() == R.id.button).
In onClick method you need to check the id regarding the view, with getId() method.