0

I've run into a UI challenge on Android that should be simple enough to solve, but I'm still having a hard time.

I have a RelativeLayout with inside two fragments horizontally layed out, like the Master-Detail flow. Now when something happens in the detail, I have reason to hide the Master-fragment, making the Detail-fragment go full-screen.

When I use FragmentTransaction to hide the Master-fragment, after the animation the Detail-fragment snaps instantly to it's now new available width. So in summary, it animates only the hide-part, which I can understand, but only hiding doesn't look smooth. You would expect the remaining view, it's context, to also animate to give a proper impression.

I tried custom animations as well, by using fragment.getView() on both Fragments. And although the x-translation part works well, I'm having issues with the new width for the detail-fragment. As it moves to the left, it's size should expand to the right. But instead it creates like partial snap-shots of each animated frame, instead of showing a smooth UI. I also suspect this should be doable easy enough with FragmentTransactions.

Here's a quick GIF I made from a sample iOS app to illustrate what I want to achieve. Notice how the detail-part grows/shrinks as the master hides/shows.

https://imgflip.com/gif/r8oui

My layout in the activity:

<RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:name="nl.test.slideit.SplitActivityFragment"
        android:id="@+id/fragment_menu"
        tools:layout="@layout/fragment_menu"
        android:layout_width="240dp"
        android:layout_height="match_parent" />

    <fragment android:name="nl.test.slideit.DetailFragment"
        android:id="@+id/fragment_detail"
        tools:layout="@layout/fragment_menu"
        android:layout_toRightOf="@id/fragment_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

And my FragmentTransaction:

FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out, android.R.animator.fade_in, android.R.animator.fade_out);
if (menuFragment.isHidden()) {
      transaction.show(menuFragment);
} else {
      transaction.hide(menuFragment);
}    
transaction.commit();
Justin Hammenga
  • 1,091
  • 1
  • 8
  • 14

1 Answers1

0

It's been a few days and I've come up with a working solution involving custom animations. It's fairly simple, but I guess I'm surprised I couldn't get FragmentTransaction to be more flexible.

Anywhere, here's my method to toggle the state I described in my question. It takes 2 fragments within a RelativeLayout. In my actual project I wrapped all logic in a custom class SplitViewLayout, extending RelativeLayout, so my activity and fragments don't get cluttered with code they aren't responsible for.

public void toggle() {
    int animationTime = 300;
    int masterTargetX;
    int detailTargetLeftMargin;
    if (masterIsHidden) {
        masterTargetX = 0;
        detailTargetLeftMargin = (int) getResources().getDimension(R.dimen.master_width);
        masterIsHidden = false;
    } else {
        masterTargetX = 0 - (int) getResources().getDimension(R.dimen.master_width);
        detailTargetLeftMargin = 0;
        masterIsHidden = true;
    }

    final View animatedMasterView = masterFragment.getView();
    ObjectAnimator masterAnimation = ObjectAnimator.ofFloat(animatedMasterView, "x", masterTargetX);
    masterAnimation.setDuration(animationTime);
    masterAnimation.start();

    final View animatedDetailView = detailFragment.getView();
    final RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) animatedDetailView.getLayoutParams();
    ValueAnimator detailAnimation = ValueAnimator.ofInt(params.leftMargin, detailTargetLeftMargin);
    detailAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator)
        {
            params.leftMargin = (Integer) valueAnimator.getAnimatedValue();
            animatedDetailView.requestLayout();
        }
    });
    detailAnimation.setDuration(animationTime);
    detailAnimation.start();
}

This code just animates the x-property of the masterView and animates the leftMargin of the detailView. I took that approach after reading one of the entries mentioned here: Complete Working Sample of the Gmail Three-Fragment Animation Scenario?

For people wanting to use more advanced animations then what FragmentTransaction offers, that's a pretty nice thread that points to some different approaches.

Community
  • 1
  • 1
Justin Hammenga
  • 1,091
  • 1
  • 8
  • 14