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();
}
});
Related
I have a view that is invisible by default(Just for the first time).
Now I need to switch the visibility to VISIBLE with this animation:
if (myView.getVisibility() == View.INVISIBLE) {
myView.setVisibility(View.VISIBLE);
myView.animate().translationY(0);
}
(Like the SnackBar default animation)
But this isn't working. It will turn visible with default animation
Is there any simple way that I could achieve this?
Note
I'm animating my view to dismiss, like this:
myView.animate().translationY(myView.getHeight());
You can do this using XML animation.
Create a slide-up animation XML using set and alpha and put this XML into your resource anim folder.
slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="500"
android:fromYDelta="100%"
android:toYDelta="0" />
</set>
USE:
Use AnimationUtils.loadAnimation() to load animation from XML and set and start animation using .startAnimation() method.
Here is an example:
ImageView imageView = (ImageView) findViewById(R.id.imageView);
// slide-up animation
Animation slideUp = AnimationUtils.loadAnimation(this, R.anim.slide_up);
if (imageView.getVisibility() == View.INVISIBLE) {
imageView.setVisibility(View.VISIBLE);
imageView.startAnimation(slideUp);
}
Hope this will help~
Add animations using ConstraintLayout
Just add below code above the views whose visibility is updated:
TransitionManager.beginDelayedTransition(constraintLayout)
Note:
ConstraintLayout will only perform animation on its direct children since it only knows when you change layout parameters and constraints on the children that it handles.
ConstraintLayout only animates layout related changes.
For more see this post https://robinhood.engineering/beautiful-animations-using-android-constraintlayout-eee5b72ecae3
This is the best way to animate views visibility :
private void viewGoneAnimator(final View view) {
view.animate()
.alpha(0f)
.setDuration(500)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
}
});
}
private void viewVisibleAnimator(final View view) {
view.animate()
.alpha(1f)
.setDuration(500)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.VISIBLE);
}
});
}
And then call this method wherever you wanted to make a view visible or gone and give the intended view to methods as the parameter.
Just You need to add android:animateLayoutChanges="true" to your layout.
When I set visibility gone to linear_container, linear_bottom will animate from bottom to up and take place of "linear_container".
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/layoutTop"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:id="#+id/linear_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
</LinearLayout>
<LinearLayout
android:id="#+id/linear_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
</LinearLayout>
</LinearLayout>
Based on this answer:
with this methods, I can set the visibility of my view to VISIBLE with a slideUp animation(Like snackbar animation):
int getScreenHeight() {
DisplayMetrics displaymetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
return displaymetrics.heightPixels;
}
public void animateOnScreen(View view) {
final int screenHeight = getScreenHeight();
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "y", screenHeight, (screenHeight * 0.8F));
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
}
Then I can use it like this:
if (myView.getVisibility() == View.INVISIBLE) {
myView.setVisibility(View.VISIBLE);
animateOnScreen(myView);
}
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();
}
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();
}
I have a simple button in my layout. Setting leftMargin to the view actually showing different results.
my_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/left_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="hello pandora"/>
</RelativeLayout>
In my activity, I'm setting the leftMargin property to the Button.
Button leftBtn = (Button) findViewById(R.id.left_btn);
LayoutParams params = (LayoutParams) leftBtn.getLayoutParams();
params.leftMargin = 550;
If I set leftMargin as negative value or 0, its working fine, but If I set the value greater than the width of screen, it just resizing/compressing the button. I am expecting the button to go out of bounds like negative value.
I am expecting the button in the 3rd image to go out of bounds like the button in 1st image.
Please don't say to set the button layout_alignParentRight="true" in layout and rightMargin = -50in activity(this works) because I want to move the button from left to right.
I assume assigning a specific width larger than the screen size (eg. 1000 dp) to the parent RelativeLayout should solve your problem.
Also why do you want to make out-of-screen UI elements? What is the desired behaviour? Perhaps a transition animation would be better?
EDIT
I've tried the animation + storing the measured width of the Button. It seems to work.
Can you try this on GB?
MainActivity.java
public class MainActivity extends Activity {
final Context context = this;
Button mButton;
int mButtonWidth; // Measured width of Button
int amountToMove; // Amount to move the button in the x direction
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
amountToMove = 600;
mButton = (Button) findViewById(R.id.button);
// Measure Button's width
mButton.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
mButtonWidth = mButton.getMeasuredWidth();
// Simple onClick listener showing a Toast
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context,"Hello Pandora clicked!",Toast.LENGTH_SHORT).show();
}
});
// Onclick listener for the other button
Button toggle = (Button) findViewById(R.id.toggle);
toggle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Animate the other button
TranslateAnimation a = new TranslateAnimation(0, amountToMove, 0, 0);
a.setDuration(1000);
// Finalize movement when animation ends
a.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)mButton.getLayoutParams();
// Restore measured width and change left margin
lp.width = mButtonWidth;
lp.leftMargin = lp.leftMargin + amountToMove;
mButton.setLayoutParams(lp);
amountToMove = -amountToMove;
}
#Override
public void onAnimationStart(Animation animation) { /* Do nothing */ }
#Override
public void onAnimationRepeat(Animation animation) { /* Do nothing */ }
});
mButton.startAnimation(a);
}
});
}
}
activity_main.xml
<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:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Pandora"
android:id="#+id/button" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Move the other button"
android:id="#+id/toggle"/>
</LinearLayout>
EDIT 2
It works on a GB Emulator too (the Button gets clipped, is clickable).
u can use max line=1 to show complete text in one line on button when you use leftMargin = 550;
try this
<Button
android:id="#+id/left_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:maxLines="1"
android:text="hello pandora"/>
Hello Edit your button property like this,
android:layout_gravity="center_horizontal"
android:singleLine="true"
and change parent layout to frameLayout
i have 2 views in a RelativeLayout like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:id="#+id/view1"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#drawable/shape_red"/>
<View
android:id="#+id/view2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="#id/view1"
android:background="#drawable/shape_blue"/>
</RelativeLayout>
now, i animate the y property of view1 from 0 to 300. but i want view2 to change its position according to view1 - because view2 is set to be below (layout-below) view1.
as this isn't working, i tried to add an onAnimationEnd listener to the animation. like this:
ObjectAnimator ani = ObjectAnimator.ofFloat(view1, "Y", 200);
ani.setDuration(2000);
ani.addListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation)
{
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, view2.getHeight());
params.addRule(RelativeLayout.BELOW, view1.getId());
view2.setLayoutParams(params);
}
});
but view2 didn't change its position at all.
i would like to avoid making a new animation for view2 if possible, so please don't bother to suggest one. ;)
does anyone have some suggestions?
Check this if it solves your problem:
ani.addListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation)
{
LayoutParams params = view2.getLayoutParams();
// change it ...
params.addRule(RelativeLayout.BELOW, view1.getId());
view2.setLayoutParams(params);
}
});