ValueAnimator
Applying ValueAnimator we can animate any number of objects of any type at the
same time using one instance of it. Also you can use not only the animated value
but the fraction to be able to interpolate between other two values without
creating a new instance of ValueAnimator. So great tool.
Usage:
Firstly we create an instance of ValueAnimator as
The following example shows how ValueAnimator is useful in animating several views each in several properties.
Example 1: Please create a new Android Studio project and use Empty template. Then edit the activity_main.xml (layout) file as shown below. Note that the background resource of buttons in this example is an SVG file which has been converted to ic_sun.xml file.
The MainActivity.java file is as follows.
And it is the result!
Example 2: Here we use a custom view (something like the custom view created in custom view) and make it to play a simple animation. Please create a new Android Studio project and use Empty template and refer to custom view for details of the custom view. We should create a new java file PieView.java shown below.
Then edit the activity_main.xml as below.
In the mainActivity.java one instance of ValueAnimator can simply do the animation properly.
Now the result is something like below.
Usage:
Firstly we create an instance of ValueAnimator as
ValueAnimator animator =
ValueAnimator.ofFloat(start value, end value);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float) animator.getAnimatedValue();
//update your views here
//do not forget to invalidate your views
}
});
animator.setDuration(duration);
animator.start();
The following example shows how ValueAnimator is useful in animating several views each in several properties.
Example 1: Please create a new Android Studio project and use Empty template. Then edit the activity_main.xml (layout) file as shown below. Note that the background resource of buttons in this example is an SVG file which has been converted to ic_sun.xml file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<android.support.constraint.ConstraintLayout | |
android:id="@+id/lay1" | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="com.msh_n.myp.valueanimator1.MainActivity"> | |
<Button | |
android:id="@+id/button" | |
android:layout_width="70dp" | |
android:layout_height="70dp" | |
android:background="@drawable/ic_sun" | |
android:text="" | |
/> | |
</android.support.constraint.ConstraintLayout> |
The MainActivity.java file is as follows.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MainActivity extends AppCompatActivity implements View.OnClickListener { | |
Button btn; | |
Button[] buttons; | |
ConstraintLayout layout; | |
int whichAnimation; | |
int flowerX = 0; | |
int flowerY = 0; | |
double[] flowerXD = new double[5]; | |
double[] flowerYD = new double[5]; | |
double r = 200; | |
int duration = 200; | |
//int[] startDelay = {100, 80, 60, 80, 100}; | |
//int[] exitDelay = {100, 80, 60, 80, 100}; | |
int[] startDelay = {0,0,0,0,0}; | |
int[] exitDelay = {0,0,0,0,0}; | |
int w; | |
int h; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
btn = (Button) findViewById(R.id.button); | |
layout = (ConstraintLayout) findViewById(R.id.lay1); | |
// w = 1000; | |
// h = 1400; | |
ViewTreeObserver vto = layout.getViewTreeObserver(); | |
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { | |
@Override | |
public void onGlobalLayout() { | |
w = layout.getMeasuredWidth(); | |
h = layout.getMeasuredHeight(); | |
for(int i = 0; i < 5; i++){ | |
flowerXD[i] = w/2 + r*(Math.cos(Math.toRadians(72*(i)))); | |
flowerYD[i] = h/2 + r*(Math.sin(Math.toRadians(72*(i)))); | |
} | |
} | |
}); | |
btn.setOnClickListener(this); | |
for(int i = 0; i < 5; i++){ | |
flowerXD[i] = w/2 + r*(Math.cos(Math.toRadians(72*(i)))); | |
flowerYD[i] = h/2 + r*(Math.sin(Math.toRadians(72*(i)))); | |
} | |
initializeBtns(); | |
} | |
@Override | |
public void onClick(View v){ | |
switch(v.getId()){ | |
case R.id.button: | |
if(whichAnimation == 0){ | |
for(Button btn: buttons){ | |
btn.setVisibility(View.VISIBLE); | |
} | |
for(int i = 0; i < 5; i++){ | |
playEnter(buttons[i], i); | |
} | |
whichAnimation = 1; | |
}else{ | |
for(int i = 0; i < 5; i++){ | |
playExit(buttons[i], i); | |
} | |
whichAnimation = 0; | |
} | |
} | |
} | |
private void playEnter(final Button b, final int position){ | |
/** | |
* Animator that animates buttons x and y position simultaneously with size | |
*/ | |
AnimatorSet buttonAnimator = new AnimatorSet(); | |
/** | |
* ValueAnimator to update x position of a button | |
*/ | |
int destinationX = flowerX +(int) flowerXD[position]; | |
ValueAnimator btnAnimatorX = ValueAnimator.ofFloat(flowerX, destinationX); | |
btnAnimatorX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { | |
@Override | |
public void onAnimationUpdate(ValueAnimator valueAnimator) { | |
b.setX((float) valueAnimator.getAnimatedValue()); | |
b.requestLayout(); | |
} | |
}); | |
int destinationY = flowerY + (int) flowerYD[position]; | |
ValueAnimator btnAnimatorY = ValueAnimator.ofFloat(flowerY, destinationY); | |
btnAnimatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { | |
@Override | |
public void onAnimationUpdate(ValueAnimator valueAnimator) { | |
b.setY((float) valueAnimator.getAnimatedValue()); | |
b.requestLayout(); | |
} | |
}); | |
ValueAnimator btnAnimatorSize = ValueAnimator.ofFloat(5f, 100f); | |
btnAnimatorSize.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { | |
@Override | |
public void onAnimationUpdate(ValueAnimator valueAnimator) { | |
float width =(float) valueAnimator.getAnimatedValue(); | |
//b.setLayoutParams(new ConstraintLayout.LayoutParams((int) width, (int) width)); | |
b.getLayoutParams().width = (int) width; | |
b.getLayoutParams().height = (int) width; | |
b.requestLayout(); | |
} | |
}); | |
btnAnimatorX.setDuration(duration); | |
btnAnimatorY.setDuration(duration); | |
btnAnimatorSize.setDuration(duration); | |
/** | |
* Add both x and y position update animation in | |
* animator set | |
*/ | |
buttonAnimator.play(btnAnimatorX).with(btnAnimatorY).with(btnAnimatorSize); | |
buttonAnimator.setStartDelay(startDelay[position]); | |
buttonAnimator.start(); | |
} | |
private void playExit(final Button b, int position){ | |
/** | |
* Animator that animates buttons x and y position simultaneously with size | |
*/ | |
AnimatorSet buttonAnimator = new AnimatorSet(); | |
int destinationX = flowerX +(int) flowerXD[position]; | |
ValueAnimator btnAnimatorX = ValueAnimator.ofFloat(destinationX, flowerX); | |
btnAnimatorX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { | |
@Override | |
public void onAnimationUpdate(ValueAnimator valueAnimator) { | |
b.setX((float) valueAnimator.getAnimatedValue()); | |
b.requestLayout(); | |
} | |
}); | |
int destinationY = flowerY + (int) flowerYD[position]; | |
ValueAnimator btnAnimatorY = ValueAnimator.ofFloat(destinationY, flowerY); | |
btnAnimatorY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { | |
@Override | |
public void onAnimationUpdate(ValueAnimator valueAnimator) { | |
b.setY((float) valueAnimator.getAnimatedValue()); | |
b.requestLayout(); | |
} | |
}); | |
ValueAnimator btnAnimatorSize = ValueAnimator.ofFloat(100f, 5f); | |
btnAnimatorSize.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { | |
@Override | |
public void onAnimationUpdate(ValueAnimator valueAnimator) { | |
float width =(float) valueAnimator.getAnimatedValue(); | |
//b.setLayoutParams(new ConstraintLayout.LayoutParams((int) width, (int) width)); | |
b.getLayoutParams().width = (int) width; | |
b.getLayoutParams().height = (int) width; | |
b.requestLayout(); | |
} | |
}); | |
btnAnimatorX.setDuration(duration); | |
btnAnimatorY.setDuration(duration); | |
btnAnimatorSize.setDuration(duration); | |
/** | |
* Add both x and y position update animation in | |
* animator set | |
*/ | |
buttonAnimator.play(btnAnimatorX).with(btnAnimatorY).with(btnAnimatorSize); | |
buttonAnimator.setStartDelay(exitDelay[position]); | |
buttonAnimator.addListener(new Animator.AnimatorListener() { | |
@Override | |
public void onAnimationStart(Animator animator) { | |
} | |
@Override | |
public void onAnimationEnd(Animator animator) { | |
for(Button btn: buttons){ | |
btn.setVisibility(View.INVISIBLE); | |
} | |
} | |
@Override | |
public void onAnimationCancel(Animator animator) { | |
for(Button btn: buttons){ | |
btn.setVisibility(View.INVISIBLE); | |
} | |
} | |
@Override | |
public void onAnimationRepeat(Animator animator) { | |
} | |
}); | |
buttonAnimator.start(); | |
} | |
public void initializeBtns(){ | |
buttons = new Button[5]; | |
for(int i = 0; i < buttons.length; i++){ | |
buttons[i] = new Button(MainActivity.this); | |
//buttons[i].setX(flowerX); | |
//buttons[i].setY(flowerY); | |
buttons[i].setLayoutParams(new ConstraintLayout.LayoutParams(5,5)); | |
buttons[i].setText(String.valueOf(i + 1)); | |
buttons[i].setBackgroundResource(R.drawable.ic_sun); | |
buttons[i].setTextSize(20); | |
buttons[i].setVisibility(View.INVISIBLE); | |
((ConstraintLayout) findViewById(R.id.lay1)).addView(buttons[i]); | |
} | |
} | |
} |
And it is the result!
Example 2: Here we use a custom view (something like the custom view created in custom view) and make it to play a simple animation. Please create a new Android Studio project and use Empty template and refer to custom view for details of the custom view. We should create a new java file PieView.java shown below.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PieView extends View { | |
// magnitude of rounding rectangle | |
int rx; | |
int ry; | |
String titleText; | |
int radius; | |
float angle; | |
Paint paintBackground; | |
Paint paintV; | |
Paint paintShadow; | |
Rect rect; | |
int backColor; | |
int viewColor; | |
int height =500; | |
int width=250; | |
int ori; | |
int actionBarHeight; | |
RectF rectCircle; | |
RectF rectOval; | |
public PieView(Context context){ | |
//this(context, null, 0); | |
super(context); | |
} | |
public PieView(Context context, AttributeSet attrs){ | |
//this(context, attrs, 0); | |
super(context, attrs); | |
init(context, attrs); | |
} | |
public PieView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(context, attrs); | |
} | |
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) | |
public PieView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){ | |
super(context, attrs, defStyleAttr, defStyleRes); | |
init(context, attrs); | |
} | |
public String getTitleText(){ | |
return titleText; | |
} | |
public void setTitleText(String str){ | |
titleText = str; | |
} | |
public int getRadius(){ | |
return radius; | |
} | |
public float getAngle() { | |
return angle; | |
} | |
public void setAngle(float ang) { | |
this.angle = ang; | |
} | |
public int getRx(){ | |
return rx; | |
} | |
public void setRx(int rx){ | |
this.rx = rx; | |
} | |
public int getRy(){ | |
return ry; | |
} | |
public void setRy(int ry){ | |
this.ry = ry; | |
} | |
protected void init(Context context, @Nullable AttributeSet attrs){ | |
angle = 5; | |
rx = 0; | |
ry = 0; | |
rect = new Rect(); | |
// finding height of the action bar | |
TypedValue tv = new TypedValue(); | |
context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); | |
actionBarHeight = getResources().getDimensionPixelSize(tv.resourceId); | |
paintBackground = new Paint(Paint.ANTI_ALIAS_FLAG); | |
paintV = new Paint(); | |
paintShadow = new Paint(); | |
ori = ((Activity) context).getResources().getConfiguration().orientation; | |
if(attrs == null){ | |
backColor = getResources().getColor(android.R.color.holo_purple); | |
viewColor = getResources().getColor(android.R.color.holo_green_light); | |
paintBackground.setColor(backColor); | |
paintV.setColor(viewColor); | |
paintShadow.setColor(getResources().getColor(android.R.color.holo_purple)); | |
return; | |
} | |
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, | |
R.styleable.PieView, 0, 0); | |
try { | |
titleText = a.getString(R.styleable.PieView_titleText); | |
backColor = a.getColor(R.styleable.PieView_backgroundColor, | |
getResources().getColor(android.R.color.holo_blue_light)); | |
viewColor = a.getColor(R.styleable.PieView_viewColor, | |
getResources().getColor(android.R.color.holo_orange_dark)); | |
} finally { | |
a.recycle(); | |
} | |
paintBackground.setColor(backColor); | |
paintV.setColor(viewColor); | |
paintShadow.setColor(getResources().getColor(android.R.color.holo_purple)); | |
} | |
@Override | |
protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
super.onSizeChanged(w, h, oldw, oldh); | |
width = w; | |
//height = h + actionBarHeight; | |
height = h; | |
//rectCircle = new RectF(width/2, 0, (3/4)*width, height/3); | |
rectCircle = new RectF(width/4, 0, 3*width/4, height/3); | |
rectOval = new | |
RectF(width/4, 0,width/2+width/3, height/3); | |
boolean isWidthLarger = width > height/3; | |
if(isWidthLarger){ | |
radius = height/6; | |
}else{ | |
radius = width/2; | |
} | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ | |
int widthSize = MeasureSpec.getSize(widthMeasureSpec); | |
int heightSize = MeasureSpec.getSize(heightMeasureSpec); | |
int widthMode = MeasureSpec.getMode(widthMeasureSpec); | |
int heightMode = MeasureSpec.getMode(heightMeasureSpec); | |
int desiredWidth = 1000; | |
int desiredHeight = 1500; | |
int width; | |
int height; | |
//width | |
if(widthMode == MeasureSpec.EXACTLY){ | |
//Must be this size | |
width = widthSize; | |
}else if(widthMode == MeasureSpec.AT_MOST){ | |
//Can't be bigger than... | |
width = Math.min(widthSize, desiredWidth); | |
}else{ | |
//Be whatever you want | |
width = desiredWidth; | |
} | |
//height | |
if(heightMode == MeasureSpec.EXACTLY){ | |
//Must be this size | |
height = heightSize; | |
}else if(heightMode == MeasureSpec.AT_MOST){ | |
//Can't be bigger than... | |
height = Math.min(heightSize, desiredHeight); | |
}else{ | |
//Be whatever you want | |
height = desiredHeight; | |
} | |
//MUST CALL THIS | |
setMeasuredDimension(width, height); | |
} | |
@Override | |
protected void onDraw(Canvas canvas) { | |
super.onDraw(canvas); | |
rect.left = 0; | |
rect.top = 0; | |
if(ori == Configuration.ORIENTATION_PORTRAIT) { | |
rect.bottom = height / 3; | |
rect.right = width; | |
RectF rect2 = new RectF(); | |
rect2.left = 0; | |
rect2.right = width; | |
rect2.bottom = height; | |
rect2.top = 0; | |
canvas.drawRect(rect, paintBackground); | |
canvas.drawRoundRect(rectCircle, rx, ry, paintShadow); | |
canvas.drawArc(rectCircle, 0, angle, true, paintV); | |
canvas.drawCircle(width/2, height/6, radius/1.2f, paintShadow); | |
}else if(ori == Configuration.ORIENTATION_LANDSCAPE){ | |
} | |
} | |
} |
Then edit the activity_main.xml as below.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<android.support.constraint.ConstraintLayout | |
android:animateLayoutChanges="true" | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="com.msh_n.myp.customview1.MainActivity"> | |
<com.msh_n.myp.customview1.PieView | |
android:id="@+id/pieView2" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:layout_marginBottom="8dp" | |
android:layout_marginEnd="8dp" | |
android:layout_marginStart="8dp" | |
app:layout_constraintBottom_toTopOf="@+id/button1" | |
app:layout_constraintEnd_toEndOf="parent" | |
app:layout_constraintStart_toStartOf="parent" | |
app:layout_constraintTop_toBottomOf="parent" | |
app:titleText="Foreground color" /> | |
<Button | |
android:id="@+id/button1" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginBottom="8dp" | |
android:layout_marginEnd="8dp" | |
android:layout_marginStart="8dp" | |
android:layout_marginTop="8dp" | |
android:text="Click" | |
app:layout_constraintBottom_toBottomOf="parent" | |
app:layout_constraintEnd_toEndOf="parent" | |
app:layout_constraintStart_toStartOf="parent" | |
/> | |
</android.support.constraint.ConstraintLayout> |
In the mainActivity.java one instance of ValueAnimator can simply do the animation properly.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MainActivity extends AppCompatActivity{ | |
Button button; | |
Activity activity = this; | |
EditText edtTxt; | |
float newAngle; | |
float oldAngle; | |
ObjectAnimator animatorX; | |
ValueAnimator.AnimatorUpdateListener mListener; | |
int height; | |
int width; | |
int centerX; | |
int centerY; | |
int actionBarHeight; | |
int statusBarHeight; | |
int radius; | |
PieView pieView; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
button = (Button) findViewById(R.id.button1); | |
pieView = (PieView) findViewById(R.id.pieView2); | |
final ValueAnimator animator = ValueAnimator.ofFloat(0f, 360f); | |
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { | |
@Override | |
public void onAnimationUpdate(ValueAnimator valueAnimator) { | |
float value = (float) animator.getAnimatedValue(); | |
pieView.setAngle(value); | |
pieView.setRx((int) value); | |
pieView.setRy((int) value); | |
pieView.invalidate(); | |
} | |
}); | |
animator.setDuration(5000); | |
button.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View view) { | |
animator.start(); | |
} | |
}); | |
} | |
} |
Now the result is something like below.
Comments
Post a Comment