Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FloatingActionButton expand into a new activity

On the android material design principles page, one of the examples shows a FAB expanding into a new full screen. (Under "Full Screen")

http://www.google.com/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-transitions

I've tried to implement the same effect in my app, but with little success.
I managed to create a FAB that expands into a view using this code as reference: https://gist.github.com/chris95x8/882b5c5d0aa2096236ba.

It worked, but I was wondering whether I could apply the same effect to an activity transition. I've tried looking it up and playing with it myself but could not find anything that might work.

I know I could make the FAB expand into a Fragment and not a whole new activity, but I'm not sure if that's what being done, and whether that's optimal or not.

And so my question is, is there a way to implement the fab-expanding reveal effect as an activity transition, or is it supposed to just reveal a new fragment?

like image 459
Asaf Avatar asked Jul 25 '15 09:07

Asaf


People also ask

How to add floating action buttons in activity_main?

Now open activity_main.xml and add the floating action buttons. In the activity_main.xml file add the floating action buttons and invoke the following code. Now invoke the normal FAB button.

What is Floating Action Button (Fab)?

Floating Action Button (FAB) in Android with Example. The floating action button is a bit different button from the ordinary buttons. Floating action buttons are implemented in the app’s UI for primary actions (promoted actions) for the users and the actions under the floating action button are prioritized by the developer.

What are the different types of floating action buttons in Android?

There are mainly four types of floating action buttons available on Android. In this article let’s discuss the Normal/Regular Floating Action Button and Mini Floating Action Button with a sample example in Android. Regular FABs are FABs that are not expanded and are regular size.

How to add floating action buttons to a vector asset?

One can do that by right-clicking drawable folder -> New -> Vector Asset. Refer to the following image to import the vector Icon. Now select your vector icon: Now open activity_main.xml and add the floating action buttons.


2 Answers

I am developing an app which expands a FloatingActionButton into a new Activity. I'm not sure that if you like my implementation, but please see pictures at first:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

So the first picture shows MainActivity and the last one shows SecondActivity, which is "expanded" from FAB.

Now, I want to mention that I'm not actually expanding a FAB into a new Activity but I can let user feel that the new page is expanded from that FAB, and I think that's enough for both developers and users.

Here's implementation:

Preparation:

  1. A FloatingActionButton of course,
  2. Visit https://github.com/kyze8439690/RevealLayout and import this library to your project. It is used to play reveal animation. It has a custom BakedBezierInterpolator to control reveal animation and make it material-styled.

Steps:

  1. create activity_main.xml like this:

    <FrameLayout     xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="match_parent">      <!--Your main content here-->      <RevealLayout         android:id="@+id/reveal_layout"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:visibility="invisible">          <View             android:id="@+id/reveal_view"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:visibility="invisible"/>      </RevealLayout>  </FrameLayout> 
  2. find Views:

    mRevealLayout = (RevealLayout) findViewById(R.id.reveal_layout); mRevealView = findViewById(R.id.reveal_view); 
  3. expand when user clicks FAB:

    mFab.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View v) {         mFab.setClickable(false); // Avoid naughty guys clicking FAB again and again...         int[] location = new int[2];         mFab.getLocationOnScreen(location);         location[0] += mFab.getWidth() / 2;         location[1] += mFab.getHeight() / 2;          final Intent intent = new Intent(MainActivity.this, SecondActivity.class);          mRevealView.setVisibility(View.VISIBLE);         mRevealLayout.setVisibility(View.VISIBLE);          mRevealLayout.show(location[0], location[1]); // Expand from center of FAB. Actually, it just plays reveal animation.         mFab.postDelayed(new Runnable() {             @Override             public void run() {                 startActivity(intent);                 /**                  * Without using R.anim.hold, the screen will flash because of transition                  * of Activities.                  */                 overridePendingTransition(0, R.anim.hold);             }         }, 600); // 600 is default duration of reveal animation in RevealLayout         mFab.postDelayed(new Runnable() {             @Override             public void run() {                 mFab.setClickable(true);                 mRevealLayout.setVisibility(View.INVISIBLE);                 mViewToReveal.setVisibility(View.INVISIBLE);             }         }, 960); // Or some numbers larger than 600.     } }); 

    And here is hold.xml in res/anim:

    <set     xmlns:android="http://schemas.android.com/apk/res/android"     android:shareInterpolator="false">      <translate         android:duration="960" <!-- Enough-large time is OK -->         android:fromXDelta="0%"         android:fromYDelta="0%"         android:toXDelta="0%"         android:toYDelta="0%"/>  </set> 

That's all.

Improvements:

  1. RevealLayout has a bug(plays rectangular instead of circular reveal animation) for devices under API 17(Android 4.2), you can add these lines in constructor of it:

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {     setLayerType(View.LAYER_TYPE_SOFTWARE, null); } 
  2. If your SecondActivity contains complicated contents, a simple View used as reveal_view in the layout.xml isn't enough/perfect. You can include the second layout inside the RevealLayout reveal_layout. It seems wasteful and hard to control if the second layout won't appear same at every time. But for me, it will. So you can make other improvements if you should.

  3. If you want to implement totally same animation shown in Material Design Guide, you can set layout_height of the RevealLayout into a specific number instead of match_parent. After expanding animation ends(or some time after the animation plays, which should make the whole process of animation smoothly), then you can animate translationY. The important point is, just cheat users visually by controlling animation duration.

Finally, this is my own experience/attempt and I'm a beginner in developing Android apps. If there are any mistakes/further improvements, please leave comments/edit my answer. Thank you.

like image 178
ywwynm Avatar answered Sep 28 '22 10:09

ywwynm


I made a custom activity, based on this question Circular reveal transition for new activity , that handle the CircularRevealAnimation and his reverse effect when the activity finish:

public class RevealActivity extends AppCompatActivity {  private View revealView;  public static final String REVEAL_X="REVEAL_X"; public static final String REVEAL_Y="REVEAL_Y";  public void showRevealEffect(Bundle savedInstanceState, final View rootView) {      revealView=rootView;      if (savedInstanceState == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {         rootView.setVisibility(View.INVISIBLE);          ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver();          if(viewTreeObserver.isAlive()) {             viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {                 @Override                 public void onGlobalLayout() {                      circularRevealActivity(rootView);                      if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {                         rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);                     } else {                         rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);                     }                  }             });         }      } }  @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void circularRevealActivity(View rootView) {      int cx = getIntent().getIntExtra(REVEAL_X, 0);     int cy = getIntent().getIntExtra(REVEAL_Y, 0);      float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());      // create the animator for this view (the start radius is zero)     Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, 0, finalRadius);     circularReveal.setDuration(400);      // make the view visible and start the animation     rootView.setVisibility(View.VISIBLE);     circularReveal.start(); }  @Override public boolean onOptionsItemSelected(MenuItem item) {  switch (item.getItemId()) {  case android.R.id.home: onBackPressed();break; return super.onOptionsItemSelected(item);  }      }  @Override public void onBackPressed() {     destroyActivity(revealView); }  private void destroyActivity(View rootView) {     if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)         destroyCircularRevealActivity(rootView);     else         finish(); }  @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void destroyCircularRevealActivity(final View rootView) {     int cx = getIntent().getIntExtra(REVEAL_X, 0);     int cy = getIntent().getIntExtra(REVEAL_Y, 0);      float finalRadius = Math.max(rootView.getWidth(), rootView.getHeight());      // create the animator for this view (the start radius is zero)     Animator circularReveal = ViewAnimationUtils.createCircularReveal(rootView, cx, cy, finalRadius, 0);     circularReveal.setDuration(400);      circularReveal.addListener(new Animator.AnimatorListener() {         @Override         public void onAnimationStart(Animator animator) {          }          @Override         public void onAnimationEnd(Animator animator) {             rootView.setVisibility(View.INVISIBLE);             finishAfterTransition();         }          @Override         public void onAnimationCancel(Animator animator) {          }          @Override         public void onAnimationRepeat(Animator animator) {          }     });      // make the view visible and start the animation     rootView.setVisibility(View.VISIBLE);     circularReveal.start(); } } 

You can extend this with your own activity and call in your onCreate the method 'showRevealEffect' like this:

@Override protected void onCreate(@Nullable Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.your_activity_layout);      //your code      View root= findViewById(R.id.your_root_id);     showRevealEffect(savedInstanceState, root);  } 

You also have to use a transparent theme like this one:

<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">     <item name="android:windowIsTranslucent">true</item>     <item name="android:windowBackground">@android:color/transparent</item>     <item name="colorPrimary">@color/colorPrimary</item>     <item name="colorPrimaryDark">@color/colorPrimaryDark</item>     <item name="colorAccent">@color/colorAccent</item>     <item name="colorControlNormal">@android:color/white</item> </style> 

In the end, to launch this activity you should pass via extra the coordinates where the animation should start:

int[] location = new int[2];      fab.getLocationOnScreen(location);      Intent intent = new Intent(this, YourRevealActivity.class);      intent.putExtra(SearchActivity.REVEAL_X, location[0]);     intent.putExtra(SearchActivity.REVEAL_Y, location[1]);      startActivity(intent); 
like image 42
Bronx Avatar answered Sep 28 '22 09:09

Bronx