Scale animation in Android degrades image quality - android

In Android 5.0 (API Level 21) and above, you can define vector
drawables, which scale without losing definition.
I use Vector Assets in Android Studio (API 28). Example: ic_mybutton.xml.
Vector drawables are displayed on Views in original quality: image, image, image.
ImageButton button = new ImageButton(context);
button.setImageResource(R.drawable.ic_mybutton);
Using this animation on Buttons and ImageButtons (or other Views):
Animation animation = new ScaleAnimation(
1.0f, 0.9f,
1.0f, 0.9f,
Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 1f);
animation.setFillAfter(true);
animation.setDuration(1000);
button.startAnimation(animation);
produces terrible image distortion: image, image, image.
What am I doing wrong?

The problem can be solved using the animator.
All other known libraries, such as Boom, RxAnimation and the like, degrade image quality when scaling.
anim_scale.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="#android:integer/config_shortAnimTime"
android:propertyName="scaleX"
android:repeatCount="1"
android:valueFrom="1.0"
android:valueTo="0.9"
android:valueType="floatType" />
<objectAnimator
android:duration="#android:integer/config_shortAnimTime"
android:propertyName="scaleY"
android:repeatCount="1"
android:valueFrom="1.0"
android:valueTo="0.9"
android:valueType="floatType" />
</set>
animated_vector.xml:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/ic_mybutton">
<target
android:animation="#animator/anim_scale"
android:name="anim_scale" />
</animated-vector>
Put your paths in <group> tag and set the pivot.
ic_mybutton.xml:
<group
android:name="anim_scale"
android:pivotX="177.5"
android:pivotY="37.5">
...
</group>
Thus (with slight changes), you can make an animation of a button click without losing vector image quality:
ImageButton button = findViewById(R.id.button_apply);
button.setImageResource(R.drawable.animated_vector);
button.setOnClickListener(v -> ((Animatable) button.getDrawable()).start());
P.S.
Following method does not work (losing quality):
ObjectAnimator animator = ObjectAnimator.ofFloat(button, "scale", 0.9f);
animator.setDuration(200);
animator.start();
And StateListAnimator class also degrades image quality.

Related

How to Create Android Animated Vector Drawable

I have https://lottiefiles.com/share/gordjiyb Lottie file for which I want to create Android Animated Vector Drawable,
We have used the Fresco library and converted the Lottie file into GIF in the drawable folder, which is taking 600 kb space.
I am sure Animated Vector Drawable will be more space sufficient.
You can do the next
Create a file that it is a html
Import the file in the assets folder
Add an webview component in the view
Load the file in the component
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.webkit.WebView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val webview = findViewById(R.id.webview)
webview.loadUrl("file:///android_asset/index.html")
}
}
create an animation inside the res/animator resource folder (create it if it doesn't exist) and let's call it expand1 like so:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<set>
<objectAnimator
android:propertyName="scaleX"
android:duration="1000"
android:valueTo="0.5"
android:interpolator="#android:anim/linear_interpolator"/>
<objectAnimator
android:propertyName="scaleY"
android:duration="1000"
android:valueTo="0.5"
android:interpolator="#android:anim/linear_interpolator"/>
</set>
<set>
<objectAnimator
android:propertyName="scaleX"
android:duration="1000"
android:valueTo="1.5"
android:interpolator="#android:anim/linear_interpolator"/>
<objectAnimator
android:propertyName="scaleY"
android:duration="1000"
android:valueTo="1.5"
android:interpolator="#android:anim/linear_interpolator"/>
</set>
</set>
this will create an object animator that will in turn scale up and down an icon
then create 3 more expand2 expand3 expand4
on each one change the scaleX and scaleY to be from 0.5 to 1.5 , to 0.7 to 1.3 etc, according to how big your circles should be at minimum and maximum
then create your vector drawable (ic_sound.xml) like so:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group
android:name="icon">
<path
android:fillColor="#android:color/white"
android:pathData=" draw the microphone icon here "/>
</group>
<group
android:name="circle1">
<path
android:fillColor="#android:color/holo_green_light"
android:fillAlpha="0.2"
android:pathData=" draw the darkest circle here "/>
</group>
repeat three more times for circle2, circle3 and circle4 with different fillAlphas
</vector>
finally create a vector drawable like so:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/ic_sound">
<target
android:name="circle1"
android:animation="#animator/expand1" />
<target
android:name="circle2"
android:animation="#animator/expand2" />
<target
android:name="circle3"
android:animation="#animator/expand3" />
<target
android:name="circle4"
android:animation="#animator/expand4" />
</animated-vector>
what this does is that it tells the ic_sound vector, to apply an animator to each group , so circle1 will expand from 0.5 to 1.5 , circle 2 will expand from 0.7 to 1.3 and so on
you can play with the times (if you want the animation to last longer) how long it will play , the interpolators (to add an accelerate or decelerate effect) to make it look as you wish

Android - objectAnimator xml alpha animation not working

Using alpha as the propertyName of the objectAnimator does nothing at all. The objectAnimator is connected to a path inside the vector.
fading_animator.xml:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="700"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:repeatCount="infinite"
android:repeatMode="restart"/>
</set>
animated_vector.xml:
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/vector_drawable">
<target
android:name="pathTarget"
android:animation="#animator/fading_animator"/>
</animated-vector>
When trying to animate the alpha of a path, you have to use either fillAlpha or strokeAlpha. Similarly, scaleX and scaleY will not work on a path but it will work on a group inside the vector.

Android Animated Vector Drawable: change rotation degree at runtime

I'm trying to make an Android vector animation, which contains a rotation on a vectorial group. Basically, if the degree transition would be constant, I would use those resources:
My VectorDrawable:
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportHeight="600"
android:viewportWidth="600">
<group
android:name="myRotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="0">
<path
android:name="myName"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z"/>
</group>
</vector>
My AnimatedVectorDrawable:
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/my_vector_drawable" >
<target
android:name="myRotationGroup"
android:animation="#animator/my_rotation" />
</animated-vector>
And my ObjectAnimator:
<objectAnimator
android:duration="500"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
However, as you can see above, the final degree of rotation is set in the ObjectAnimator resource, as 360° for instance.
For my animation, how can I change programmatically this final value? The rotation degree need to be computed with some other data, so I don't know the target value before the animation starts.
Thank you for your help!
I think the solution is to create an object of ObjectAnimator in the class like this
ObjectAnimator myRotation = ObjectAnimator.ofFloat(target,"rotation",360f);
myRotation.setDuration(500);
myRotation.start(); // this will start the animation
// here target is the object of the view on which you want to apply this animation
// and you can change this 360f to any number at which degree you want to rotate
for more information check this out

Android Animation ObjectAnimator Pivot from center

Another simple question that I can't seem to wrap my head around. I want an animation using ObjectAnimator to scale upwards from the center. However, I'm not sure how to set the PivotX/Y properties as any value I apply doesn't seem to affect the view. When I was using a scaleanimation, it worked fine but I must use an ObjectAnimator here.
I've tried
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view,"scaleX",0.0f,1.0f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view,"scaleY",0.0f,1.0f);
//I've tried a wide range of values from 0,0 to 0,0.5, to 0.5, 0.5 but none of them do anything
ObjectAnimator pivotX = ObjectAnimator.ofFloat(view,"pivotX",0,1f);
ObjectAnimator pivotY = ObjectAnimator.ofFloat(view,"pivotY",0,1f);
//I've also tried view.setPivotX(0.5f) but that didn't do anything either
animatorSet.playTogether(scaleX,scaleY,pivotX,pivotY);
animatorSet.start();
I'm just not really sure how to make it scale from the center. I've tried not even using pivot but that didn't do anything either.
Any help is appreciated,
Thanks
** EDIT **
The following sort of works, except it isn't completely centered it instead grows towards the top left but still sort of centered. Its hard to describe. I tried using 0.5f and it didn't work either
ObjectAnimator pivotX = ObjectAnimator.ofFloat(view,"pivotX",1f);
ObjectAnimator pivotY = ObjectAnimator.ofFloat(view,"pivotY",1f);
I was having a similar problem scaling a vector animation defined in XML. It now scales from its center after placing the pivotX & pivotY settings in the Drawable instead of the ObjectAnimator, like so:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt" >
<aapt:attr name="android:drawable">
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="100"
android:viewportHeight="100">
<group
android:name="scaleGroup"
android:pivotX="50"
android:pivotY="50"
android:scaleX="1.0"
android:scaleY="1.0" >
<path
android:name="v"
android:strokeColor="#00e9bd"
android:strokeWidth="6"
android:pathData="M 50 13 C 70.4345357437 13 87 29.5654642563 87 50 C 87 70.4345357437 70.4345357437 87 50 87 C 29.5654642563 87 13 70.4345357437 13 50 C 13 29.5654642563 29.5654642563 13 50 13 Z" />
</group>
</vector>
</aapt:attr>
<target android:name="scaleGroup"> *
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="0.5"
android:valueTo="1.0" />
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:valueFrom="0.5"
android:valueTo="1.0" />
</set>
</aapt:attr>
</target>
</animated-vector>
If you want to scale upwards one clear option is:
view.setPivotY(100);
and downwards:
view.setPivotY(0);
then animate.
pivotX and pivotY should be 1/2 the viewportWidth and viewportHeight respectively if you want to scale from the center.
These properties should be set on the group tag of your vector drawable. This group tag should be outside of the path tag that you want to scale, similar to the answer above.

Creating animation at runtime in Android

i can use an XML file like below
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator">
<translate android:fromXDelta="100%" android:toXDelta="0%" android:duration="500" />
</set>
and load this xml from code like
AnimationUtils.loadAnimation(mContext, com.....R.anim.slidein)
everything works fine
But now for some reason i need to do that same thing without using XML
how do i create the same animation using just code
i tried something like this
TranslateAnimation in = new TranslateAnimation(1.0f,0.0f,0.0f,0.0f);
in.setInterpolator(AnimationUtils.loadInterpolator(mContext,
android.R.anim.accelerate_interpolator));
in.setDuration(500);
but didnt work, nothing animates
i think the problem is with percentages, in the xml i have specified percentages but in the TranslateAnimation constructor how do i specify percentages
You are right. Constructor that you used creates animation with absolute values (pixels). You need to use another constructor. Like this for example:
TranslateAnimation in = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 1.0f,
Animation.RELATIVE_TO_SELF, 0.0f, 0, 0.0f, 0, 0.0f);
Experiment with first and third parameter. Try use Animation.RELATIVE_TO_PARENT to fit your needs.
Try this (use percent 'p'):
<translate android:fromXDelta="100%p" android:toXDelta="0%p" android:duration="500" />

Categories

Resources