Cilenco
Cilenco

Reputation: 7137

Animate layout change of bottom sheet

In my application I use a bottom sheet (from the support library) which works great. Now I would like to animate a layout change while the sheet is dragged up. For this I have created a subclass of BottomSheetCallback (this is normaly an inner class of a Fragment so not all objects used in this calss are initialized here):

public class MyBehavior extends BottomSheetBehavior.BottomSheetCallback {

    Transition transition;
    float lastOffset = 0;
    Scene scene;

    public PlayerBehavior() {
        TransitionInflater inflater = TransitionInflater.from(getContext());
        transition = inflater.inflateTransition(R.transition.player);
        //transition.setDuration(300);

        scene = fullLayout;

        transition.setInterpolator(new Interpolator() {
            @Override
            public float getInterpolation(float v) {
                return lastOffset;
            }
        });
    }

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        if(newState == BottomSheetBehavior.STATE_DRAGGING) {
            TransitionManager.go(scene, transition);
        }
    }

    @Override
    public void onSlide(View bottomSheet, final float slideOffset) {
        scene = (slideOffset > lastOffset) ? smallLayout : fullLayout;
        lastOffset = slideOffset;
    }
}

As you can see I also created two Scene from different layout files and a custom Transition to animate between the scenes with the TransitionManager. My problem is that the Transition should be based on the slideOffset parameter (in range of 0-1) but the TransitionManager uses the Animation class in the background which is normally time based in Android.

I tried to create the custom Intapolator but this does not work properly. So how can I create a Transition which is based on an external variable and not on time?

Upvotes: 35

Views: 27255

Answers (5)

C.F.G
C.F.G

Reputation: 1463

This is my try using setWindowsAnimation(res int style):

mainBottomSheet.getWindow().setWindowAnimations(R.style.YourAnimation);

and in styles.xml add your favorite animations as follow:

<style name="YourAnimation" parent="Animation.AppCompat.Dialog">
      <item name="android:windowEnterAnimation">@anim/slide_in</item>
      <item name="android:windowExitAnimation">@anim/slide_out</item>
</style>

Add the following files in res/anime directory:

<!-- slide_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:duration="700"
     android:interpolator="@anim/interpolator_slight_overshoot">

    <translate
        android:fromYDelta="-100%"
        android:toYDelta="0%"/>

</set>

Upvotes: 0

Prashant Kumar
Prashant Kumar

Reputation: 53

Based on your description, I think you are trying to achieve something like google maps bottom sheet behaviour. The layout changes as the bottomsheet is dragged up.

If that is what you are trying to achieve then you don't need to enforce custom animations, as the bottomsheetdialog itself has those animation behaviour when incorporated inside a parent Coordinator Layout.

Here is a sample code of how I'm implementing the same behaviour. It also makes the FloatingActionButton invisible when the bottomsheet is dragged up to full screen size :

  1. Create a bottomsheetdialog that you want to use inside your main layout

    public class CustomBottomDialog extends BottomSheetDialogFragment {
    
    String mSomeName;
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // if some arguments are passed from the calling activity 
        mSomeName = getArguments().getString("some_name");
    
    
    }
    
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View bottomSheet = inflater.inflate(R.layout.bottomsheet_layout, container, false);
         // initialise your bottomsheet_layout items here 
        TextView tvName = bottomSheet.findViewById(R.id.display_name);
        tvName.setText(mSomeName); 
        tvName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               // do something here
                ((MainActivity)getActivity()).doSomething();
            }
        });
    
        return bottomSheet;
    }
    }
    
  2. bottomsheet_layout:

    <android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <android.support.design.widget.FloatingActionButton
    android:id="@+id/nav"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/navigation_tilt_grey"
    app:backgroundTint="@color/colorAccent"
    app:elevation="3dp"
    app:fabSize="normal"
    android:layout_marginEnd="@dimen/activity_horizontal_margin"
    app:layout_anchor="@+id/live_dash"
    app:layout_anchorGravity="top|right" />
    
    <!--BottomSheet-->
    
    <android.support.v4.widget.NestedScrollView
    android:id="@+id/live_dash"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#F3F3F3"
    android:clipToPadding="true"
    app:layout_behavior="android.support.design.widget.BottomSheetBe 
    havior"
    tools:layout_editor_absoluteY="150dp">
    
    <!--Include your items here, the height of all items combined
    will take the main screen layout size with animation-->
    
    </android.support.v4.widget.NestedScrollView>
    
    </android.support.design.widget.CoordinatorLayout>
    
  3. Calling this BottomSheet from your activity:

    public void notifyBottomSheet(String somename){
    
    BottomSheetDialogFragment customDialogFragment = new CustomBottomDialog();
    Bundle args = new Bundle();
    args.putString("some_name", somename);
    customDialogFragment.setArguments(args);
    customDialogFragment.show(getSupportFragmentManager(), customDialogFragment.getTag());
    customDialogFragment.setCancelable(false); // if you don't wish to hide
    }
    

    Hope this solves what you are trying to achieve.

Upvotes: 4

Drping
Drping

Reputation: 53

I'm not sure if that is what you want but maybe instead of using transition, you can use the function animate() since with this function, you can change all things about your animation (time, visibility etc.).

Upvotes: 0

akshat saini
akshat saini

Reputation: 1

## Translation Animation ##
<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:fillAfter="true"
    >
    <translate
        android:fromYDelta="100%p"
        android:toYDelta="-30%p"
        android:duration="900" />
</set>

## Main Activity ##

@Override
protected void onResume() {
    super.onResume();
    Animation am= AnimationUtils.loadAnimation(this,R.anim.fadeout);
    tv5.startAnimation(am);
    Animation myanim= AnimationUtils.loadAnimation(this,R.anim.translate);
    tv1.startAnimation(myanim);
    myanim.setStartOffset(500);
    Animation animation= AnimationUtils.loadAnimation(this,R.anim.translate);
    animation.setStartOffset(1000);
    tv2.startAnimation(animation);
    Animation an= AnimationUtils.loadAnimation(this,R.anim.translate);
    an.setStartOffset(1500);
    tv3.startAnimation(an);
    Animation ab= AnimationUtils.loadAnimation(this,R.anim.translate);
    ab.setStartOffset(2000);
    tv4.startAnimation(ab);
    Animation ac= AnimationUtils.loadAnimation(this,R.anim.fadein);
    ac.setStartOffset(2500);
    btn1.startAnimation(ac);
}

Upvotes: 0

Alexandru Cristescu
Alexandru Cristescu

Reputation: 3948

To easily slide something off the bottom of the screen, you can use code such as:

final int activityHeight = findViewById(android.R.id.content).getHeight();
cardContainer.animate().yBy(activityHeight - cardContainer.getY()).setDuration(SLIDE_OUT_DURATION);

where cardContainer is the view you are trying to slide off the screen.

See this blog post for the complete example. Note that you can also use translationY instead of yBy. Another, more generic way of doing it is with this code:

public static ViewPropertyAnimator slideOutToBottom(Context ctx, View view) {
    final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
    int[] coords = new int[2];
    view.getLocationOnScreen(coords);
    return view.animate().translationY(screenHeight - coords[Y_INDEX]).setDuration(SLIDE_OUT_DURATION);
}

public static ViewPropertyAnimator slideInFromBottom(Context ctx, View view) {
    final int screenHeight = ctx.getResources().getDisplayMetrics().heightPixels;
    int[] coords = new int[2];
    view.getLocationOnScreen(coords);
    view.setTranslationY(screenHeight - coords[Y_INDEX]);
    return view.animate().translationY(0).setDuration(SLIDE_IN_DURATION).setInterpolator(new OvershootInterpolator(1f));
}

Upvotes: 0

Related Questions