Android - Making translations and objectAnimator on the same XML file - android

I've been trying to make a 3D Cube rotation effect while sliding from one fragment to another.
First i was using a translate effect (on XML) calling with FragmentTransaction.setCustomAnimations(...) and then, when opening/closing the fragment, i was playing with the Camera classe to make the rotation.
This was working FINE, but seems that I HAVE TOO (don't ask me why) use all of this animation using only XML file. After a long search i found out that i should use objectAnimator to make the rotation.
Followed the Google sample and i manage to make the flip animation. Now i need to translate the fragments making them sliding in and sliding out. Seems that i can't use objectAnimator and translate effect on the same XML file. Since this error appears:
java.lang.RuntimeException: Unknown animator name: translate at (...)
Any ideas on how i can make the sliding effect and use the objectAnimator on the same time?
Thank you for your time!
Code i've been using:
card_flip_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0" />
<!-- Rotate. -->
<objectAnimator
android:duration="#integer/card_flip_time_full"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:propertyName="rotationY"
android:valueFrom="180"
android:valueTo="0" />
<!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
<objectAnimator
android:duration="1"
android:propertyName="alpha"
android:startOffset="#integer/card_flip_time_half"
android:valueFrom="0.0"
android:valueTo="1.0" />
</set>
Fragment calling another fragment: (cube rotation should be visible between this 2)
private void launchArticle(int prev, int pos){
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putString("pos", pos);
args.putInt("prev", prev);
newFragment.setArguments(args);
android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment currFrag = (Fragment)getFragmentManager().findFragmentById(R.id.headlines_fragment);
if (currFrag != null) {
transaction.hide(currFrag);
}
transaction.setCustomAnimations(
R.animator.card_flip_right_in,
R.animator.card_flip_right_out,
R.animator.card_flip_left_in,
R.animator.card_flip_left_out
);
transaction.replace(R.id.fragment_container, newFragment, pos);
transaction.addToBackStack(null);
transaction.commit();
}
UPDATE:
I've manage to solve the previous problem using a class that extends my framelayout of the fragments i'm using
SlidingFrameLayout.java
public class SlidingFrameLayout extends FrameLayout
{
private static final String TAG = SlidingFrameLayout.class.getName();
public SlidingFrameLayout(Context context) {
super(context);
}
public SlidingFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public float getXFraction()
{
final int width = getWidth();
if(width != 0) return getX() / getWidth();
else return getX();
}
public void setXFraction(float xFraction) {
final int width = getWidth();
setX((width > 0) ? (xFraction * width) : -9999);
}
public float getYFraction()
{
final int height = getHeight();
if(height != 0) return getY() / getHeight(); else return getY();
}
public void setYFraction(float yFraction) {
final int height = getHeight();
setY((height > 0) ? (yFraction * height) : -9999);
}
}
and by adding this to the objectAnimator:
<!-- Move -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#integer/card_flip_time_full"
android:interpolator="#android:anim/linear_interpolator"
android:propertyName="xFraction"
android:valueFrom="-1"
android:valueTo="0" />
This is working better, but the rottation axes are in the middle of the FrameLayout and it's not making the illusion of a cube... Is it possible to set the rotation axes on a certain point?

Solved the problem by creating my own methods on the extended FrameLayout. Here's the code from the extended FrameLayout:
//Rotate from Left to Right turning visible
public float getRotateLeftRightIn(){
return getRotationY();
}
public void setRotateLeftRightIn(int rotateLeftRightIn){
setPivotX(getWidth());
setPivotY(getHeight()/2);
setRotationY(rotateLeftRightIn);
}
And on the XML:
<!-- Rotate. -->
<objectAnimator
android:duration="#integer/card_flip_time_full"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:propertyName="rotateLeftRightIn"
android:valueFrom="#integer/card_flip_rotation_off"
android:valueTo="0"
android:valueType="intType"/>
In this case, #integer/card_flip_time_full stands for the duration of the entire animation and #integer/card_flip_rotation_off stands for the degrees (in this case -90%).
After this, all I need to do to make this animation to work is, when starting the fragment, set the xml files in the custom animation
transaction.setCustomAnimations(enter,exit,popEnter,popExit);
Hope this can be useful to some one ^^

An alternative to the answer accepted, you could define an animator set, as seen here:
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="100"
android:valueTo="2"
android:valueType="floatType"/>
<!-- you could have other objectAnimator elements
here for "x" and "y", or other properties -->
</set>
</item>
<item android:state_enabled="true"
android:state_pressed="false"
android:state_focused="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="100"
android:valueTo="2"
android:valueType="floatType"/>
</set>
</item>
How to use StateListAnimator?

Related

Fragment transition having a blinking effect

I'm trying to put slide animation on a FragmentContainerView. However, the fragment that should appear flickers before starting the animation.
To summarize, when the transaction begins, the current fragment slides out, the next fragment flickers and then slides in.
I'm not asking for direct solutions, but if somebody knows what can cause this kind of bug, because I don't even know what to look at to solve this problem.
Edit: Code samples
FragmentContainerView (XML)
<androidx.fragment.app.FragmentContainerView
android:id="#+id/frame_cigarette"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/toolbar_activity_filter"
app:layout_constraintBottom_toBottomOf="parent"
/>
Code for fragment transition
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right,
R.anim.slide_out_left,
R.anim.slide_in_left,
R.anim.slide_out_right
)
.replace(
R.id.frame_cigarette,
CigaretteCravingFragment.newInstance(),
CigaretteCravingFragment::class.java.simpleName
)
.addToBackStack(CigaretteCravingFragment::class.java.simpleName)
.commit()
Code for an exit slide animation (slide out right)
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:toXDelta="100%p" />
</set>
Code for an enter slide animation (slide in left)
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="-100%p"
android:toXDelta="0" />
</set>
I had the same problem and I solved this few moments ago. In my case Im using navigation graph to define all animation between fragments but in your case should work the same because setCustomAnimations also can take #AnimatorRes. You have to use ObjectAnimator and custom layout. Inspiration from: Animate the transition between fragments
CustomConstraintLayout.java
public class CustomConstraintLayout extends ConstraintLayout {
public float getXFraction() {
return getX() / getWidth();
}
public float getYFraction() {
return getY() / getHeight();
}
public void setXFraction(float xFraction) {
final int width = getWidth();
setX((width > 0) ? (xFraction * width) : -9999);
}
public void setYFraction(float yFraction) {
final int height = getHeight();
setY((height > 0) ? (yFraction * height) : -9999);
}
}
slide_in_from_bottom_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:valueType="floatType"
android:propertyName="yFraction"
android:valueFrom="1.0"
android:valueTo="0.0"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:duration="#integer/change_view_anim_time"/>
</set>
YourFragment.xml
<?xml version="1.0" encoding="utf-8"?>
<com.yourapp.ui.CustomConstraintLayout 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">
<!-- some stuff -->
</com.yourapp.ui.CustomConstraintLayout>

Rotation is working while translation is not working at same time in android

First of all, please don't declare it as duplicate because i have been spending time on stackoverflow since last five days and i have read a lot of answers but still not able to achieve this.
I want card flip animation in my application. I tried https://developer.android.com/training/animation/cardflip.html also but exit animation is not being played there for me.
So i used two frame layouts for two fragments in a Linear Layout and then tried to rotate and translate first frame layout towards left out of screen while rotating and translating next frame layout in from right.First frame is rotating as required but translation is not there.
Please help to make frame layout translate also with rotation so that next fragment could enter the screen.
My code is:
activity_card.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/toolbar"
android:background="#color/colorPrimary"
>
</android.support.v7.widget.Toolbar>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="horizontal"
>
<FrameLayout
android:id="#+id/frame_layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
>
</FrameLayout>
<FrameLayout
android:id="#+id/frame_layout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
>
</FrameLayout>
</LinearLayout>
</LinearLayout>
card_flip_left_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Rotate. -->
<objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:startOffset="0"
android:duration="1000" />
<!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="0"
android:duration="800" />
<objectAnimator
android:propertyName="translationX"
android:valueTo="-200"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:startOffset="0"
android:duration="1000"
/>
</set>
card_flip_right_in.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Before rotating, immediately set the alpha to 0. -->
<objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" />
<!-- Rotate. -->
<objectAnimator
android:valueFrom="180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:duration="1000" />
<!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
<objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="0"
android:duration="800" />
<objectAnimator
android:propertyName="translationX"
android:valueTo="0"
android:interpolator="#android:interpolator/accelerate_decelerate"
android:startOffset="0"
android:duration="1000"
/>
</set>
CardActivity.java:
public class CardActivity extends AppCompatActivity implements
Toolbar.OnMenuItemClickListener {
FragmentTransaction fragmentTransaction;
FrameLayout layout1, layout2;
String visibleFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle("Digital card");
toolbar.setTitleTextColor(Color.WHITE);
toolbar.inflateMenu(R.menu.menu);
toolbar.setOnMenuItemClickListener(this);
layout1 = (FrameLayout) findViewById(R.id.frame_layout1);
layout2 = (FrameLayout) findViewById(R.id.frame_layout2);
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame_layout1, GeneralDetailsFragment.newInstance());
fragmentTransaction.replace(R.id.frame_layout2, AddMoreDetailsFragment.newInstance());
visibleFragment = "layout1";
fragmentTransaction.commit();
}
#Override
public boolean onMenuItemClick(MenuItem item) {
Animator anim1 = AnimatorInflater.loadAnimator(this,R.animator.card_flip_left_out);
Animator anim2 = AnimatorInflater.loadAnimator(this,R.animator.card_flip_right_in);
if(visibleFragment.equals("layout2"))
{
anim1.setTarget(layout2);
anim2.setTarget(layout1);
anim1.start();
anim2.start();
visibleFragment = "layout1";
}
else
{
anim1.setTarget(layout1);
anim2.setTarget(layout2);
anim1.start();
anim2.start();
visibleFragment = "layout2";
}
return true;
}
}
Try this way:
below is xml file for animation (left to right with rotation) and start animation on view. And make changes as per you need.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="5"
android:toDegrees="359" />
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0%p" //For Y - android:fromYDelta="0%p" also -value
android:toXDelta="100%p" //For Y - android:toYDelta="50%p"
android:repeatCount="0"
android:duration="3000">
</translate>
</set>
Hope this will help you.
I found a different and pretty simple way to achieve card flip animation here . This animation is not for replacing or adding fragment using FragmentTransaction but for two frame layouts which already contain corresponding Fragment.
Sample code is:
public void flip(final View front, final View back, final int duration) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
AnimatorSet set = new AnimatorSet();
set.playSequentially(
ObjectAnimator.ofFloat(front, "rotationY", 90).setDuration(duration / 2),
ObjectAnimator.ofInt(front, "visibility", View.GONE).setDuration(0),
ObjectAnimator.ofFloat(back, "rotationY", -90).setDuration(0),
ObjectAnimator.ofInt(back, "visibility", View.VISIBLE).setDuration(0),
ObjectAnimator.ofFloat(back, "rotationY", 0).setDuration(duration / 2)
);
set.start();
}
else {
front.animate().rotationY(90).setDuration(duration / 2).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
front.setVisibility(View.GONE);
back.setRotationY(-90);
back.setVisibility(View.VISIBLE);
back.animate().rotationY(0).setDuration(duration / 2).setListener(null);
}
});
}
}

how to implement umano/AndroidSlidingUpPanel lib

My App has a map fragment . And on clicking marker , an slide up animation shows from bottom to half of screen . And it slide down on clicking marker again .
I want : Slide up menu should be clickable or drag gable so that it
can move to top of screen . to be more clear , i mean either on
clicking or dragging this slide up menu which is on half of screen ,
should go to top of screen .
So far i done : On clicking marker, call the slide up animation to half of screen. :
Animation code : slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="yFraction"
android:valueType="floatType"
android:valueFrom="1.0"
android:valueTo="0.58"
android:duration="#android:integer/config_mediumAnimTime"/>
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="0.58"
android:valueTo="1.0"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
slide_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="yFraction"
android:valueType="floatType"
android:valueFrom="0.58"
android:valueTo="1.0"
android:duration="#android:integer/config_mediumAnimTime"/>
<objectAnimator
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="alpha"
android:valueType="floatType"
android:valueFrom="1"
android:valueTo="0"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
The code in Activity which calling this Animation on Marker click :
public void toggleList() {
Fragment f = getFragmentManager().findFragmentByTag(LIST_FRAGMENT_TAG);
if (f != null) {
getFragmentManager().popBackStack();
} else {
getFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_up,
R.anim.slide_down,
R.anim.slide_up,
R.anim.slide_down)
.add(R.id.list_fragment_container, BaseMapSlidingFragment
.instantiate(this, BaseMapSlidingFragment.class.getName()),
LIST_FRAGMENT_TAG
)
.addToBackStack(null).commit();
googleMap.getUiSettings().setAllGesturesEnabled(false);
if(animCheck == false){
animCheck = true;
}else
{
animCheck= false;
}
}}
menu_Sliding.up.xml
<?xml version="1.0" encoding="utf-8"?>
<com.trickyandroid.fragmenttranslate.app.view.SlidingRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#7c7c7c">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.trickyandroid.fragmenttranslate.app.view.SlidingRelativeLayout>
Custom_View :
**package com.trickyandroid.fragmenttranslate.app.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
/**
* Created by paveld on 4/13/14.
*/
public class SlidingRelativeLayout extends RelativeLayout {
private float yFraction = 0;
public SlidingRelativeLayout(Context context) {
super(context);
}
public SlidingRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlidingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private ViewTreeObserver.OnPreDrawListener preDrawListener = null;
public void setYFraction(float fraction) {
this.yFraction = fraction;
if (getHeight() == 0) {
if (preDrawListener == null) {
preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
setYFraction(yFraction);
return true;
}
};
getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
return;
}
float translationY = getHeight() * fraction;
setTranslationY(translationY);
}
public float getYFraction() {
return this.yFraction;
}
}**
Now how to get this menu to top of screen on clicking slide up menu
which is on the half of screen ?
I suggest you to use AndroidSlidingUpPanel library, that can be found here. There is no point for you to write the same thing again.
It has what you need and much more. It is easy to use and modify (I am using it in my project).
How to use
Include com.sothree.slidinguppanel.SlidingUpPanelLayout as the root element in your activity layout.
The layout must have gravity set to either top or bottom.
Make sure that it has two children. The first child is your main layout. The second child is your layout for the sliding up panel.
The main layout should have the width and the height set to match_parent.
The sliding layout should have the width set to match_parent and the height set to either match_parent, wrap_content or the max desireable height.
By default, the whole panel will act as a drag region and will intercept clicks and drag events. You can restrict the drag area to a specific view by using the setDragView method or umanoDragView attribute.

Fragment transaction animation: slide in and slide out

I've check some tutorials for animate transaction between fragments. I've used this method for animation and it works:
fragmentTransaction.setCustomAnimations(android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
But I want to invert this animation: old fragment slide out to the left, and new fragment slide in to the right, but no value of R.anim file seems to be useful for my scope.
How can I do it?
UPDATE For Android v19+ see this link via #Sandra
You can create your own animations. Place animation XML files in res > anim
enter_from_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%p" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
enter_from_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%p" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
exit_to_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="-100%p"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
exit_to_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="100%p"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
you can change the duration to short animation time
android:duration="#android:integer/config_shortAnimTime"
or long animation time
android:duration="#android:integer/config_longAnimTime"
USAGE
(note that the order in which you call methods on the transaction matters. Add the animation before you call .replace, .commit):
FragmentTransaction transaction = supportFragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
transaction.replace(R.id.content_frame, fragment);
transaction.addToBackStack(null);
transaction.commit();
There is three way to transaction animation in fragment.
Transitions
So need to use one of the built-in Transitions, use the setTranstion()
method:
getSupportFragmentManager()
.beginTransaction()
.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN )
.show( m_topFragment )
.commit()
Custom Animations
You can also customize the animation by using the setCustomAnimations() method:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations( R.anim.slide_up, 0, 0, R.anim.slide_down)
.show( m_topFragment )
.commit()
slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="translationY"
android:valueType="floatType"
android:valueFrom="1280"
android:valueTo="0"
android:duration="#android:integer/config_mediumAnimTime"/>
slide_down.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="translationY"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1280"
android:duration="#android:integer/config_mediumAnimTime"/>
Multiple Animations
Finally, It's also possible to kick-off multiple fragment animations
in a single transaction. This allows for a pretty cool effect where
one fragment is sliding up and the other slides down at the same time:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations( R.anim.abc_slide_in_top, R.anim.abc_slide_out_top ) // Top Fragment Animation
.show( m_topFragment )
.setCustomAnimations( R.anim.abc_slide_in_bottom, R.anim.abc_slide_out_bottom ) // Bottom Fragment Animation
.show( m_bottomFragment )
.commit()
To more detail you can visit URL
Note:- You can check animation according to your requirement because above may be have issue.
slide_in_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
</set>
slide_in_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
</set>
slide_out_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="-100%"
android:toYDelta="0"
/>
</set>
slide_out_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="-100%p"
/>
</set>
direction = down
activity.getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_out_down, R.anim.slide_in_down)
.replace(R.id.container, new CardFrontFragment())
.commit();
direction = up
activity.getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up)
.replace(R.id.container, new CardFrontFragment())
.commit();
I have same issue, i used simple solution
1)create sliding_out_right.xml in anim folder
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-50%p"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
2) create sliding_in_left.xml in anim folder
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="50%p" android:toXDelta="0"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
3) simply using fragment transaction setCustomeAnimations() with two custom xml and two default xml for animation as follows :-
fragmentTransaction.setCustomAnimations(R.anim.sliding_in_left, R.anim.sliding_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right );
This is another solution which I use:
public class CustomAnimator {
private static final String TAG = "com.example.CustomAnimator";
private static Stack<AnimationEntry> animation_stack = new Stack<>();
public static final int DIRECTION_LEFT = 1;
public static final int DIRECTION_RIGHT = -1;
public static final int DIRECTION_UP = 2;
public static final int DIRECTION_DOWN = -2;
static class AnimationEntry {
View in;
View out;
int direction;
long duration;
}
public static boolean hasHistory() {
return !animation_stack.empty();
}
public static void reversePrevious() {
if (!animation_stack.empty()) {
AnimationEntry entry = animation_stack.pop();
slide(entry.out, entry.in, -entry.direction, entry.duration, false);
}
}
public static void clearHistory() {
animation_stack.clear();
}
public static void slide(final View in, View out, final int direction, long duration) {
slide(in, out, direction, duration, true);
}
private static void slide(final View in, final View out, final int direction, final long duration, final boolean save) {
ViewGroup in_parent = (ViewGroup) in.getParent();
ViewGroup out_parent = (ViewGroup) out.getParent();
if (!in_parent.equals(out_parent)) {
return;
}
int parent_width = in_parent.getWidth();
int parent_height = in_parent.getHeight();
ObjectAnimator slide_out;
ObjectAnimator slide_in;
switch (direction) {
case DIRECTION_LEFT:
default:
slide_in = ObjectAnimator.ofFloat(in, "translationX", parent_width, 0);
slide_out = ObjectAnimator.ofFloat(out, "translationX", 0, -out.getWidth());
break;
case DIRECTION_RIGHT:
slide_in = ObjectAnimator.ofFloat(in, "translationX", -out.getWidth(), 0);
slide_out = ObjectAnimator.ofFloat(out, "translationX", 0, parent_width);
break;
case DIRECTION_UP:
slide_in = ObjectAnimator.ofFloat(in, "translationY", parent_height, 0);
slide_out = ObjectAnimator.ofFloat(out, "translationY", 0, -out.getHeight());
break;
case DIRECTION_DOWN:
slide_in = ObjectAnimator.ofFloat(in, "translationY", -out.getHeight(), 0);
slide_out = ObjectAnimator.ofFloat(out, "translationY", 0, parent_height);
break;
}
AnimatorSet animations = new AnimatorSet();
animations.setDuration(duration);
animations.playTogether(slide_in, slide_out);
animations.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationCancel(Animator arg0) {
}
#Override
public void onAnimationEnd(Animator arg0) {
out.setVisibility(View.INVISIBLE);
if (save) {
AnimationEntry ae = new AnimationEntry();
ae.in = in;
ae.out = out;
ae.direction = direction;
ae.duration = duration;
animation_stack.push(ae);
}
}
#Override
public void onAnimationRepeat(Animator arg0) {
}
#Override
public void onAnimationStart(Animator arg0) {
in.setVisibility(View.VISIBLE);
}
});
animations.start();
}
}
The usage of class. Let's say you have two fragments (list and details fragments)as shown below
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ui_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/list_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/details_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</FrameLayout>
Usage
View details_container = findViewById(R.id.details_container);
View list_container = findViewById(R.id.list_container);
// You can select the direction left/right/up/down and the duration
CustomAnimator.slide(list_container, details_container,CustomAnimator.DIRECTION_LEFT, 400);
You can use the function CustomAnimator.reversePrevious();to get the previous view when the user pressed back.
Have the same problem with white screen during transition from one fragment to another. Have navigation and animations set in action in navigation.xml.
Background in all fragments the same but white blank screen. So i set navOptions in fragment during executing transition
//Transition options
val options = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = R.anim.slide_in_left
popExit = R.anim.slide_out_right
}
}
.......................
this.findNavController().navigate(SampleFragmentDirections.actionSampleFragmentToChartFragment(it),
options)
It worked for me. No white screen between transistion. Magic )
For me the only way I was able to get rid of the white background on the animation is if I changed to a DialogFragment instead of a Fragment and added the following code:
Created the following styles:
<style name="WindowSlideInAndOutAnimation">
<item name="android:windowEnterAnimation">#anim/slide_in_bottom</item>
<item name="android:windowExitAnimation">#anim/slide_out_top</item>
</style>
<style name="AppFullScreenDialogTransparentTitleBar" parent="Theme.MaterialComponents.Light.Dialog">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="colorPrimaryDark">#android:color/transparent</item>
</style>
Then reference these styles in the dialog fragment as such:
override fun getTheme() = R.style.AppFullScreenDialogTransparentTitleBar
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.window?.setWindowAnimations(R.style.WindowSlideInAndOutAnimation)
//rest of code on view created here
}
Then I just navigate to the fragment as normal via the navigation component (also note to change the type in the navigation xml file for the fragment to be dialog)

shaking / wobble view animation in android

I created an anim.xml file such as below to shake imageview like IOS icon shaking in android.
However it does not provide me same result.
Is there any better idea?
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromDegrees="-2"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="2" />
Try setting android:repeatMode="reverse". Below animation gives a very reasonable immitation on my Galaxy Nexus. Obviously you can fine tune the parameters to your own liking.
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
android:fromDegrees="-5"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toDegrees="5" />
Nice shake animation;
res/anim/shake.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:duration="150"
android:fromXDelta="-10%"
android:repeatCount="5"
android:repeatMode="reverse"
android:toXDelta="10%"/>
</set>
How to use it
final Animation animShake = AnimationUtils.loadAnimation(this, R.anim.shake);
btn_done = (Button) findViewById(R.id.btn_act_confirm_done);
btn_done.startAnimation(animShake);
How to use it (Simpler version):
btn_done.startAnimation(AnimationUtils.loadAnimation(this,R.anim.shake));
You could try this:
shake.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="10"
android:duration="1000"
android:interpolator="#anim/cycle_7" />
cycle_7.xml
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:cycles="7" />
try to use this one:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="70"
android:fromDegrees="-5"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="5"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:toDegrees="5" />
<translate
android:fromXDelta="-10"
android:toXDelta="10"
android:repeatCount="5"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:duration="70" />
</set>
To make shake effect like this
First define shake animation inside anim folder as shake.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="70"
android:fromDegrees="-5"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="5"
android:repeatMode="reverse"
android:toDegrees="5" />
<translate
android:duration="70"
android:fromXDelta="-10"
android:interpolator="#android:anim/linear_interpolator"
android:repeatCount="5"
android:repeatMode="reverse"
android:toXDelta="10" />
</set>
Then in code
if (TextUtils.isEmpty(phone.getText())
|| phone.getText().length() < 10)
{
//shake animation
phone.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.shake));
}
I created a shake effect on Android and posted in GitHub. See if it works better.
https://github.com/teoinke/ShakeAnimation
Relevant code:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/overshoot_interpolator"
android:fillAfter="true">
<translate
android:startOffset="100"
android:fromXDelta="0%p"
android:toXDelta="10%p"
android:duration="50" />
<translate
android:startOffset="150"
android:fromXDelta="0%p"
android:toXDelta="-25%p"
android:duration="110" />
<translate
android:startOffset="260"
android:fromXDelta="0%p"
android:toXDelta="25%p"
android:duration="120" />
<translate
android:startOffset="380"
android:fromXDelta="0%p"
android:toXDelta="-20%p"
android:duration="130" />
<translate
android:startOffset="510"
android:fromXDelta="0%p"
android:toXDelta="10%p"
android:duration="140" />
</set>
This one works pretty well (though not perfectly) as an iOS "incorrect PIN" shaking clone:
final float FREQ = 3f;
final float DECAY = 2f;
// interpolator that goes 1 -> -1 -> 1 -> -1 in a sine wave pattern.
TimeInterpolator decayingSineWave = new TimeInterpolator() {
#Override
public float getInterpolation(float input) {
double raw = Math.sin(FREQ * input * 2 * Math.PI);
return (float)(raw * Math.exp(-input * DECAY));
}
};
shakeField.animate()
.xBy(-100)
.setInterpolator(decayingSineWave)
.setDuration(500)
.start();
/**
*
* #param view view that will be animated
* #param duration for how long in ms will it shake
* #param offset start offset of the animation
* #return returns the same view with animation properties
*/
public static View makeMeShake(View view, int duration, int offset) {
Animation anim = new TranslateAnimation(-offset,offset,0,0);
anim.setDuration(duration);
anim.setRepeatMode(Animation.REVERSE);
anim.setRepeatCount(5);
view.startAnimation(anim);
return view;
}
use:
TextView tv;
makeMeShake(tv,20,5); // it will shake quite fast
For Kotlin users:
First create an Animation resource file called shake.xml. Right click on the res folder in Android Studio, then click New > Android Resource File > enter shake for the file name and select Animation for Resource type dropdown. Click OK.
Inside shake.xml paste the following:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:duration="200"
android:fromXDelta="-5%"
android:repeatCount="3"
android:repeatMode="reverse"
android:toXDelta="5%"/>
</set>
Now just call it on a view!
From within a fragment:
myView.startAnimation(AnimationUtils.loadAnimation(requireContext(), R.anim.shake))
From within an activity:
myView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.shake))
(note - myView is the ID given to the view that you want to animate)
If you would like to fine-tune the animation, simply modify the values in shake.xml.
I created a very good approximation of iOS shaking (when you long press a icon to remove app from homescreen). You have to apply inside your code, programmatically, as it requires random number generation:
int dur1 = 70 + (int)(Math.random() * 30);
int dur2 = 70 + (int)(Math.random() * 30);
// Create an animation instance
Animation an = new RotateAnimation(-3, 3, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// Set the animation's parameters
an.setDuration(dur1); // duration in ms
an.setRepeatCount(-1); // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE);
an.setFillAfter(true); // keep rotation after animation
// Create an animation instance
Animation an2 = new TranslateAnimation(-TranslateAnimation.RELATIVE_TO_SELF,0.02f,
TranslateAnimation.RELATIVE_TO_SELF,0.02f,
-TranslateAnimation.RELATIVE_TO_SELF,0.02f,
TranslateAnimation.RELATIVE_TO_SELF,0.02f);
// Set the animation's parameters
an2.setDuration(dur2); // duration in ms
an2.setRepeatCount(-1); // -1 = infinite repeated
an2.setRepeatMode(Animation.REVERSE);
an2.setFillAfter(true); // keep rotation after animation
AnimationSet s = new AnimationSet(false);//false means don't share interpolators
s.addAnimation(an);
s.addAnimation(an2);
// Apply animation to image view
itemView.setAnimation(s);
This code was design to be applied inside an adapter's gridview (getView), but you can apply to any view by changing the last line to:
yourViewName.setAnimations(s);
Kotlin version of lincolnq's answer
val FREQ = 3f
val DECAY = 2f
val decayingSineWave = TimeInterpolator { input ->
val raw = sin(FREQ * input * 2 * Math.PI)
(raw * exp((-input * DECAY).toDouble())).toFloat()
}
// where binding.loginFrame is the view you wanna shake
binding.loguinFrame.animate()
.withEndAction{
// here you can clear the fields after the shake
}
.xBy(-100f)
.setInterpolator(decayingSineWave)
.setDuration(500)
.start()
IOS wobble animation is not that simple try to change pivot x and y randomly when rotate. You should change the value programatically though. May be you also can use translate animation simultaneously
Banging my head for more than two hours, I knew how to shake and wobble an view.
Unfortunately the accepted answer won't work apart from onCreateView of fragment.
Example if you have onClick method and inside in it. You have animation like below it won't work.
Please go through the code.
DoneStart.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
register(view);
}
});
The register method has some checks like below code
private void register(View view) {
String type = typedThings.getText.toString();
String km = Km_Now.getText().toString();
if (serviceType == null) {
animationServiceList = AnimationUtils.loadAnimation(getActivity(), R.anim.shake_wobble);
silverServiceButton.setAnimation(animationServiceList);
generalServiceButton.setAnimation(animationServiceList);
platinumServiceButton.setAnimation(animationServiceList);
animationServiceList.start();
} else if (km == null) {
animationEditText = AnimationUtils.loadAnimation(getActivity(), R.anim.shake_wobble);
Km_Now.setAnimation(animationEditText);
animationEditText.start();
}
The Call animationServiceList.start(); will never be called,
SOLUTION: Use PropertyAnimator like ObjectAnimator.
Other answers are correct as well but this is a bit smoother than them since it uses an interpolator produces smooth numbers for back an forth movement
public class WobblyView extends ImageView implements ValueAnimator.AnimatorUpdateListener {
private final ValueAnimator va = ValueAnimator.ofInt(-10, 10);
public WobblyView(Context context) {
this(context, null);
}
public WobblyView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WobblyView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setAdjustViewBounds(true);
setImageResource(R.drawable.ic_logo);
va.setInterpolator(new AccelerateDecelerateInterpolator());
va.setRepeatMode(ValueAnimator.REVERSE);
va.setRepeatCount(ValueAnimator.INFINITE);
va.setDuration(1000);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
va.addUpdateListener(this);
va.start();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
va.removeUpdateListener(this);
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int heading = (int) animation.getAnimatedValue();
setRotation(heading);
}
}

Categories

Resources