I need to animate a path in a vector using a gradient, but I cannot find a solution on how to do this. Any help is appreciated.
<?xml version="1.0" encoding="utf-8"?>
<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="88dp"
android:viewportWidth="80"
android:viewportHeight="88">
<path
android:name="curve"
android:pathData="M79.6,54.8623V54.3618C79.7,52.8601 79.8,50.9579 79.9,48.8555V48.6553C79.9,47.1536 80,45.6519 80,44.0501C80,42.4482 80,40.8464 79.9,39.4448V39.2446C79.8,37.1422 79.7,35.24 79.6,33.7383V32.537C79.5,30.9352 79.4,29.934 79.4,29.8339C79.4,29.6337 79.4,29.5336 79.3,29.3333V29.2332C79.3,28.9329 79.2,28.6325 79.2,28.4323V28.3322C78.5,24.9283 76.6,21.2241 74.3,18.521C73.4,17.4198 72.4,16.5188 71.4,15.7179C71.3,15.6177 70.2,14.8168 68.3,13.5154C66.1,12.0137 63.0999,10.0114 59.5999,8.0091C53.5,4.3049 48,1.802 47.2999,1.5017H47.2C45.6,0.8009 43.5999,0.3003 41.5,0.1001C41.0999,0.1001 40.7,0 40.2999,0H38.5999C35.7999,0.1001 33.2,0.6007 31.1,1.5017C30.8,1.6018 25.2999,4.1047 19,7.8089C19,7.8089 18.9,7.909 18.7999,7.909C12.4,11.7133 7.3,15.4175 7.0999,15.6177C5.4999,16.8191 4.0999,18.3208 2.7999,20.223C2.4999,20.6234 2.4999,21.7247 3.7999,21.7247H6.9999C8.4,21.7247 8.7,20.9238 10.2,19.8225C11.9,18.6212 15.2,16.1183 21.4,12.4141C25.7999,9.8111 30,7.7088 32,6.8077C32,6.8077 32.0999,6.8077 32.0999,6.7076H32.4C32.9,6.5074 33.2,6.3072 33.2,6.3072C34.6,5.7065 36.6,5.306 38.7,5.2059H39.5C40.0999,5.2059 40.7,5.306 41.2999,5.306C42.5999,5.5063 43.7,5.7065 44.7,6.1069C44.7999,6.1069 44.9,6.207 45,6.207C45,6.207 45.1,6.207 45.2,6.3072C46.1,6.7076 51.3,9.1103 56.9,12.4141C60.2,14.3163 63.1999,16.3185 65.1999,17.7201C66.9999,18.9215 68.1,19.7224 68.1,19.8225C68.9,20.4232 69.7,21.2241 70.4,22.1251C72.3,24.5279 73.8,27.8316 74,30.3345C74,30.3345 74.1,31.4357 74.2,33.0375V36.4414C74.2999,38.6439 74.4,41.1468 74.4,43.8498C74.4,46.5529 74.2999,49.1559 74.2,51.2582V54.6621C74.1,56.364 74,57.3652 74,57.3652C73.7,59.868 72.2,63.1718 70.4,65.5745C69.7,66.4755 68.9,67.3766 68.1,67.8771C68.1,67.8771 66.9999,68.678 65.1999,69.9795C63.1999,71.3811 60.2,73.3834 56.9,75.2855C51.3,78.5893 46.1,80.992 45.2,81.3925C45.1,81.3925 45.0999,81.4926 45,81.4926C44.9,81.4926 44.7999,81.5927 44.7,81.5927C43.7999,81.9932 42.5999,82.2935 41.2999,82.3936C40.7,82.4937 40.0999,82.4937 39.5,82.4937H38.7C36.6,82.4937 34.6,82.0933 33.2,81.3925C33.2,81.3925 32.9,81.2924 32.4,80.992C32.3,80.992 32.3,80.8919 32.2,80.8919C32.2,80.8919 32.0999,80.8919 32.0999,80.7918H32C32,80.7918 31.9,80.7918 31.9,80.6917C31.7999,80.6917 31.8,80.6917 31.7,80.5916C29.6,79.5904 25.5999,77.5882 21.4,75.0853C15.2,71.4812 11.9,68.9784 10.2,67.6769C8.7,66.9761 8.4,66.1752 6.9999,66.1752H3.9C2.6,66.1752 2.6999,67.2764 2.9,67.6769C4.2,69.5791 5.5999,71.0808 7.2,72.2821C7.4,72.4824 12.5,76.1866 18.9,79.9909C19.1999,80.1911 19.6,80.3913 19.9,80.5916C25.7999,83.9954 30.9,86.2981 31.2,86.4983C33.2,87.3993 35.9,88 38.7,88H40.4C40.8,88 41.1999,88 41.5999,87.8999C43.6999,87.6997 45.7,87.1991 47.2999,86.4983H47.4C48.1,86.198 53.6,83.6951 59.7,80.091C63.1,78.0887 66.2,76.0865 68.3,74.5848C70.1,73.2833 71.3,72.4824 71.4,72.3823C72.4,71.6815 73.4,70.6803 74.3,69.5791C76.6,66.7759 78.5,63.0717 79.2,59.7679V59.6678C79.2999,59.3675 79.3,59.0671 79.3,58.8669V58.7668C79.3,58.5666 79.3,58.4664 79.4,58.2662C79.4,58.1661 79.5,57.165 79.6,55.5631V54.8623Z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="-0.32032"
android:startX="41.2774"
android:endY="87.8803"
android:endX="41.2774"
android:type="linear">
<item android:offset="0" android:color="#FF666666"/>
<item android:offset="0.4" android:color="#FF777777"/>
<item android:offset="0.66" android:color="#FF818181"/>
<item android:offset="1" android:color="#FF919191"/>
</gradient>
</aapt:attr>
</path>
</vector>
</aapt:attr>
<target
android:name="curve" >
<!-- There should be an animated gradient -->
</target>
</animated-vector>
The solution does not have to be in an XML file, it may be in code.
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 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