Glitches while playing audio in Android - java

I am using MediaPlayer to play audio in Android. MediaPlayer is working perfectly but when I use a SeekBar which is updated with the duration of audio, the audio doesn't play smoothly and breaks while playing. Below is my code snippet for reference.
mSongSeekBar.setMax(mMediaPlayer.getDuration());
mSongSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mMediaPlayer.seekTo(progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
mSongSeekBar.setProgress(mMediaPlayer.getCurrentPosition());
}
}, 0, 500);
What is the reason for this behavior? How to get rid of this? Should I go with any alternative of Timer?

I had the same problem where the audio was continuously seeking by itself and causing glitches. You need to make sure the audio is seeked only when the user scrubs it and not when it is normally running. For this purpose the boolean fromUser parameter of onProgressChanged is useful. This would ensure the audio seeking only when the user scrubs it.
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(fromUser){
mMediaPlayer.seekTo(progress);
}
}

Reason
Audio is not playing continuously, it is changing every 500 millisecond, and so it feels breaking. Because Timer() is changing the value of Progress integer. And mMediaPlayer seeks to it every time automatically, as the code is written:
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mMediaPlayer.seekTo(progress);
}
How to Get rid of this
You should use boolean fromUser to change position of audio, only when user changes seekBar manually, You can do this by just a little change(using if)
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser)
mMediaPlayer.seekTo(progress);
}

You might consider overriding the onStartTrackingTouch and onStopTrackingTouch methods as well. Because when you are dragging the seek-bar the onProgressChanged is called frequently and each time it is trying to set the media player to play from a specific position and hence you are getting that cracking or breaking of your audio.
I would recommend, pausing the media player temporarily while the seek-bar being dragged and resume the audio when you are done with the dragging. The methods mentioned above might help you to achieve those behaviors.

Related

Seekbar in Android Studio

I came across a piece of code,now I am stuck with it.
SeekBar volumeControl=(SeekBar)findViewById(R.id.volumeSeekBar);
volumeControl.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,progress,0 );
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
Here I know that volumeControl is a variable of type SeekBar. In the second line of code volume control is set with a function setOnSeekBarChangeListener. I am unable to understand what's written inside the brackets of setOnSeekBarChangeListener. Can anyone please explain it in detail. I am just introduced to java and don't have much knowledge
This is a small piece of code to control volume using a seek bar.
Inside the brackets of onSeekBarChangeListener, we declare a new SeekBar.onSeekBarChangeListener which implements three methods :
onProgressChanged : This basically tracks the change in the seek bar and then sets the volume according to the amount of change.
onStartTrackingTouch : This methods contains the code which should be executed when the touch gesture starts.
onStopTrackingTouch:
This method contains the code which should be executed which the touch gesture stops.

How to constantly update a Text View

I'm trying out making an app using Android Studio and I want to constantly update a text view.
I at first thought I should use a loop but that ended up not even running for some reason.
TextView amount = findViewById(R.id.amountTextView);
final SeekBar seekBar = findViewById(R.id.seekBar);
int p = seekBar.getProgress() + 1;
amount.setText(String.valueOf(p));
});
}
}
With this method it doesn't update the results text view. I would like it to constantly update the text view so it shows what number the seek bar is on.
You will need an event for update the state of SeekBar and TextView, in this case I user a Handler(android.os) for update the views each 100 milliseconds of time, but you can use another event like the load of a web service or another.
In your case, it doesn't update because you just update the state of variable, but never update the state of seekbar, that is the reason it will never increment the current state.
private void startLoopUntilEnd() {
final Handler handler = new Handler();
final Runnable runnable = new Runnable() {
#Override
public void run() {
int progress = seekBar.getProgress();
textView.setText(String.valueOf(progress));
progress++;
seekBar.setProgress(progress);
startLoopUntilEnd();
}
};
if (currentState <seekBar.getMax()){
handler.postDelayed(runnable,100);
}
}
You can call this method from onCreate in case of Activity or onResume in case of Fragment and after it will go until the SeekBar is complete recursively.
If you want to change the max of seekBar, you can set it in xml or in code.
Use onSeekBarChangeListener:
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
amount.setTextprogress);
}
});

Android: How to run a callback right after setText() is rendered

I want to display some text in an EditText and do some work right away after the text is displayed. I have the following code in my onCreate() method:
this.editor.setText(text, TextView.BufferType.EDITABLE);
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
// Work that needs to be done right after the text is displayed
}
}, 1000);
This works OK, but I want to minimize the delay between setText() rendering and the work being done-- a 1s delay is unacceptable. However, if I change the delay to 0ms or 1ms, then the work is done before the text gets rendered.
I could keep typing in numbers to search for the perfect delay time that would execute my code just after the text was rendered, but that seems very tedious/imprecise. Is there a better way to tell Android to run a callback right after that happens? Thanks.
edit: Here are two things I've tried that didn't work. For bonus points, it would be very helpful if you could explain to me why these didn't work.
Using Handler.post
new Handler(Looper.getMainLooper()).post(r) also runs r before the text finishes rendering. I thought setText adds rendering code to the queue, so shouldn't post(r) being called after that add r after the rendering code?
Using View.post
this.editor.post(r) didn't work either, r is still called before the text is rendered.
Use this it would hlp
mSongNameTextView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
You can assign a TextWatcher to your EditText.
A TextWatcher is basically a listener that listens for changes to the text (before, during and after) in the EditText.
It can be implemented as follows:
EditText et;
et.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
// Work that needs to be done right after the text is displayed
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
}
So when you set the text explicitly, this listener should be called and after the text is changed, the // Work that needs to be done right after the text is displayed code will be run.
You can use ViewTreeObserver as below:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// do your work here. This call back will be called after view is rendered.
yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// or below API 16: yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
I initially wanted to delay the work because it was CPU-intensive. I realized the solution was to spin up a new thread for the work, rather than post it to the UI thread.

Updating Seekbar after changing Edittext

So I've got a seekbar and an edittext. My aim is to type in a number in the edittext, and in the same time the seekbar should jump to the related position. This should also work vise versa.
I've already got the code for them seperately. But something is missing. Because when I put in both and start then i can slide the seekbar and the value is changing in the same time in the edittext. But when i try to type in a number, it jumps to the beginning. Example: i want to type 123...result is 321. more or less the same with deleting.
I think the two codes are crashing each other, bit i don't know what to change.
And maybe i should say that i'm new in this field.
I'm not sure if my intentions are understandable..
Here my codes:
1.
value.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int after, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
try{
//Update Seekbar value after entering a number
int progress = Math.round(Float.parseFloat(s.toString()));
seekbar.setProgress(progress);
value.setText(value.getText().length());
} catch(Exception ex) {}
}
});
2.
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
value.setText(String.valueOf(progress));
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
As you see, there is a parameter passed into "onProgressChanged" function called "fromUser":
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
It defines that progress is changed by "physically" shfting the slider, so all You have to do, is just type as below:
if (fromUser) value.setText(String.valueOf(progress));
It prevents doing this kind of loop.
value.setText(value.getText().length());
This line is called first, changing the text.
value.setText(String.valueOf(progress));
Then this line is called, changing the text to something else. These 2 setText()'s happen so fast, you cannot see that it happened twice, you only see the result of the last one.
I propose you remove the value.setText(value.getText().length());, displaying the length of the value string is not something you want to do, if I understand your question correctly.
The problem is in your afterTextChanged() method.
1. Remove line value.setText(value.getText().length()); from afterTextChanged().
value.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence charSequence, int after, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
try {
int progress = Math.round(Float.parseFloat(s.toString()));
seekBar.setProgress(progress);
} catch (Exception e) {}
}
});
2. Update onProgressChanged() method as below, to move cursor position as per text change.
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
value.setText(String.valueOf(progress));
value.setSelection(String.valueOf(progress).length());
}
OUTPUT:
Hope this will help~
value.setText(value.getText().length()); is not using to display the length. It is used to place the cursor position at the end of edittext instead of going it to start position

Method onStartTrackingTouch and getProgress() of seekbar. First value is always 0.

For a lock/unlock system I use the onStartTrackingTouch and onStopTrackingTouch methods. And testing, I don't understand one thing:
public void onStartTrackingTouch(SeekBar seekBar) {
Log.d(TAG,"Progress: "+seekBar.getProgress());
}
The first value is always zero.
Why? I need the real value!
Can anybody help me?
Thanks in advance.
I've changed my idea, finally i've used onProgressChanged:
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(progress>95){
seekBar.setVisibility(SeekBar.INVISIBLE);
//code
}
public void onStopTrackingTouch(SeekBar seekBar) {
seekBar.setProgress(0);
}
Thanks

Categories

Resources