I have two separate animations for same progress bar:
#Override
public void onProgressStarted() {
mHandler.post(new Runnable() {
#Override
public void run() {
mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.setProgress(0);
mProgressBar.setMax(1000);
ObjectAnimator animation = ObjectAnimator.ofFloat (mProgressBar, "alpha", 1.0f, 0.5f); // see this max value coming back here, we animale towards that value
animation.setRepeatMode(ValueAnimator.INFINITE);
animation.setDuration (150); //in milliseconds
animation.start();
}
});
}
#Override
public void onProgressUpdate(final int progress) {
mHandler.post(new Runnable() {
#Override
public void run() {
int current = mProgressBar.getProgress();
ObjectAnimator animation = ObjectAnimator.ofInt (mProgressBar, "progress", current, progress*10); // see this max value coming back here, we animale towards that value
animation.setDuration (1000); //in milliseconds
animation.setInterpolator (new DecelerateInterpolator());
animation.start();
}
});
}
How to animate ProgressBar progress while simultaneously i have to animate width of the progress line (infinitely/pulse) or alpha in this particular case?
Related
I have a question of RotationAnimation.
First.
Is there a Listener to during animation?
start, start animation
repeat, repeat animation
stop, stop animation.
but there are no animation listener to check the on going.
second,
is any other get a current rotate angle of image?
I think, ImageView is rotate by rotationAnimation function.
so I made a timer thread and run 1second
'''
timer = new Timer();
timerTask = new TimerTask() {
public void run(){
Log.e("LOG", " [angle]: " + String.format("%3.1f", rotateImage.getRotation());
}
};
timer.schedule(timerTask, 0, 1000);
'''
but, I can't see the changed value during rotate.
how can I get the current angle during rotate?
thanks.
For Rotation Animation, Yes There is as shown below:
RotateAnimation rotateAnimation = new RotateAnimation();
rotateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
You can also use Object Animators to animate Rotation:
ObjectAnimator rotateAnimation = ObjectAnimator.ofFloat(targetView, View.ROTATION, startAngle, endAngle);
rotateAnimation.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
To get the angle of your ImageView simply use imageView.getRotation(); this will give you an int value of the current angle of rotation.
You also don't need Timer because both ObjectAnimator and rotateAnimator provide time control for you:
rotateAnimation.setDuration(1000); // run animation for 1000 milliseconds or 1 second
rotateAnimation.setStartDelay(1000); // delay animation for 1000 milliseconds or 1 second
Finally, To get rotation Angle DURING the time animation is running there is a listener method called addUpdateListener :
rotateAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue(); // dynamic value of angle
}
});
I have two things.
The first is a looping scale animation doing a kind of permanent zoom in / zoom out.
The second thing is a TimerTask that set the duration of this scale animation every 20 seconds.
The problem is that sometimes there is kind of "jump" in the animation when the setDuration() occurs.
First i put this setDuration() in the TimerTask, then I just tried to put a flag in the TimerTask and changed the duration in onAnimationEnd(), it didn't work neither, same problem. In he code below I use this flag technic.
In case it's not enough clear the goal of all this is to have an "infinite" zoom in / out of a drawable circle, the zoom in / out speed decreasing in time. It actually works but it's not smooth, as said above.
Is there a way to do this smoothly ?
The TimeTask that sets the flag "changeDurationFlag"
private void setRegularRythmeDecrease() {
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
if (elapsedTime > sessionLengthInSec) {
circle.clearAnimation();
}
zoomDuration = zoomDuration + (toDecreaseEveryUpdate / 2);
changeDurationFlag = true;
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
timer.schedule(task, 0, BREATH_RYTHME_UPDATE_INTERVAL_IN_SECONDS*1000);
}
The ScaleAnimation I use to zoom in and out
public Animation scaleAnimation(View v, float startScale, float endScale, long duration) {
Animation anim = new ScaleAnimation(
startScale, endScale,
startScale, endScale,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
anim.setFillAfter(true);
anim.setDuration(duration);
return anim;
}
The Animation listeners where the duration is set
zoomDuration = ZOOM_DURATION_START;
animZoomIn = scaleAnimation(circle, 1f, ZOOM_FACTOR,zoomDuration);
animZoomOut = scaleAnimation(circle, ZOOM_FACTOR, 1f,zoomDuration);
animZoomIn.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
// If the flag is true (modified in the TimerTask) I set the Duration to decrease the speed
// it's where the not smoothly thing happens
if(changeDurationFlag) {
Log.d("beat ","Set breath to " + String.valueOf(zoomDuration * 2d));
animZoomIn.setDuration(zoomDuration);
animZoomOut.setDuration(zoomDuration);
changeDurationFlag = false;
}
circle.startAnimation(animZoomOut);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
animZoomOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
circle.startAnimation(animZoomIn);
currentDateTime = Calendar.getInstance().getTime();
elapsedTime = currentDateTime.getTime() - startDateTime.getTime();
long elapsedTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTime);
beatCount++;
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
I had a problem with the value update of the variables elapsedTime and sessionLengthInSec. It means that sometimes (elapsedTime > sessionLengthInSec) was true when it shouldn't and a circle.clearAnimation() happened... so sure it produced a jump !
Sure the jump can still happen if the duration change is too much big, but it's kind of normal since the speed would increase rapidly. In my code it was not the case because the speed increasing is less than 200ms so hardly viewable with eyes/
Sorry for this, actually this code works perfectly...
I am trying to create a small animation which changes smoothly the background color. My problem is that it only shows the last value (100, that means it directly goes to a red background). I don't know why my created while-loop doesn't actualize every value (so that it would show a smoothly color animation)
New code (which almost works, but Idk how to stop the animation)
imageButton_info.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
final Handler handler = new Handler();
Runnable ChangeBackgroundRunnable = new Runnable() {
#Override
public void run() {
number++;
float[] hsvColor = {0, 1, 1};
hsvColor[0] = 360f * number / 100;
color.setBackgroundColor(Color.HSVToColor(hsvColor));
handler.postDelayed(this, 80);
if (number >=100)
number = 1;
}
};
number = 0;
handler.removeCallbacks(ChangeBackgroundRunnable);
handler.postDelayed(ChangeBackgroundRunnable, 0);
}
});
Code:
public void onClick(View v){
try {
while (number<=100) {
number=number+1;
float[] hsvColor = {0, 1, 1};
hsvColor[0] = 360f * number / 100;
color.setBackgroundColor(Color.HSVToColor(hsvColor));
Thread.sleep(10);
}
}catch(Exception e){
//New exception
Log.e("Camera Error!",e.getMessage());
}
Thank you for your answer in advance...
When you change something in the UI, it doesn't happen immediately. Instead, it posts a message to the Looper on the UI thread. When control returns to the looper (when you're done with whatever function the framework called), it will process all the messages on the Looper, until it eventually processes the redraw request. Then it will draw. So if you're looping in onClick, you will not get any updates to the screen. If you want something to happen in 10ms, post a delayed message to a Handler and update the UI in that thread.
Side note: NEVER EVER sleep on the UI thread. The reason is that no input or draw commands can be processed if you're not returning control to the Looper. So your app becomes unresponsive. If you do it long enough, it can even cause the framework to kill your app for being unresponsive.
A better way to do this would be to use an Android animation. I stole the code for this from here
int colorFrom = getResources().getColor(R.color.red);
int colorTo = getResources().getColor(R.color.blue);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.setDuration(250); // milliseconds
colorAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
textView.setBackgroundColor((int) animator.getAnimatedValue());
}
});
colorAnimation.start();
Wrapped the answers posted by #gabe-sechan and #jesse-buss
ValueAnimator support from devices SDK above HONEYCOMB. So below that SDK level we'll use #gabe-sechan suggestion. Check the below code.
private void executeBackgroundChange() {
// Handler and runnable to run the animation in devices sdk below honeycomb.
mHandler = new Handler();
mChangeBackgroundRunnable = new Runnable() {
#Override
public void run() {
number++;
float[] hsvColor = {0, 1, 1};
hsvColor[0] = 360f * number / 100;
mContainer.setBackgroundColor(Color.HSVToColor(hsvColor));
mHandler.postDelayed(this, 500);
if (number == 100)
number = 0;
}
};
number = 0;
mHandler.removeCallbacks(mChangeBackgroundRunnable);
mHandler.postDelayed(mChangeBackgroundRunnable, 0);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void executeBackgroundChangeUsingValueAnimator() {
colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), getResources().getColor(R.color.red), getResources().getColor(R.color.blue));
colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(final ValueAnimator animator) {
mContainer.setBackgroundColor((Integer) animator.getAnimatedValue());
}
});
colorAnimation.setRepeatCount(ValueAnimator.INFINITE);
colorAnimation.setDuration(10 * 1000);
colorAnimation.start();
}
Add the below method and to stop the animation on click of something call the below method.
private void stopBackgroundChangeAnimation() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
if (colorAnimation != null && colorAnimation.isRunning())
colorAnimation.end();
} else {
if (mHandler != null && mChangeBackgroundRunnable != null)
mHandler.removeCallbacks(mChangeBackgroundRunnable);
}
}
Check the github project for reference.
Try to use runOnUiThread
public void onCreate(Bundle savedInstanceState) {
res = getResources();
super.onCreate(savedInstanceState);
setContentView(R.layout.xyz);//**Works**/
handler.postDelayed(runnable, 10);
}
private Runnable runnable = new Runnable() {
public void run() {
runOnUiThread(new Runnable() {
public void run()
{
try {
while (number<=100) {
number=number+1;
float[] hsvColor = {0, 1, 1};
hsvColor[0] = 360f * number / 100;
color.setBackgroundColor(Color.HSVToColor(hsvColor));
}
}catch(Exception e){
//New exception
}
}
});
}
}
Now I have to create an animation of a spinning fan. When user click POWER ON button, this fan begin to spin, then keep it's spinning speed at some level.When user click POWER OFF button, it slowing down then it stop.
I make some code as follows:
ImageView mFanImageview;
private ValueAnimator mFanValueAnimator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_centripetal_particle);
ButterKnife.bind(this);
}
private void startFan() {
mFanValueAnimator = new ValueAnimator();
mFanValueAnimator.setDuration(1000);
mFanValueAnimator.setFloatValues(mFanImageview.getRotation(), mFanImageview.getRotation() + 360);
mFanValueAnimator.setInterpolator(new AccelerateInterpolator());
mFanValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mFanImageview.setRotation((Float) animation.getAnimatedValue());
}
});
mFanValueAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mFanValueAnimator = new ValueAnimator();
mFanValueAnimator.setDuration(500);
mFanValueAnimator.setFloatValues(mFanImageview.getRotation(), mFanImageview.getRotation() + 360);
mFanValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mFanValueAnimator.setRepeatMode(ValueAnimator.RESTART);
mFanValueAnimator.setInterpolator(new LinearInterpolator());
mFanValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mFanImageview.setRotation((Float) animation.getAnimatedValue());
}
});
mFanValueAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationCancel(Animator animation) {
stopFanValueAnimator().start();
}
});
mFanValueAnimator.start();
}
#Override
public void onAnimationCancel(Animator animation) {
stopFanValueAnimator().start();
}
});
mFanValueAnimator.start();
}
private ValueAnimator stopFanValueAnimator() {
ValueAnimator stopAnimator = new ValueAnimator();
stopAnimator.setDuration(1000);
stopAnimator.setFloatValues(mFanImageview.getRotation(), mFanImageview.getRotation() + 360);
stopAnimator.setInterpolator(new DecelerateInterpolator());
stopAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mFanImageview.setRotation((Float) animation.getAnimatedValue());
}
});
return stopAnimator;
}
#OnClick(R.id.stop_button)
public void onStopButtonClicked() {
mFanValueAnimator.cancel();
}
#OnClick(R.id.start_button)
public void onStartButtonClicked() {
startFan();
}
The animation above seems ok, but I found that the fan spinning speed at the end of AccelerateInterpolator animator is hard to match the beginning of LinearInterpolator.I have to adjust duration of LinearInterpolator animator carefully.
How can I get the update rate at the end of AccelerateInterpolator animator then set the right duration of LinearInterpolator animator?
I think the best approach is set accelerate interpolator initially, and on repeat again set LinearInterpolator. Now in case of repeat put a flag for handling switch off. If the switch off demanded then change value of switch Off flag that will apply Decelerate interpolator and make sure that the animation will not run again.
mFanValueAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationRepeat(Animator animation) {
if (switchOff) {
animation.setInterpolator(new DecelerateInterpolator());
} else {
animation.setInterpolator(new LinearInterpolator());
}
}
});
So what i am trying to achieve is user would open to first page of the view pager, and the view pager would bounce to half of the second page and bounce back to the fist page indicating that there are more pages to scroll to. I was wondering on how i could implement this?
You can use fakeDragBy method to achieve this effect:
viewPager.beginFakeDrag();
viewPager.fakeDragBy(offset); //offset in pixels.
viewPager.endFakeDrag();
EDIT:
I have made method for this:
private int animFactor;
private ValueAnimator animator = new ValueAnimator();
private void animateViewPager(final ViewPager pager, final int offset, final int delay) {
if (!animator.isRunning()) {
animator.removeAllUpdateListeners();
animator.removeAllListeners();
//Set animation
animator.setIntValues(0, -offset);
animator.setDuration(delay);
animator.setRepeatCount(1);
animator.setRepeatMode(ValueAnimator.RESTART);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = animFactor * (Integer) animation.getAnimatedValue();
if (!pager.isFakeDragging()) {
pager.beginFakeDrag();
}
pager.fakeDragBy(value);
}
});
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
animFactor = 1;
}
#Override
public void onAnimationEnd(Animator animation) {
pager.endFakeDrag();
}
#Override
public void onAnimationRepeat(Animator animation) {
animFactor = -1;
}
});
animator.start();
}
}
Example of usage:
animateViewPager(pager, 10, 1000);
Edit2: ValueAnimator is class for Api level 11. Also set pager adapter before calling this method.
Adding a note to #Yuraj's answer. Call the method in onWindowFocusChanged when hasFocus==true as follows to avoid indexOutOfBoundsException:
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
if(hasFocus)
{
Handler handler = new Handler();
final Runnable r = new Runnable()
{
public void run()
{
if(mViewPager.getCurrentItem() == 0)
{
Context context = Activity_main.this;
String filename="Init";
SharedPreferences stats;
stats = context.getSharedPreferences(filename, 0);
int appOpen = stats.getInt("appOpen", 0);
if(appOpen <= 5)
{
animateViewPager(mViewPager, 10, 300);
appOpen += 1;
SharedPreferences.Editor editor = stats.edit();
editor.putInt("appOpen", appOpen);
editor.commit();
}
}
}
};
handler.postDelayed(r, WAIT_VIEWPAGER_NUDGE);
}
}
Thank you Yuvraj! It worked with a simple modification. If anybody is getting "Invalid index 0, size is 0" error, here's a simple fix for it. If you call animateViewPager() method in onCreate() you might get this error, "Invalid index 0, size is 0". I believe viewpager.beginFakeDrag(); is being called before viewPager items / childs are initialized. So, call animateViewPager() with a delay like so:
new Handler().postDelayed(() -> animateViewPager(viewPager, 10, 1000), 500);
500 is the delay in milisecond