In my sample project i have a linear layout, its height is set to wrap content in xml and there is also a fragment below it which takes all the remaining space. The fragment contains a button which when clicked will remove the fragment and the height of the linear layout is set to match parent. I tried adding android:animateLayoutChanges="true" but the transition from wrap_content to match_parent is not smooth. How can i animate from android:layout_height="wrap_content" to android:layout_height="match_parent"
Here is the layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg"
android:orientation="vertical"
android:id="#+id/layoutRoot"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:id="#+id/cloudHolder"
android:orientation="vertical"
android:animateLayoutChanges="true"
android:gravity="center"
android:layout_height="wrap_content">
<ImageButton
android:layout_width="100dp"
android:layout_gravity="center"
android:layout_height="100dp"
android:background="#drawable/play"/>
</LinearLayout>
#tdjprog Answer with some edit
1 - Stritch
private void animateViewTostritch_height(final View target) {
int fromValue = 0;
// int fromValue = target.getHeight();
// int toValue = ((View) target.getParent()).getHeight();// matchparent
int toValue = (int) getResources().getDimension(R.dimen.dialog_header_height);//spesific hight
// int toValue = (int) (getResources().getDimension(R.dimen.dialog_header_height) / getResources().getDisplayMetrics().density);
ValueAnimator animator = ValueAnimator.ofInt(fromValue, toValue);
animator.setDuration(2000);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
target.getLayoutParams().height = (int) animation.getAnimatedValue();
target.requestLayout();
}
});
animator.start();
}
Call animateViewTostritch_height(your_View);
2 - Scale %
public void scaleView(View v, float startScale, float endScale) {
Animation anim = new ScaleAnimation(
1f, 1f, // Start and end values for the X axis scaling
startScale, endScale, // Start and end values for the Y axis scaling
Animation.RELATIVE_TO_SELF, 0f, // Pivot point of X scaling
Animation.RELATIVE_TO_SELF, 1f); // Pivot point of Y scaling
anim.setFillAfter(true); // Needed to keep the result of the animation
anim.setDuration(1000);
v.startAnimation(anim);
}
Call scaleView(your_View,0f,10f); // 10f match parent
You may need to try adding android:animateLayoutChanges="true" in the parent layout itself like for example:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg"
android:orientation="vertical"
android:animateLayoutChanges="true"
android:id="#+id/layoutRoot"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:id="#+id/cloudHolder"
android:orientation="vertical"
android:gravity="center"
android:layout_height="wrap_content">
<ImageButton
android:layout_width="100dp"
android:layout_gravity="center"
android:layout_height="100dp"
android:background="#drawable/play"/>
</LinearLayout>
</LinearLayout>
If the above code doesn't work, you might need to take a look at:
Animation of height of LinearLayout container with ValueAnimator
Or
Animation in changing LayoutParams in LinearLayout
I think relying on animateLayoutChanges isn't a good idea. Try below code instead.
import android.view.ViewPropertyAnimator;
// In your activity
private int parentHeight;
private int childHeight;
private float childScaleFactor;
//In onCreate()
mChildView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
parentHeight = mParentView.getMeasuredHeight();
childHeight = mChildView.getMeasuredHeight();
childScaleFactor = parentHeight/childHeight;
}
});
mChildView.animate()
.scaleY(childScaleFactor)
.setDuration(500)
.start();
If this doesn't work, refer to this answer on another post
try this:
private void animateViewToMatchParent(final View target) {
int fromValue = target.getHeight();
int toValue = ((View) target.getParent()).getHeight();
ValueAnimator animator = ValueAnimator.ofInt(fromValue, toValue);
animator.setDuration(250);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
target.getLayoutParams().height = (int) animation.getAnimatedValue();
target.requestLayout();
}
});
animator.start();
}
Related
I have a view that looks like this:
https://i.stack.imgur.com/zKe01.png
When clicked, I want to animate the grey background color so that it slides from the right side to 50% of its initial width:
https://i.stack.imgur.com/s96BX.png
Here is the relevant portion of the layout xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/content_view"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginBottom="4dp">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="1.0"/>
<View
android:id="#+id/background"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="#+id/guideline"
android:background="#drawable/background" />
</androidx.constraintlayout.widget.ConstraintLayout>
I've tried the following:
contentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(contentView);
constraintSet.setGuidelinePercent(guideline.getId(), 0.5f);
TransitionManager.beginDelayedTransition(contentView);
constraintSet.applyTo(contentView);
}
});
But instead of the background color sliding from the right to the left (100% to 50%), it sort of just cross-fades.
What am I doing wrong? How would I change my code so the animation SLIDES the background color when the guideline percent changes?
You can use android.animation.ValueAnimator to change the percentage value of your guideline inside your constraintLayout, the code would be like this:
contentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// initialize your guideline
// Guideline guideline = findViewById ....
ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f, 0.5f);
// set duration
valueAnimator.setDuration(1000);
// set interpolator and updateListener to get the animated value
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
// update guideline percent value through LayoutParams
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams) guideline.getLayoutParams();
// get the float value
lp.guidePercent = (Float) animation.getAnimatedValue();
// update layout params
guideline.setLayoutParams(lp);
}
});
valueAnimator.start();
}
});
I need to implement a circular reveal animation after the transition between 2 fragments is finished so that the ImageButton (android:id="#+id/update_profile_pic") will be revealed with a nice animation
The problem is that it doesn't work as it should:
setEnterSharedElementCallback(new SharedElementCallback() {
Handler handler = new Handler();
#Override
public void onSharedElementEnd(List<String> sharedElementNames,
List<View> sharedElements,
List<View> sharedElementSnapshots) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
update_profile_pic.setVisibility(View.GONE);
// get the center for the clipping circle
int cx = update_profile_pic.getWidth() / 2;
int cy = update_profile_pic.getHeight() / 2;
// get the final radius for the clipping circle
float finalRadius = (float) Math.hypot(cx, cy);
// create the animator for this view (the start radius is zero)
Animator anim = ViewAnimationUtils.createCircularReveal(update_profile_pic, cx, cy, 0f, finalRadius);
// make the view visible and start the animation
update_profile_pic.setVisibility(View.VISIBLE);
anim.start();
}
}, 600);
}
});
When I try this animation in a button click listener, the button needs to be clicked twice to make the animation work
The layout is like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_simple_two"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context=".FragmentB"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.mikhaellopez.circularimageview.CircularImageView
android:id="#+id/profile_picture"
android:clickable="false"
android:src="#drawable/roni"
app:civ_border_color="#color/fadedText"
app:civ_border_width="0.1dp"
android:layout_width="260dp"
android:layout_height="260dp"
android:layout_margin="18dp"
android:transitionName="#string/simple_fragment_transition"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:scaleType="centerInside"
android:elevation="3dp"
android:layout_margin="26dp"
android:id="#+id/update_profile_pic"
android:src="#drawable/ic_menu_camera"
android:background="#drawable/round_button"
android:backgroundTint="#color/colorAccent"
app:layout_anchor="#id/profile_picture"
app:layout_anchorGravity="bottom|right|end"
android:visibility="gone"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Any help?
try to set visibility of your ImageButton to invisible instead of gone initially.
I created simple app with some animations in android. In my layout I have an ImageView with a sourceImage and a button on it. When I click the button, I want it move and resize in same time.
I used ObjectAnimator and ValueAnimator for create animations and play them together with animatorSet. My problem is that my button not move smoothly.
I checked several animation library like Transitions Everywhere and APIDemos. In these libraries when I set ImageBackground the view can't move smoothly
and all of them have my problem.
Can anyone help me to solve this problem?
my layout code:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageview"
android:fitsSystemWindows="true"
android:src="#drawable/image"
android:scaleType="fitXY" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/frame">
<Button
android:layout_width="100dp"
android:layout_height="45dp"
android:id="#+id/btn_start"
android:layout_gravity="center_horizontal|bottom"
android:gravity="center_vertical|center_horizontal"
android:textAllCaps="false"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="Start" />
// ....................
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
my animations code:
btn_start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ButtonAnimation();
}
});
private void ButtonAnimation() {
Animator moveButtonAnimY = ObjectAnimator.ofFloat(btn_start, "translationY", 0, 200)
.setDuration(500);
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(width_btn_start, width_btn_start * 2.0f);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
btn_start.getLayoutParams().width = value.intValue();
btn_start.requestLayout();
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(moveButtonAnimY, valueAnimator);
animatorSet.start();
}
According to Wikipedia:
Animation is the process of making the illusion of motion and change by means of the rapid display of a sequence of static images that minimally differ from each other.
Therefore, animation is a pretty heavy task for the device to process, and the smoothness as you referred, depends upon the device configuration.
So, First check if the device have enough power to carry out that animation smoothly.
Secondly, Your code initializes animation every time the button is clicked, Instead you should initialize animation once, and use the instances of them every time you need to start the animation.
A better version of your code would be :
btn_start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ButtonAnimation();
}
});
Animator moveButtonAnimY = ObjectAnimator.ofFloat(btn_start,"translationY", 0, 200).setDuration(500);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(width_btn_start, width_btn_start * 2.0f);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
btn_start.getLayoutParams().width = value.intValue();
btn_start.requestLayout();
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(moveButtonAnimY, valueAnimator);
private void ButtonAnimation() {
animatorSet.start();
}
I have an Imageview inside a FrameLayout (both have Layout_margins),also i got my custom FrameLayout(same with Layout_margins).
So what i have now:
Source code(.axml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffff363c"
android:layout_weight="100"
android:clickable="true">
<FrameLayout
android:id="#+id/MainFrameLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_weight="80">
<FrameLayout
android:id="#+id/SourceFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="10dp">
<ImageView
android:src="#android:drawable/ic_menu_camera"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/Img"
android:layout_margin="10dp" />
</FrameLayout>
<CustomFrameLayout
android:id="#+id/CustomView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
android:layout_margin="10dp" />
</FrameLayout>
</LinearLayout>
And what i want exactly(programmatically rotation left,right via button):
So my main goal is to rotate properly image(and CustomFrameLayout too),also i need to store my source Width/Height of image.How can i achieve this?
I tried to set scaleType.center / scaletype.centerInisde , but that is wrong idea.
Any suggestions? Thanks!
Don't know if i exactly understand your problem.
If u want programmatically rotate something u can try this:
ImageView image.animate().rotation(90).start();
or this
FrameLayout frame.animate().rotation(90).start();
or both.
To store Width/Height u can use
int image_h = image.getHeight();
int image_w = image.getWidth();
Better late than never.
I was facing the exactly same problem.
After some trying, I came with this solution:
private void rotateImageBy90(){
imageView.animate()
.rotation(90)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(250)
.setListener(rotateListener)
.start();
Log.d("chatImage", "Rotating image by "+90+" degrees clockwise");
}
Declare an AnimatorListener, the one that solves the problem:
Animator.AnimatorListener rotateListener = new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationEnd(Animator animator) {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) imageView.getLayoutParams();
params.height = FrameLayout.LayoutParams.MATCH_PARENT;
params.width = FrameLayout.LayoutParams.MATCH_PARENT;
imageView.setLayoutParams(params);
imageView.requestLayout();
//This is the trick: I get the current bitmap, rotate it
Bitmap newBitmap = rotateImage(((BitmapDrawable)imageView.getDrawable()).getBitmap(),90);
//Then, rotate the imageview back to it's original position, without animation
imageView.setRotation(0);
//As I set the new, rotate bitmap, to it.
imageView.setImageBitmap(newBitmap);
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
};
The rotateImage method:
public static Bitmap rotateImage(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix,
true);
}
I created simple animation in CustomView to transition beetwen activities. Everything is fine except animation smooth. There are visible lags when circle radius is increased. How can I performance animation to avoid lags?
public class CircleAnimationView extends View {
...
public void startAnimation(int x, int y) {
startX = x;
startY = y;
baseAnimator = ObjectAnimator.ofFloat(this, "baseRadius", 0, maxRadius)
.setDuration(1000);
baseAnimator
.setInterpolator(new AccelerateDecelerateInterpolator());
baseAnimator.start();
finalAnimator = ObjectAnimator.ofFloat(this, "finalRadius", 0, maxRadius)
.setDuration(2000);
finalAnimator
.setInterpolator(new AccelerateDecelerateInterpolator());
if(listener!=null)
finalAnimator.addListener(listener);
finalAnimator.start();
}
public void setBaseRadius(final float radius) {
radiusBase = radius;
invalidate();
}
public void setFinalRadius(final float radius) {
radiusFinal = radius;
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isInEditMode()) {
return;
}
canvas.drawCircle(startX, startY, radiusBase, basePaint);
canvas.drawCircle(startX, startY, radiusFinal, finalPaint);
}
};
I used view in xml:
<RelativeLayout 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:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
</RelativeLayout>
<pl.org.ldi.challenge.views.CircleAnimationView
android:id="#+id/circle_animation"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:base_color="#color/blue"
app:final_color="#color/white"/>
</RelativeLayout>
Edit:
I check .trace file from animation, and it looks like that drawing large circle takes too much time - method GLES20Canvas.nDrawDisplayList
After set:
finalAnimator.setFrameDelay(0);
baseAnimator.setFrameDelay(0);
animation looks smoothly. Thanks for advice!
You can use a handler to call invalidate by help of its postDelayed() method. Call this methods at regular intervals throughout your animations duration. You can calculate the radius according by the ratio of current_time/total_time and multiplied with the maximim radius you want.