Working on this app in which I'am trying to manage show/hide animation on a LinearLayout.
The problem I have is, on toggling the visibility it seems to hide any children the second time..
To make my story more clear here a screenshot:
on start - hidden
1st click - shows linear layout with children (textview)
2th click - hids linear layout again
3th click - shows linear layout but no children (textview)
Layoutfile:
<TextView
android:id="#+id/tv_lecture"
style="#style/Text.Primary.Light"
android:singleLine="true" />
<LinearLayout
android:visibility="gone"
android:id="#+id/grades_container"
style="#style/Container.Vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="10"/>
</LinearLayout>
code:
private void animate(final LinearLayout gradesContainer) {
if (gradesContainer.getVisibility() == View.GONE) {
gradesContainer.animate()
.translationY(gradesContainer.getHeight())
.alpha(1.0f)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
gradesContainer.setVisibility(View.VISIBLE);
gradesContainer.setAlpha(0.0f);
}
});
}
else {
gradesContainer.animate()
.translationY(0)
.alpha(0.0f)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
gradesContainer.setVisibility(View.GONE);
}
});
}
}
On lecture TextView click will trigger the method: animate()
So whats the problem here? Can't figure it out thats why I'am asking you guys. Thanks!
If you are aiming for just the simple animation. You can just go on ahead and use animateLayoutChanges in your xml.
Cheers! :)
Related
I am trying to expand and collapse my view with the help of TransitionManager animation. Following is the output,
See the overlapping layout while collapsing top view. How to avoid that ? I set "detailedView" (one with icons) visibility GONE and use following code for animation,
topView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TransitionManager.beginDelayedTransition(topView);
TransitionManager.beginDelayedTransition(bottomView);
if (detailsView.getVisibility() == View.VISIBLE) {
detailsView.setVisibility(View.GONE);
cardText.setText("Collapse Title");
} else {
detailsView.setVisibility(View.VISIBLE);
cardText.setText("Expanded Title");
}
}
});
I would build the animation differently. I would make a LinearLayout with the top cell with wrap_content, and when clicking I would do something like:
valueAnimator = ValueAnimator.ofInt(titleContainer.getHeight(),titleContainer.getHeight() + newHeight );
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
titleContainer.setHeight(animation.getAnimatedValue());
}
});
valueAnimator.setDuration(270);
valueAnimator.start();
I had the same problem.
The first argument that the TransitionManager.beginDelayedTransition() function takes must be the parent of all the views that will interact in the transition e.g:
I have the next layout:
<!-- The scene root -->
<LinearLayout
android:id="#+id/transition_container" >
<!-- First card -->
<androidx.cardview.widget.CardView
android:id="#+id/expandableCard1">
<LinearLayout
android:id="#+id/staticHeader1">
</LinearLayout>
<LinearLayout
android:id="#+id/expandableContent1">
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Second card -->
<androidx.cardview.widget.CardView
android:id="#+id/expandableCard2">
<LinearLayout
android:id="#+id/staticHeader2">
</LinearLayout>
<LinearLayout
android:id="#+id/expandableContent2">
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
And my code (kotlin):
// toggle
if( expandableContent1.visibility == View.GONE ){
// apply to the root scene
TransitionManager.beginDelayedTransition(transition_container )
// change the visibility of the expandable content
expandableContent1.visibility = View.VISIBLE
arrowBtn.setImageResource( R.drawable.ic_arrow_up_24)
} else {
// apply to the root scene
TransitionManager.beginDelayedTransition(transition_container )
// change the visibility of the expandable content
expandableContent1.visibility = View.GONE
arrowBtn.setImageResource( R.drawable.ic_arrow_down_24)
}
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 have a button alternates its visibility when a second button is clicked. For example, on the first click it slides from the right (outside the screen) to the position defined on layout file and on the second click it slides to the right. These animations also use fade in/out.
I'm currently using this code (click listeners ommited for the sake of simplicity):
<ImageButton
android:id="#+id/unlock_button"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="right"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:background="#drawable/ic_lock_red"
android:visibility="gone" />
Java
//Hiding
mUnlockButton.animate()
.alpha(0)
.setDuration(600)
.translationX(50)
.setInterpolator(new AccelerateInterpolator())
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mUnlockButton.setVisibility(View.GONE);
}
})
.start();
//Show
mUnlockButton.setAlpha(0f);
mUnlockButton.setTranslationX(50);
mUnlockButton.setVisibility(View.VISIBLE);
mUnlockButton.animate()
.alpha(1.0f)
.setDuration(600)
.translationX(-50)
.setInterpolator(new AccelerateInterpolator())
.start();
After the third click (show, hide, then show again) the animation is run but the button disappears when the animation ends.
Why?
Simple task: by pressing button, it scales X value to 0, and when animation finished starts another animation on the second view which scales X from 0 to 1. After 1 second reverse animation should be played and that's all. Running code below I've got infinite animation loop of the first part of animation.
Used nineoldandroids lib, but I don't think this is something really different from native animations framework at least on jelly bean devices.
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View mybutton = findViewById(R.id.mybutton);
final View myprogress = findViewById(R.id.myprogress);
mybutton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
animate(mybutton).scaleX(0).setListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation)
{
mybutton.setVisibility(View.INVISIBLE);
myprogress.setVisibility(View.VISIBLE);
ViewHelper.setScaleX(myprogress, 0f);
animate(myprogress).scaleX(1).setListener(new AnimatorListenerAdapter()
{
#SuppressWarnings("ConstantConditions")
#Override
public void onAnimationEnd(Animator animation)
{
mybutton.getHandler().postDelayed(new Runnable()
{
#Override
public void run()
{
animate(myprogress).scaleX(0).setListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation)
{
myprogress.setVisibility(View.INVISIBLE);
mybutton.setVisibility(View.VISIBLE);
ViewHelper.setScaleX(mybutton, 0);
animate(mybutton).scaleX(1);
}
});
}
}, 1000);
}
});
}
});
}
});
}
}
Layout is simple:
<RelativeLayout 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:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/mycontainer">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="string text"
android:id="#+id/mybutton"
/>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#id/mybutton"
android:layout_alignTop="#id/mybutton"
android:layout_alignRight="#id/mybutton"
android:layout_alignBottom="#id/mybutton"
android:visibility="invisible"
android:id="#+id/myprogress"
/>
</RelativeLayout>
</RelativeLayout>
Where I was wrong?
Madness!
After investigation found source of it:
When animate() method called, it creates ViewPropertyAnimator and saves it to the map.
When you try to animate this view again with animate() it takes already created viewpropertyanimator from the map and immediately calls onAnimationStart() even before you set new animation parameters, and because animationlistener was set on the first animation, it was triggered! and launched first animation(second part of it). This is creates infinite loop.
To stop it you MUST clear old listener when you try to animate view second time, so
animate(mybutton).setListener(null).scaleX(1);
stops infinite loop.
Documentation should warn about it, definitely!
I am new in Android animation and my requirement is to translate a view from one layout to layout in a single xml file on click of that view.
Scenario:
Suppose I click a button, present on the top of the header in a xml file,and it should move/translate downwards (it should give an impact that it lies on the other layout downwards to header), and also I want that when the user clicks on the same again, it should now move to its original position.
Here I am explaining with my xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/app_bg"
android:orientation="vertical" >
<RelativeLayout
android:id="#+id/top"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/header"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<Button
android:id="#+id/btnSearchHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerInParent="true"
android:background="#drawable/search_icon" />
</RelativeLayout>
<RelativeLayout
android:id="#+id/bottom"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/app_transparent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_marginTop="10dp"
android:visibility="visible" >
<Button
android:id="#+id/btnMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:text="ABC" />
<Button
android:id="#+id/btnSearchSelected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/btnMenu"
android:text="CDE" />
</RelativeLayout>
</LinearLayout>
MORE PRECISE REQUIREMENT SPECIFICATION (Kindly read carefully:)
Here I have two sub inner layouts:-
Top Layout - id-> top
Bottom Layout- id -> bottom
Now a view (Button -> btnSearchHeader) is lying in my top layout and I want to animate the same to the bottom layout (it should give an impact that it is translated with a translate animation to the bottom layout) on click of that button and when the user clicks on that button, it should again translate back to its original position with a translate animation .. i.e it should show back in the top layout
I have no idea how to give these impacts using translate animations, however i just have a basic translate animation knowledge which is insufficient for me to work upon my requirement.
Any type of related help is appreciable.
Thanks
Have you tried something simple like the following?
final int topHeight = findViewById(R.id.top).getHeight();
final int bottomHeight = findViewById(R.id.bottom).getHeight();
final View button = findViewById(R.id.btnSearchHeader);
final ObjectAnimator moveDownAnim = ObjectAnimator.ofFloat(button, "translationY", 0.F, topHeight + bottomHeight / 2 - button.getHeight() / 2);
final ObjectAnimator moveUpAnim = ObjectAnimator.ofFloat(button, "translationY", topHeight + bottomHeight / 2 - button.getHeight() / 2, 0.F);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (0.F == v.getTranslationY())
moveDownAnim.start();
else
moveUpAnim.start();
}
});
If you actually need the button view to change parents, you can use AnimatorListener to achieve this at the end of each animation. Something like:
moveDownAnim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
((ViewGroup)findViewById(R.id.top)).removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}
#Override
public void onAnimationCancel(Animator animation) { /* NOP */ }
#Override
public void onAnimationRepeat(Animator animation) { /* NOP */ }
#Override
public void onAnimationStart(Animator animation) { /* NOP */ }
});
(And analogous listener for the moveUpAnim.)
However, I doubt you need to actually do this to achieve the visual effect you want. But if you do this part, you will probably also need to set a fixed height for your top view as opposed to wrap_content. (Otherwise, if a layout pass happens while the button has been moved to the bottom view, the top layout's height might go to 0 if there's nothing else in it.) Easiest would be to just do this directly in the xml layout file. However, if you want to "do it on the fly", you can change the layout's height in the onAnimationEnd() method using something like:
#Override
public void onAnimationEnd(Animator animation) {
final ViewGroup topLayout = findViewById(R.id.top);
topLayout.getLayoutParams().height = topLayout.getHeight(); // Keep it the same height...
topLayout.removeView(button);
((ViewGroup)findViewById(R.id.bottom)).addView(button);
((RelativeLayout)button.getLayoutParams()).addRule(RelativeLayout.CENTER_IN_PARENT);
button.setTranslationY(0.F); // Since it is now positioned in the new layout, no need for translation.
}