I am currently working on an Android app in which I use a FloatingActionButton. I would like to use the speed dial to have multiple actions that spin/jump out of the action button as described in this page by Google on Android design, or as could be seen in an earlier version of the Keep app (sorry, but I can only post one link). I am using the Android Design Support library specifically version 23.1.1 (com.android.support:design:23.1.1). I already searched using Google and looked at the reference for the FloatingActionButton but couldn't find anything concerning the speed dial.
I would like to know if there is a way to easily achieve this using the default FloatingActionButton, or if I have to program all transitions/animations manually?
Additionally I would like to have little labels next to the buttons, describing the action, if possible.
Thank you in advance!
I'm here to add my 2 cents because this is where I landed after Googling for that exact title.
I hope it doesn't come too late to help someone like me.
First off, the solution comes from here, so is not mine. I just tried and it works nicely. So i thought i share with you in a single post rather have you go dig the code up from there.
The solution uses com.android.support:design:25.3.1 library so be sure to add that to build.gradle and it requires API 21 onwards.
The bad news is that it is composed from several small moving parts: 5 animators, 5 drawables plus the icons and layouts and of course, the code, the good news is that it works as it should, is highly customizable and doesn't require any coding outside MainActivity.
Some notes:
The big fab's image morphs between more and minus signs and rotates when tapped.
Buttons can have text, provided you put both the text and each small fab inside a LineaLayout and move the button id to the LinearLayout so it gets animated instead of the fab, but it requires code to hide and show the text when necessary.
This is the result:
So, the ingredients:
Drawables (res/drawable/).
animated_minus.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
android:height="24dp">
<group android:name="plus_group" android:pivotX="12" android:pivotY="12">
<path
android:name="plus_path"
android:strokeColor="#android:color/white"
android:strokeWidth="3"
android:pathData="M12,0L12,24M0,12,L24,12" />
</group>
</vector>
animated_plus.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/plus">
<target
android:animation="#animator/rotate_clockwise"
android:name="plus_group" />
<target
android:animation="#animator/plus_to_minus"
android:name="plus_path" />
</animated-vector>
fab_background.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<item>
<shape android:shape="oval">
<solid android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
minus.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
android:height="24dp">
<group android:name="plus_group" android:pivotX="12" android:pivotY="12">
<path
android:name="plus_path"
android:strokeColor="#android:color/white"
android:strokeWidth="3"
android:pathData="M12,12L12,12M0,12,L24,12" />
</group>
</vector>
plus.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp"
android:height="24dp">
<group android:name="plus_group" android:pivotX="12" android:pivotY="12">
<path
android:name="plus_path"
android:strokeColor="#android:color/white"
android:strokeWidth="3"
android:pathData="M12,0L12,24M0,12,L24,12" />
</group>
</vector>
Animators (res/animator/).
fab_state_list_animator.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:state_enabled="true">
<set>
<objectAnimator
android:propertyName="translationZ"
android:duration="100"
android:valueTo="3dp"
android:valueType="floatType" />
<objectAnimator
android:propertyName="elevation"
android:duration="0"
android:valueTo="5dp"
android:valueType="floatType" />
</set>
</item>
<!-- base state -->
<item android:state_enabled="true">
<set>
<objectAnimator
android:propertyName="translationZ"
android:duration="100"
android:valueTo="0"
android:startDelay="100"
android:valueType="floatType" />
<objectAnimator
android:propertyName="elevation"
android:duration="0"
android:valueTo="5dp"
android:valueType="floatType" />
</set>
</item>
<item>
<set>
<objectAnimator
android:propertyName="translationZ"
android:duration="0"
android:valueTo="0"
android:valueType="floatType" />
<objectAnimator
android:propertyName="elevation"
android:duration="0"
android:valueTo="0"
android:valueType="floatType" />
</set>
</item>
</selector>
minus_to_plus.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="pathData"
android:valueFrom="M12,0L12,24M12,12,L12,12"
android:valueTo="M12,0L12,24M0,12,L24,12"
android:valueType="pathType"
android:duration="#android:integer/config_mediumAnimTime" />
plus_to_minus.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="pathData"
android:valueFrom="M12,0L12,24M0,12,L24,12"
android:valueTo="M12,0L12,24M12,12,L12,12"
android:valueType="pathType"
android:duration="#android:integer/config_mediumAnimTime" />
rotate_anticlockwise.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="rotation"
android:valueFrom="90"
android:valueTo="0"
android:valueType="floatType"
android:duration="#android:integer/config_mediumAnimTime" />
rotate_clockwise.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="90"
android:valueType="floatType"
android:duration="#android:integer/config_mediumAnimTime" />
Layouts. (res/layout/)
fab.xml. All fabs are declared here. Replace android:src with your own icon on the first 3 ImageButtons.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
android:id="#+id/fab_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/activity_vertical_margin"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
android:clipChildren="false" >
<!-- Please note that the #id are defined the first time they're referenced from top to bottom -->
<ImageButton
android:id="#+id/fab_action_3"
style="#style/FloatingActionButton.Mini"
android:src="#drawable/ic_volume_up_white_24dp"
android:layout_above="#+id/fab_action_2"
android:layout_alignEnd="#+id/fab"
android:contentDescription="#null"
android:backgroundTint="#color/sa_gray"
android:width="24dp"
android:height="24dp"
android:onClick="fabAction3" />
<ImageButton
android:id="#id/fab_action_2"
style="#style/FloatingActionButton.Mini"
android:src="#drawable/ic_credit_card_white_24dp"
android:layout_above="#+id/fab_action_1"
android:layout_alignEnd="#id/fab"
android:contentDescription="#null"
android:backgroundTint="#color/sa_gray"
android:width="24dp"
android:height="24dp"
android:onClick="fabAction2" />
<ImageButton
android:id="#id/fab_action_1"
style="#style/FloatingActionButton.Mini"
android:src="#drawable/ic_add_shopping_cart_white_24dp"
android:layout_above="#id/fab"
android:layout_alignEnd="#id/fab"
android:contentDescription="#null"
android:backgroundTint="#color/sa_gray"
android:width="24dp"
android:height="24dp"
android:onClick="fabAction1" />
<ImageButton
android:id="#id/fab"
style="#style/FloatingActionButton"
android:src="#mipmap/ic_add_w"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:contentDescription="#null"
android:visibility="visible"
android:layout_marginTop="8dp" />
</RelativeLayout>
</merge>
And lastly.
The code (java//MainActivity.java)
a) Some declarations:
private static final String TAG = "Floating Action Button";
private static final String TRANSLATION_Y = "translationY";
private ImageButton fab;
private boolean expanded = false;
private View fabAction1;
private View fabAction2;
private View fabAction3;
private float offset1;
private float offset2;
private float offset3;
b) Delete the usual fab code on MainActivity's onCreate:
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
and replace it with:
final ViewGroup fabContainer = (ViewGroup) findViewById(R.id.fab_container);
fab = (ImageButton) findViewById(R.id.fab);
fabAction1 = findViewById(R.id.fab_action_1);
// insert onClickListener here
fabAction2 = findViewById(R.id.fab_action_2);
// insert onClickListener here
fabAction3 = findViewById(R.id.fab_action_3);
// insert onClickListener here
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
expanded = !expanded;
if (expanded) {
expandFab();
} else {
collapseFab();
}
}
});
fabContainer.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
fabContainer.getViewTreeObserver().removeOnPreDrawListener(this);
offset1 = fab.getY() - fabAction1.getY();
fabAction1.setTranslationY(offset1);
offset2 = fab.getY() - fabAction2.getY();
fabAction2.setTranslationY(offset2);
offset3 = fab.getY() - fabAction3.getY();
fabAction3.setTranslationY(offset3);
return true;
}
});
c) Add supporting functions on MainActivity (animation code mostly and the 3 small fab's onClick methods):
private void collapseFab() {
fab.setImageResource(R.drawable.animated_minus);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(createCollapseAnimator(fabAction1, offset1),
createCollapseAnimator(fabAction2, offset2),
createCollapseAnimator(fabAction3, offset3));
animatorSet.start();
animateFab();
}
private void expandFab() {
fab.setImageResource(R.drawable.animated_plus);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(createExpandAnimator(fabAction1, offset1),
createExpandAnimator(fabAction2, offset2),
createExpandAnimator(fabAction3, offset3));
animatorSet.start();
animateFab();
}
private Animator createCollapseAnimator(View view, float offset) {
return ObjectAnimator.ofFloat(view, TRANSLATION_Y, 0, offset)
.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
}
private Animator createExpandAnimator(View view, float offset) {
return ObjectAnimator.ofFloat(view, TRANSLATION_Y, offset, 0)
.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
}
private void animateFab() {
Drawable drawable = fab.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
}
public void fabAction1(View view) {
Log.d(TAG, "Action 1");
Toast.makeText(this, "Go shopping!", Toast.LENGTH_SHORT).show();
}
public void fabAction2(View view) {
Log.d(TAG, "Action 2");
Toast.makeText(this, "Gimme money!", Toast.LENGTH_SHORT).show();
}
public void fabAction3(View view) {
Log.d(TAG, "Action 3");
Toast.makeText(this, "Turn it up!", Toast.LENGTH_SHORT).show();
}
d) Reference the fab.xml layout from res/layout/activity_main.xml
Delete the fab declaration:
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:srcCompat="#android:drawable/ic_dialog_email" />
Replace with:
<include layout="#layout/fab" />
Last notes:
Feel free to scrap the onClick code for the fabs and replace it with
onClickListener. Those should go right where the comment that says
// insert onClickListener here. Just remember to delete the
onClick attribute for each fab in fab.xml file and get rid of
the last 3 functions in MainActivity (fabAction1, fabAction2
and fabAction3).
Most measures, dimensions, etc. I put them right in the code to avoid including even more files.
The code is not optimized on changed in any way.
I hope this helps someone and sorry for the wall of text.
I would like to know if there is a way to easily achieve this using the default FloatingActionButton
FAB from Design Library does not have this feature. You need to look for 3rd party FABs (there's a few on android-arsenal to choose from)
This library is implementing the Speed Dial from the Material Design guidelines:
https://github.com/leinardi/FloatingActionButtonSpeedDial
Related
I have made a splash screen for Android by implementing a new Activity. It points to a xml theme that includes android specific code (#style/Splash). That points to a drawable xml of splash.
My question is, If we can do these kinds of platform specific implementations, can we also create animations using this same pattern? I have placed an example of code from the android dev website to give you an idea of what I am trying to include. If yes, what would this code look like in XF.Android and how can we reference it?
SplashActivity.cs
[Activity(Label = "SplashActivity", Theme = "#style/Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
StartActivity(typeof(MainActivity));
}
}
style/Splash
<style name="Splash" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">#drawable/splash</item>
</style>
drawable/splash
<?xml version="1.0" encoding="utf-8" ?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#03045E"/>
</item>
<item>
<bitmap
android:src="#drawable/logo1"
android:tileMode="disabled"
android:gravity="center"/>
</item>
</layer-list>
Example anim that I want to include hyperspace_jump.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.4"
android:fromYScale="1.0"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="700" />
<set
android:interpolator="#android:anim/accelerate_interpolator"
android:startOffset="700">
<scale
android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="400" />
<rotate
android:fromDegrees="0"
android:toDegrees="-45"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="400" />
</set>
</set>
1. Create a Anim folder and put the hyperspace_jump.xml file in it.
2. Create the layouts.
SplashScreen Layout:
<RelativeLayout xmlns:p1="http://schemas.android.com/apk/res/android"
p1:minWidth="25px"
p1:minHeight="25px"
p1:layout_width="match_parent"
p1:layout_height="match_parent"
p1:background="#android:color/white"
p1:id="#+id/relativeLayout1">
<ImageView
p1:layout_width="wrap_content"
p1:layout_height="wrap_content"
p1:id="#+id/imageView"
p1:layout_centerVertical="true"
p1:layout_centerHorizontal="true"
p1:src="#drawable/a01" />
</RelativeLayout>
Main Layout:
<?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:minWidth="25px"
android:minHeight="25px">
<TextView
android:text="Main Activity Started"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/textView1"/>
</LinearLayout>
3. Code Behind:
SplashScreenActivity:
public class SplashScreenActivity : Activity
{
ImageView imageView;
Animation view_animation;
TextView textview;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
RequestWindowFeature(Android.Views.WindowFeatures.NoTitle);
SetContentView (Resource.Layout.SplashScreen);
imageView = (ImageView)FindViewById(Resource.Id.imageView);
view_animation = AnimationUtils.LoadAnimation(this,Resource.Animation.hyperspace_jump);
imageView.StartAnimation(view_animation);
view_animation.AnimationEnd += Rotate_AnimationEnd;
}
private void Rotate_AnimationEnd(object sender, Animation.AnimationEndEventArgs e)
{
Finish();
StartActivity(typeof(MainActivity));
}
}
MainActivity:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
Toast.MakeText(this, "Welcome to MainActivity", ToastLength.Long).Show();
}
4. Screenshot:
You could download from the GitHub for reference. https://github.com/WendyZang/Test/tree/master/SplashScreenDemo
I am trying to animate a vector path to a different path in my android app for testing but its not working properly . no animation is displayed on screen and neither any animation is shown.
My vector file is:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="8dp"
android:height="5dp"
android:viewportWidth="8"
android:viewportHeight="5">
<path
android:name="redot"
android:pathData="M2.5,2.5L6,2.5"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#E61B1B"
android:fillType="evenOdd"
android:strokeLineCap="round"/>
</vector>
And my VectorAnimation file is:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:targetApi="lollipop"
android:drawable="#drawable/ic_reddot">
<target
android:animation="#anim/redanim"
android:name="redot"/>
</animated-vector>
My Animation file in anim folder is:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="10000"
android:propertyName="pathData"
android:valueFrom="M2.5,2.5L6,2.5"
android:valueTo="M2.5,2.5L31,2.5"
android:valueType="pathType" />
</set>
And finally my MainActivityCode is as following:
public class MainActivity extends AppCompatActivity {
private TextView testObj;
private ImageView reddot;
private AnimatedVectorDrawable animation;
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// testObj = (TextView) findViewById(R.id.testObj);
// testObj.setVisibility(View.INVISIBLE);
reddot = (ImageView) findViewById(R.id.reddot);
Drawable d = reddot.getBackground();
if (d instanceof AnimatedVectorDrawable) {
Log.d("testanim", "onCreate: instancefound" );
animation = (AnimatedVectorDrawable) d;
animation.start();
}
}
}
Use Shape Shifter tool and than export the animated vector drawable file generated by shape shifter . add this file in your drawable folder and than add this as background to your imageview which you want to animate
my avd_anim.xml file:
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="80dp"
android:height="12dp"
android:viewportWidth="80"
android:viewportHeight="12">
<path
android:name="path"
android:pathData="M 6 6 L 74 6"
android:strokeColor="#e61b1b"
android:strokeWidth="12"
android:strokeLineCap="round"
android:fillType="evenOdd"/>
</vector>
</aapt:attr>
<target android:name="path">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="pathData"
android:duration="1000"
android:valueFrom="M 6 6 L 74 6"
android:valueTo="M 6 6 L 1 6"
android:valueType="pathType"
android:interpolator="#android:anim/linear_interpolator"/>
</aapt:attr>
</target>
My activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="#+id/object"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:src="#drawable/avd_anim"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
and finally my MainActivity.class
public class MainActivity extends AppCompatActivity {
private ImageView image;
private AnimatedVectorDrawable animation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image = (ImageView) findViewById(R.id.object);
}
#Override
protected void onStart() {
super.onStart();
Drawable d = image.getDrawable();
if (d instanceof AnimatedVectorDrawable) {
Log.d("testanim", "onCreate: instancefound" + d.toString());
animation = (AnimatedVectorDrawable) d;
animation.start();
}
}
}
You should use redot.getDrawable() instead of getBackground()
And if you are using app:srcCompat="#drawable/..." instead of android:src=#drawable/... for ImageView you should add
if (d instanceof AnimatedVectorDrawableCompat) {
AnimatedVectorDrawableCompat avd = (AnimatedVectorDrawableCompat) d;
avd.start();
}
And android:animation="#anim/redanim" should be android:animation="#animator/redanim". Use animator folder
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);
}
});
}
}
I want to apply Animation in my Image View inside Navigation View.
I saw its working fine in some other apps. I have tried with following code but its not working for me.
drawer_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:checkableBehavior="single">
<item
android:id="#+id/nav_logout"
android:icon="#drawable/ic_logout"
android:title="#string/nav_menu_logout" />
</group>
<group
android:id="#+id/menu_sync"
android:checkableBehavior="single">
<item
android:id="#+id/nav_sync_now"
android:title="#string/string_menu_sync_now"
app:actionLayout="#layout/layout_menu_sync_now"
app:showAsAction="always" />
</group>
</menu>
NavigationActivity.java
runOnUiThread(new Runnable() {
#Override
public void run() {
MenuItem menuItemSync = navigationBinding.navView.getMenu().findItem(R.id.nav_sync_now);
menuItemSync.setEnabled(false);
menuItemSync.setTitle(R.string.string_menu_syncing);
mImageViewSyncing = (AppCompatImageView) menuItemSync.getActionView().findViewById(R.id.nav_sync_now_image_view);
mImageViewSyncing.setColorFilter(ContextCompat.getColor(mContext, R.color.colorAccent));
/* Create Animation */
Animation mRotateAnimation = AnimationUtils.loadAnimation(mContext, R.anim.button_rotate);
/* start Animation */
mImageViewSyncing.startAnimation(mRotateAnimation);
}
});
button_rotate.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<rotate
android:duration="2500"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="restart"
android:toDegrees="0"
android:fromDegrees="360"/>
</set>
layout_menu_sync_now.xml
<android.support.v7.widget.AppCompatImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_sync_now_image_view"
android:layout_width="24dp"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:layout_gravity="end|center_vertical"
app:srcCompat="#drawable/ic_sync_black" />
Is there any suggestion what to do?
Thanks in advance.
I have a fragment that is to replace another fragment. I want to specify the animation. But the animation is ignored.
transaction.replace(R.id.my_fragment, newFrag);
transaction.addToBackStack(null);
transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up);
slide_in_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
slide_out_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
All I am really trying to achieve is for the new fragment to slide in from the bottom. My animations are ignored. What is the code missing?
transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up);
transaction.addToBackStack(null);
transaction.replace(R.id.my_fragment, newFrag);
slide_in_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
slide_out_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="-100%p" />
It's been some time since this question was asked but here is an answer for other people coming here:
e1da is right insofar as that setCustomAnimation() call has to be called before replace(). Otherwise the animation will not show.
The second problem is that you probably are using native fragments that cannot be animated with the view animations.
Use the following files:
slide_in_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true" >
<objectAnimator
android:duration="500"
android:propertyName="y"
android:valueFrom="1280"
android:valueTo="0"
android:valueType="floatType" />
</set>
slide_out_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true" >
<objectAnimator
android:duration="500"
android:propertyName="y"
android:valueFrom="0"
android:valueTo="-1280"
android:valueType="floatType" />
</set>
A little explanation:
You have to differentiate between view animation for support fragments on one hand and property animation for native fragments on the other hand.
View animation:
Is the pre-android 3.0 way to animate views. Sample code for this is the slide_in.xml and slide_up.xml by user3093402
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
It is worth mentioning you cannot animate fragments with view animation. The exception to this are fragments from the support library (android.support.v4.app.Fragment).
Property animation
This is the way to animate objects after android 3.0. It is also declared as .xml files but makes use of the "valueAnimator" tag (objectAnimator extends valueAnimator). Examples are in the answer to the question.
This is the way how native fragments (android.app.Fragment) can be animated.
See also:
http://developer.android.com/guide/topics/graphics/overview.html
swap fragment in an activity via animation
Hope this helps,
Kai
EDIT: As pointed out by Raphael Royer-Rivard the fixed screen size is bad practice. It would be better to use a constant from the OS like in
getWindowManager().getDefaultDisplay().getMetrics(metrics).xdpi
(see DisplayMetrics). But I haven't done any Android development for some time so I don't know which one.
code for slide_in_up :
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
</set>
code for slide_in_down:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
</set>
code for slide_out_up:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:toYDelta="100%" />
</set>
code for slide_out_down:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:toYDelta="-100%" />
</set>
and after that in your activity or fragment set animation like below:
Fragment fragment = new Fragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_in_down, R.anim.slide_out_down, R.anim.slide_out_up);
transaction.replace(container, fragment).commit();
Currently with android.transition this is as simple as fragment.enterTransition = Slide() or fragment.enterTransition = Fade()
Note: Min version L.
R.anim will not work there but R.animator wil do.
for example
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
This answer is written in Kotlin. For sake of the example, I'm just hiding or showing it based on it's current isHidden attribute. I'm using androidx.transition.Slide
Link: https://developer.android.com/reference/androidx/transition/Slide
val frag = supportFragmentManager.findFragmentById(R.id.frag_id_from_xml_layout)
frag?.let {
val ft = supportFragmentManager.beginTransaction()
frag.enterTransition = Slide()
frag.exitTransition = Slide()
if (frag.isHidden) ft.show(frag) else ft.hide(frag)
ft.commit()
}
here is complete working example
Pressing the button toggles between 2 fragments A and B (by slide animation right to left). The fragments are just stupid text (AAAAAA and BBBBB) with different backgrounds.
MainActivity.java
package com.example.slidetrans;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
boolean showingA = true;
Button button;
A a;
B b;
private void incarnate(FragmentManager fm){
int layoutId = R.id.frame;
boolean fragmentWasNull = false;
Fragment f = fm.findFragmentById(layoutId);
if (f == null){
if (showingA){
f = a = new A();
} else {
f = b = new B();
}
fragmentWasNull = true;
}
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, showingA ? a : b, "main").commit();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
incarnate(fm);
button = (Button)findViewById(R.id.button);
OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.setCustomAnimations(R.anim.in, R.anim.out);
transaction.replace(R.id.frame, showingA ? new B() : new A()).commit();
showingA = !showingA;
button.setText(showingA ? "slide in B" : "slide in A");
}
};
button.setOnClickListener(listener);
}
}
LL.java
package com.example.slidetrans;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class LL extends LinearLayout {
public LL(Context context) {
super(context);
}
public LL(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();
float newWidth = (width > 0) ? (xFraction * width) : -9999;
setX(newWidth);
}
}
main.xml (layout)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="slide in B" />
<FrameLayout
android:id="#+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
a.xml (layout)
<?xml version="1.0" encoding="utf-8"?>
<com.example.slidetrans.LL xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#00FF00"
>
<TextView
android:id="#+id/aText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AAAAAAAAAAAAAAAAAA"
android:textSize="30sp"
android:textStyle="bold"
/>
</com.example.slidetrans.LL>
b.xml (layout)
<?xml version="1.0" encoding="utf-8"?>
<com.example.slidetrans.LL xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFF00"
>
<TextView
android:id="#+id/bText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:textStyle="bold"
android:text="BBBBBBBBBB"
/>
</com.example.slidetrans.LL>
in.xml (anim)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:interpolator="#android:anim/linear_interpolator"
android:propertyName="xFraction"
android:valueFrom="1.0"
android:valueTo="0.0"
android:valueType="floatType" />
</set>
out.xml (anim)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:interpolator="#android:anim/linear_interpolator"
android:propertyName="xFraction"
android:valueFrom="0.0"
android:valueTo="-1.0"
android:valueType="floatType" />
</set>