I want to make a random number from 0-9 with animation.
I try to use with ValueAnimator, but no luck.
Here is my code without ValueAnimator (works):
final int min = 0;
final int max = 9;
final int random = new Random().nextInt((max - min) + 1) + min;
randomSting = String.valueOf(random);
mNumberView.setText(randomSting);
Here is with ValueAnimator that doesn't work (not works):
animator = ValueAnimator.ofInt(0, 9);
final int min = 0;
final int max = 9;
final int random = new Random().nextInt((max - min) + 1) + min;
randomSting = String.valueOf(random);
cTimer = new CountDownTimer(3000, 1000) {
public void onTick(long millisUntilFinished) {
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
mNumberView.setText(animation.getAnimatedValue().toString());
}
});
animator.start();
}
public void onFinish() {
animator.cancel();
mNumberView.setText(randomSting);
}
};
cTimer.start();
mNumberView cannot set text to randomString.
If possible to make number animator with random numbers, not using ValueAnimator
this code will generate random number between 9 and 0 every 1 second.
final int min = 0;
final int max = 9;
final int duration= 1000;
final Handler handler = new Handler();
final Runnable runnable = new Runnable() {
#Override
public void run() {
int random = new Random().nextInt(max - min + 1) + min;
handler.postDelayed(this,duration);
}
};
handler.post(runnable);
Related
I'm developing a music streaming app which has to have a crossfade function e.g. when there are 5s left to the end of the first song it should start fading out and the next song should start fading in in the very same moment so that for a moment there will be two tracks playing at once.
I was trying to use two MediaPlayer objects but always only one plays. The fade out and fade in effects work perfectly fine if I play just one track.
I check when to start the crossfade in this runnable:
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
long totalDuration = 0;
long currentDuration = 0;
if(musicPlayer.isPlaying()) {
totalDuration = musicPlayer.getDuration();
currentDuration = musicPlayer.getCurrentPosition();
// Updating progress bar
int progress = (utils.getProgressPercentage(currentDuration, totalDuration));
trackPb.setProgress(progress);
if (!trackDownloaded && currentDuration > 100) {
Log.i(TagsContainer.MUSIC_PLAYER_TAG,"next track download started");
trackDownloaded = true;
new TrackLoader().execute();
}
long crossFadeValue = currentDuration + CROSSFADE_DURATION;
if (crossFadeValue > totalDuration && !fadeStarted && currentDuration > 100) {
fadeStarted = true;
crossFade();
}
// Running this thread after 100 milliseconds
}
mHandler.postDelayed(this, 100);
}
};
Crossfade's functions look like this:
private void crossFade() {
fadeOut(musicPlayer, CROSSFADE_DURATION);
fadeIn(musicPlayer2, CROSSFADE_DURATION);
}
public void fadeOut(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = duration;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time -= 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time > 0)
h.postDelayed(this, 100);
else {
_player.stop();
_player.release();
}
}
}, 100); // 1 second delay (takes millis)
}
public void fadeIn(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = 0.0f;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time += 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time < duration)
h.postDelayed(this, 100);
}
}, 100); // 1 second delay (takes millis)
}
I defined different lock positions:
private static final int[] gears = new int[] { 0, 33, 66, 100 };
In the onStopTrackingTouch i calculate which of my lock positions is the closest to the progress state of my SeekBar:
#Override
public void onStopTrackingTouch(final SeekBar seekBar) {
int progress = seekBar.getProgress();
int distance = Math.abs(gears[0] - progress);
int index = 0;
for(int i = 1; i < gears.length; i++) {
int tempDistance = Math.abs(gears[i] - progress);
if(tempDistance < distance) {
index = i;
distance = tempDistance;
}
}
int lock = gears[index];
}
I now need to somehow set the progress state of the SeekBar to my lock position using a smooth animation.
How can i impelement that animation?
EDIT
Thanks to the answer of #bwoogie here the complete working example using a VerticalSeekBar
#Override
public void onStopTrackingTouch(final SeekBar seekBar) {
int progress = seekBar.getProgress();
int distance = Math.abs(gears[0] - progress);
int index = 0;
for(int i = 1; i < gears.length; i++) {
int tempDistance = Math.abs(gears[i] - progress);
if(tempDistance < distance) {
index = i;
distance = tempDistance;
}
}
final int lock = gears[index];
final int tick = 50;
final VerticalSeekBar verticalSeekBar = (VerticalSeekBar)seekBar;
new Thread(new Runnable() {
long now = System.currentTimeMillis();
public void run() {
while(seekBar.getProgress() != lock) {
if(now + tick < System.currentTimeMillis()) {
if(seekBar.getProgress() > lock) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
verticalSeekBar.setMax(100);
verticalSeekBar.setProgressAndThumb(seekBar.getProgress() - 1);
}
});
} else if(seekBar.getProgress() < lock) {
getActivity().runOnUiThread(new Runnable() {
public void run() {
verticalSeekBar.setMax(100);
verticalSeekBar.setProgressAndThumb(seekBar.getProgress() + 1);
}
});
} else {
break;
}
now = System.currentTimeMillis();
}
}
}
}).start();
}
What I would do is create a loop (in a new thread) that checks every few milliseconds if enough time has passed and if so then we can update the seekBar. Here is the basic idea, just throw it into a thread and implement the updateSeekBar() method since its in a thread you wont be able to access the UI thread directly.
int newPos; // the new position we are seeking to
int ticks = 50; //how many milliseconds
long now = System.getCurrentTimeMillis();
while(seekBar.getPosition() != newPos) {
if(now + ticks < System.getCurrentTimeMillis()) {
if(seekBar.getPosition() > newPos) {
updateSeekbar(seekBar.getPosition()--);}
elseif(seekBar.getPosition() < newPos{
updateSeekbar(seekBar.getPosition()++;}
else {
break; // we've reached out goal
}
now = System.getCurrentTimeMillis(); //Edit - forgot to update the time!!
}
}
I am working on a game and I am using postDelay() to make the animations smoother. A problem I am running into is that if I execute the method with postDelay() in it, the variables that the method operates on no longer gets changed in unexpected ways. This leads to weird behavior such as the bitmaps not drawing properly.
Here are the troublesome parts:
synchronized private void rotate(int dir) {
final int originalAngle = angle; //angle is the suspicious variable
final int steps = 4;
for (int i = 0; i != steps; i++) {
smoothAnimateRotate(i, steps, originalAngle, dir);
}
finishAnimateRotate(steps);
}
private void smoothAnimateRotate(final int i, final int steps, final int originalAngle, final int dir) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
angle = originalAngle + dir*(90*(i+1))/steps;
rotateStep();
}
}, 100 * (i + 1));
}
private void rotateStep() {
angle = (angle+1800) % 360;
viewdx = (int) (Math.cos(radify(angle))*(1<<16));
viewdy = (int) (Math.sin(radify(angle))*(1<<16));
moveStep();
}
private void moveStep() {
notifyViewerRedraw() ;
try {
Thread.currentThread().sleep(25);
} catch (Exception e) { }
}
When rotate() is called very quickly, I think the variable angle somehow gets messed up. This doesn't happen when I call rotate() about one second apart, but if I do it very quickly, the angle, which is only suppose to be either 0, 90, 180, or 270 gets changed to 45 degrees or something else. I can't figure out why and debugging hasn't provided fruitful results. So how do I make sure angle does get modified in such unexpected manner?
I fixed it. I put a boolean called isRunning inside the Runnables and switched it on and off.
synchronized private void rotate(int dir) {
final int originalAngle = angle;
final int steps = 4;
if (!isRunning) {
for (int i = 0; i != steps; i++) {
smoothAnimateRotate(i, steps, originalAngle, dir);
}
finishAnimateRotate(steps);
}
}
private void smoothAnimateRotate(final int i, final int steps, final int originalAngle, final int dir) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
isRunning = true;
angle = originalAngle + dir*(90*(i+1))/steps;
rotateStep();
}
}, 100 * (i + 1));
}
private void finishAnimateRotate(final int steps) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
rotateFinish();
isRunning = false;
}
}, 100 * (steps + 1));
}
in my ProgressBar I want to display the progress percentage in the 0.0 format. I have this code but the result is always rounded up or down, without displaying the number after the decimal point.
double value = Double.valueOf(data);
value = numero_a / numero_b *100;
final float numero_float = (float) value;
new Thread(new Runnable() {
float progressStatus = 0;
Handler handler = new Handler();
public void run() {
while (progressStatus < numero_float) {
progressStatus += 1;
handler.post(new Runnable() {
public void run() {
tvPercentuale.setText(progressStatus+"%");
mProgress.setProgress((int)progressStatus);
Increase variables by a factor of 10, so:
while(progressStatus < numero_float * 10)
Then increment progressStatus by 10:
progressStatus += 10;
then set text as so:
tvPercentuale.setText("" + progressStatus / 10.0f + "%");
mProgress.setProgress((int)(progressStatus/10));
NumberFormat nf = NumberFormat.getPercentInstance();
//this will make sure the format is in XX.X%
nf.setMaximumFractionDigits(1);
nf.setMinimumFractionDigits(1);
.....
tvPercentuale.setText(nf.format(progressStatus))
EDIT:
int max = mProgress.getMax();
for(int i=0; i<max; i++){
int progress = i;
float progressValue = (float)i/(float)max;
tvPercentuale.setText(nf.format(progressValue))
mProgress.setProgress(progress);
}
I'm making a simple trivia game, and running a looping ~1 min mp3 file when the user arrives at the main menu. The sound is set to stop when user clicks any of the buttons on the menu (i.e. Play Game).
My problem is that when the sound stops, its kind of a jarring cut off. Rather than do .pause() or .stop(), is there a way to make the sound slowly fade out after a button is pressed?
Thanks
EDIT (3/13/13): Updated with new QA'd code
This is my entire handler class for Android MediaPlayer. Look at the play() and pause() functions. Both contain the ability to either fade or not. The updateVolume() function was the key to let the sound increase/decrease linearly.
public class MusicHandler
{
private MediaPlayer mediaPlayer;
private Context context;
private int iVolume;
private final static int INT_VOLUME_MAX = 100;
private final static int INT_VOLUME_MIN = 0;
private final static float FLOAT_VOLUME_MAX = 1;
private final static float FLOAT_VOLUME_MIN = 0;
public MusicHandler(Context context)
{
this.context = context;
}
public void load(String path, boolean looping)
{
mediaPlayer = MediaPlayer.create(context, Uri.fromFile(new File(path)));
mediaPlayer.setLooping(looping);
}
public void load(int address, boolean looping)
{
mediaPlayer = MediaPlayer.create(context, address);
mediaPlayer.setLooping(looping);
}
public void play(int fadeDuration)
{
//Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MIN;
else
iVolume = INT_VOLUME_MAX;
updateVolume(0);
//Play music
if(!mediaPlayer.isPlaying()) mediaPlayer.start();
//Start increasing volume in increments
if(fadeDuration > 0)
{
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(1);
if (iVolume == INT_VOLUME_MAX)
{
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration/INT_VOLUME_MAX;
if (delay == 0) delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
public void pause(int fadeDuration)
{
//Set current volume, depending on fade or not
if (fadeDuration > 0)
iVolume = INT_VOLUME_MAX;
else
iVolume = INT_VOLUME_MIN;
updateVolume(0);
//Start increasing volume in increments
if(fadeDuration > 0)
{
final Timer timer = new Timer(true);
TimerTask timerTask = new TimerTask()
{
#Override
public void run()
{
updateVolume(-1);
if (iVolume == INT_VOLUME_MIN)
{
//Pause music
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
timer.cancel();
timer.purge();
}
}
};
// calculate delay, cannot be zero, set to 1 if zero
int delay = fadeDuration/INT_VOLUME_MAX;
if (delay == 0) delay = 1;
timer.schedule(timerTask, delay, delay);
}
}
private void updateVolume(int change)
{
//increment or decrement depending on type of fade
iVolume = iVolume + change;
//ensure iVolume within boundaries
if (iVolume < INT_VOLUME_MIN)
iVolume = INT_VOLUME_MIN;
else if (iVolume > INT_VOLUME_MAX)
iVolume = INT_VOLUME_MAX;
//convert to float value
float fVolume = 1 - ((float) Math.log(INT_VOLUME_MAX - iVolume) / (float) Math.log(INT_VOLUME_MAX));
//ensure fVolume within boundaries
if (fVolume < FLOAT_VOLUME_MIN)
fVolume = FLOAT_VOLUME_MIN;
else if (fVolume > FLOAT_VOLUME_MAX)
fVolume = FLOAT_VOLUME_MAX;
mediaPlayer.setVolume(fVolume, fVolume);
}
}
https://stackoverflow.com/a/29246026/922514
private static void crossFade() {
MediaPlayerManager.fadeOut(currentPlayer, 2000);
MediaPlayerManager.fadeIn(auxPlayer, 2000);
currentPlayer = auxPlayer;
auxPlayer = null;
}
public static void fadeOut(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = duration;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time -= 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time > 0)
h.postDelayed(this, 100);
else {
_player.stop();
_player.release();
}
}
}, 100); // 1 second delay (takes millis)
}
public static void fadeIn(final MediaPlayer _player, final int duration) {
final float deviceVolume = getDeviceVolume();
final Handler h = new Handler();
h.postDelayed(new Runnable() {
private float time = 0.0f;
private float volume = 0.0f;
#Override
public void run() {
if (!_player.isPlaying())
_player.start();
// can call h again after work!
time += 100;
volume = (deviceVolume * time) / duration;
_player.setVolume(volume, volume);
if (time < duration)
h.postDelayed(this, 100);
}
}, 100); // 1 second delay (takes millis)
}
public static float getDeviceVolume() {
int volumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
return (float) volumeLevel / maxVolume;
}
You can use AudioManager:
public static final int STEP_DOWN = 5; // how far each step goes down
// later on, and in a backgroud thread like an AsyncTask
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
int targetVol = 0; // or whatever you wanted.
int currentVol = am.getStreamVolume(AudioManager.STREAM_MUSIC);
while(currentVol > targetVol)
{
am.setStreamVolume(AudioManager.STREAM_MUSIC, currentVol - STEP_DOWN, 0);
currentVol = am.getStreamVolume(AudioManager.STREAM_MUSIC);
thread.sleep(100);
}
You probably also want to record the original volume they had media set to, and reset it to that after you fade out to mute and stop your music.
That code may not be exact, I don't currently have access to any way of testing it, but I hope it leads you in the right direction....