Any Android phone has developer options to modify animation speed. Window animation scale, Transition animation scale, and Animator duration scale are the three settings I'm talking about.
The below code snippet ignores your settings:
.delay(200, TimeUnit.MILLISECONDS).subscribe
The code ignores animation settings because "delay" is not inherently tied to animations. In my code's case, it is.
How can I get this code in my app to scale based on the device's developer options animation scale settings?
Do not tie your code to animation using delay in milliseconds.
While this is an easy solution, the animation delay or duration, may differ from the value you set to it. Instead, you can use animation listeners callbacks.
So I found out how to get the system settings for a multiplier...
.delay(getScaledDelayDuration(200), TimeUnit.MILLISECONDS).subscribe
private long getScaledDelayDuration(long delay) {
float multiplier = Settings.System.getFloat(
this.getContext().getContentResolver(),
Settings.System.TRANSITION_ANIMATION_SCALE, 1);
return (long) (multiplier * delay);
}
...but that not only doesn't solve the root issue I'm having, it also is just not a good way to go about this at all. I'm thinking I should just delete the question at this point.
Related
My app relies heavily on animations to work, and when their speed is changed in the developer's options, the whole app breaks and stops working. I've been researching ways of ignoring the developer's settings and forcing the animations to run as they should, but it is not changing anything really.
I'm calling this function in my App's Application Class' onCreate:
private fun fixAnimation() {
val durationScale = Settings.Global.getFloat(contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
Log.d("fixAnimation", "durationScale: $durationScale")
if (durationScale != 1f) {
try {
ValueAnimator::class.java.getMethod("setDurationScale",Float::class.javaPrimitiveType).invoke(null, 1f)
} catch (t: Throwable) {
Log.e("fixAnimation", t.message ?: t.toString())
}
}
}
But nothing changed.
Got it from here: Make ObjectAnimator animation duration independent of global animator duration scale setting in developer options
And here: https://medium.com/#arpytoth/android-objectanimator-independent-of-global-animator-duration-fe33c808c83e
Apparently it is working for some people but not on my case.
Maybe it is because I am using ViewPropertyAnimator, and not ValueAnimator directly? But it should still work, as this is changing the ValueAnimator's duration scale setting globally, and ViewPropertyAnimator is backed by a ValueAnimator...
You are calling your code too early.
If you set a breakpoint into ValueAnimator.setDurationScale(float durationScale) you will notice that after your code has run WindowManagerGlobal will reset the duration scale back to the system value.
So one possible solution is to call your code in Activity.onResume(). Theoretically you could also put it in Activity.onCreate() however if the user (or the system, e.g. to save battery) then changes it while the app is backgrounded, it won't work.
There still are edge cases, e.g. if the system decides to change the factor while the app is in foreground or possibly in multi-window scenarios, but that's as good as it gets with this workaround. After all you likely don't want to check and fix this every second or so as there is reflection involved.
I'm trying to animate an element by CSS3 transtions using translate3d: JSFiddle.
// for start animation
$("#content")
.css("-webkit-transition", "all 100s");
.css("-webkit-transform", "translate(0, -900px)");
// for stop animation
$("#content")
.css("-webkit-transition", "none");
In desktop Chrome and Safari is good, but in the default browser on Android 4.1.x (SGSII, Galaxy Nexus, etc) this approach does not work - transition does not stop. Additionally, I note that the situation is only a relatively translate3d: with translate and position CSS props (e.g. "top", "left") it works.
The transition implementation on Android 4 seems to be buggy in cases where a transitioning hardware-rendered layer is canceled by adjusting the webkitTransitionDuration to 0 (and setting webkitTransition to 'none' or '' often implies this). This can be circumvented by using a transition duration of .001ms or similar, although this very likely still draws multiple frames.
A more practical work-around on at least certain devices is to use a negative value for the webkitTransitionDelay, forcing a new transition to take effect, but choosing this value such that the transition starts directly in its finished state.
Like so:
e.style.webkitTransition = '-webkit-transform linear 10s';
e.style.webkitTransform = 'translate3d(100px,100px,0)';
# now cancel 10s-long transition in 1s and reset transformation
setTimeout(function() {
e.style.webkitTransitionDelay = '-10s'
e.style.webkitTransform = 'translate3d(0,0,0)';
}, 1000)
Here is what I discovered with some experimentation:
Stopping a running translate2d or 3d on chrome, safari, firefox, and iphone webview can be done by setting a transition of "none" or a transition with a negative or 0 time delay and giving a new translation to the current position as described above.
This however does not work for android webview. The only solution I could find for android was to set the transition delay to a small positive number like .001s and giving a translate for the current position.
Note that in iphone webview the solution of a negative transition delay is preferable to "none" or a small positive number which will flash the final position of the ongoing transition before performing the preempting following transition.
This solves my (very similar) problem:
$el.removeClass('THE_ANIMATION').css('opacity', 0.99);
window.setTimeout(function () {
$el.css('opacity', 1);
}, 0);
I'll try to explain what I mean.
I'm developing a 2d game. When I run the code below on the small screen it works more quickly than the same code on the big screen. I think it depends on an iteration of the game loop takes more time on the big screen than on the small. How can I implement time unit or something else to it doesn't depend on the iteration of the game loop?
private void createDebris(){
if(dx<=0) return;
if(stepDebris==2){
Debris debris = new Debris(gameActivity, dx-=1280*coefX/77, 800*coefY-50*coefY, coefX, coefY);
synchronized (necessaryObjects) {
necessaryObjects.add(debris);
}
stepDebris=-1;
Log.e("COUNT", (count++)+"");
}
stepDebris++;
}
P.S. Debris is visual object which is drawn on canvas. I'll appreciate your answers. Thanks.
If, stepDebris is the unit by which you move objects on the screen - then incrementing it per draw call is wrong, because it ties the rate of movement to the framerate.
What you want is something like this
stepDebris += elapsedMilliseconds * speedFactor
where elapsedMilliseconds is the time elapsed since the game started (in mS). Once you find the correct speedFactor for stepDebris - it will move at the same speed on different machines/resolutions irrespective of framerate.
Hope this helps!
You can make an 'iteration' take x milliseconds by measuring the time it takes to do the actual iteration (= y), and afterwards sleeping (x-y) millisecs.
See also step 3 of this tutorial
Android provies the Handler API, which implements timed event loops, to control the timing of computation. This article is pretty good.
You will save battery life by implementing a frame rate controller with this interface rather redrawing as fast as you can.
I'm working on a custom tween effect targeting Android 2.2. This is not a straight View animation (many things are happening based on the progress of the tween), so the Animation classes available in 2.2 aren't sufficient (apparently Animator does this but is not available).
I've implemented the basic logic (porting JS and AS tweening engines I'd written earlier), and it seems to work fine, but is a little slow. For example, running an interval of 25ms in JS or AS produces a smooth visual effect, but seems "chunky" in the Android implementation - reducing the interval to 10ms seemed to help some but it certainly isn't as smooth as the built-in animations.
I'm using Timer and Timer task - I've read that ScheduledThreadPoolExecutor is "preferred" but from what I've read the advantages seem to be more to do with exception handling and multiple tasks (I'll only ever have the one tween running).
Is Timer particularly slow? If ScheduledThreadPoolExecutor more efficient? Is there another, better alternative that I'm not aware of?
TYIA
for future searchers, the answer was to use just a straight Handler and sendMessage (without a delay).
after lots of experimentation, including Threads, Timers, Executors, etc, the best performance, most predictable result, and simplest code was basically just:
private Handler handler = new Handler() {
#Override
public void handleMessage(final Message message) {
switch (message.what) {
case TWEEN:
try {
double progress = timeKeeper.getEasedProgress(5);
float position = (float) originalValue + ((destinationValue - originalValue) * progress));
setValue(position);
if(!timeKeeper.atEnd()){
sendEmptyMessage(TWEEN);
}
} catch (Exception e) {
}
}
}
};
where originalValue, destinationValue and setValue are just arbitrary members to handle the delta of the tween. timeKeeper is a very simple class that just measures ellapsed time, and returns the delta (ellapsed / duration). getEasedProgress just applies some basic easing interpolation to that delta.
thanks to pskink from the google android developers mailing list for pointing me in the right direction.
You do not need a ScheduledThreadPoolExecutor because you do not need a ThreadPool, you only need a single thread to manage your animation. The slowdown is probably in the implementation of your animation engine. I'm not sure how effective Timer is.
The main advantage I see with ScheduledThreadPoolExecutor is you can pool number of threads, if one thread is somehow hungup other thread can be used from the defined pool. Here is interesting SO discussion on this topic.
I was trying to make an into transition to my game by having two bitmaps slide apart, like a garage door opening from the middle and half sliding downwards and half upwards. Anyway, when I do it, it looks really choppy and the frame rate seems unstable/unreliable. Here's how I'm doing it.
public class TFView extends View{
...
public void startlevel(Canvas c){
long l =(SystemClock.currentThreadTimeMillis()-starttime)/3;//*(height/500);
if(l<1000){
c.drawBitmap(metalbottom,0,height/2+l,p);
c.drawBitmap(metaltop,0,0-l,p);}
}
public void endlevel(Canvas c){
long l =(SystemClock.currentThreadTimeMillis()-failtime)/3;
if(l>=height/2){
c.drawBitmap(metaltop, 0, 0, p);
c.drawBitmap(metalbottom, 0,height/2 , p);
}
else{
c.drawBitmap(metalbottom,0,-height/2+l,p);
c.drawBitmap(metaltop,0,height-l,p);}
}}
and i set the times for when I want to open/close the doors respectively. So what do you think I should change to make it a more smooth transition? Would converting it to surfaceview help?
I had the same problem. I know what you mean with "choppy". The animation speed is NOT consistent even though you have a time based animation i.e. you are using
SystemClock.currentThreadTimeMillis()
The choppiness is caused by currentThreadTimeMillis(). It "returns milliseconds running in the current thread". That is, when you use currentThreadTimeMillis() "time" only elapses when the current thread is RUNNING. But your renderer thread is NOT ALWAYS running - as you would expect in a multitasking environment. Thus every time the thread is not running your animation is also not "running" (time is not elapsing).
Solution: Use
SystemClock.elapsedRealtime()
I have to admit I'm not the best when it comes to animation in Android but I thought I'd contribute.
From your explanation, could you use TranslateAnimation? Your animation would then be very smooth.
As far as I'm aware, if the animations Android provides are not sufficient you should be drawing your graphics in a separate thread, implementing SurfaceView.
This may help or take a look at the Lunar Lander example.