Skip to content

Commit

Permalink
cleaned up the fragment animation bug fix and provided some loging fo…
Browse files Browse the repository at this point in the history
…r potential bug fixes
  • Loading branch information
marksalpeter committed Feb 12, 2018
1 parent 20de1a6 commit 3d5189d
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
package com.marksalpeter.fragment;

import android.app.Activity;
import android.app.Fragment;

import java.lang.reflect.ParameterizedType;

/**
* ContractFragment is a fragment whos parent fragment or parent activity must implement an interface
* Created by Mark Salpeter. Original concepts taken from Jake Thwarton
* ContractFragment is a fragment whos parent fragment or parent activity must implement the interface T
* Created by Mark Salpeter. Original concepts taken from Jake Wharton
* see https://gist.github.com/JakeWharton/2621173
*/
public abstract class ContractFragment<T> extends ChildFragment {
public abstract class ContractFragment<T> extends Fragment {

public final static String TAG = ChildFragment.class.getSimpleName();
public final static String TAG = Fragment.class.getSimpleName();

private T mContract;

@Override
public void onAttach(Activity activity) {
Fragment parentFragment = getParentFragment();
android.app.Fragment parentFragment = getParentFragment();
if (parentFragment != null && isContractImplemented(parentFragment.getClass())) {
mContract = (T) getParentFragment();
} else if (isContractImplemented(activity.getClass())) {
Expand All @@ -44,6 +43,10 @@ public void onDetach() {
mContract = null;
}

/**
* getContract returns the interface of type T that must be
* implemented on either the parent fragment or the parent activity
*/
public final T getContract() {
return mContract;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,50 @@
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.ValueAnimator;
import android.app.Fragment;
import android.content.res.Resources;
import android.util.Log;

import java.lang.reflect.Field;

/**
* ChildFragment fixes a critical view bug where child fragments are sometimes removed from the screen
* Fragment fixes an animation bug where child fragments are sometimes removed from the screen
* before their parents are removed from the screen.
* More Info: http://stackoverflow.com/questions/14900738/nested-fragments-disappear-during-transition-animation
* Created by Mark Salpeter on 9/20/16.
*/
public class ChildFragment extends Fragment {
public class Fragment extends android.app.Fragment {

public static String TAG = ChildFragment.class.getSimpleName();
public static String TAG = Fragment.class.getSimpleName();

/**
* sDefaultChildAnimationDuration is an arbitrary, but reasonable transition duration we can use if
* reflection fails to obtain the parent Fragment next animation id
*/
private static final int sDefaultChildAnimationDuration = 1000;

@Override public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {

// if a child fragment is being removed because its parent is being removed
// return a fake animator that lasts the duration of the parent's animator
Fragment removingParentFragment = getRemovingParent(getParentFragment());
if (!enter && removingParentFragment != null) {
// This is a workaround for the bug where child fragments disappear when
// the parent is removed (as all children are first removed from the parent)
// See https://code.google.com/p/android/issues/detail?id=55228
long duration = getNextAnimatiorDuration(removingParentFragment);
return ValueAnimator.ofFloat(0, 1).setDuration(duration);
/**
* onCreateAnimator is overridden to fix the following animation bug:
* http://stackoverflow.com/questions/14900738/nested-fragments-disappear-during-transition-animation
*/
@Override public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
if(!enter) {
android.app.Fragment removingParentFragment = getRemovingParentFragment();
if (removingParentFragment != null) {
return ValueAnimator.ofFloat(0f, 1f).setDuration(getNextAnimatorDuration(removingParentFragment));
}
}

// inflate the animator
Animator animator = null;
try {
animator = AnimatorInflater.loadAnimator(getActivity(), nextAnim);
} catch (Exception e) {}

return animator;
return super.onCreateAnimator(transit, enter, nextAnim);
}

/**
* getRemovingParent returns the first fragment or parent fragment that is removing r null if it can't find one
* getRemovingParentFragment returns the first fragment or parent fragment that is removing r null if it can't find one
*/
private static Fragment getRemovingParent(Fragment fragment) {
private android.app.Fragment getRemovingParentFragment() {
android.app.Fragment fragment = getParentFragment();
while (fragment != null) {
if (fragment.isRemoving()) {
Log.v(TAG, fragment.toString() + " is removing");
return fragment;
}
fragment = fragment.getParentFragment();
Expand All @@ -63,21 +58,29 @@ private static Fragment getRemovingParent(Fragment fragment) {
* getNextAnimationDuration returns the "mNextAnim" animators duration
* TODO: this needs a bug fix, but its not mission critical unless people are adding fragment transition
*/
private static long getNextAnimatiorDuration(Fragment fragment) {
private static long getNextAnimatorDuration(android.app.Fragment fragment) {
try {

// attempt to get the resource ID of the next animation that will be applied to the given fragment.
Field nextAnimField = Fragment.class.getDeclaredField("mNextAnim");
Field nextAnimField = android.app.Fragment.class.getDeclaredField("mNextAnim");
nextAnimField.setAccessible(true);
int nextAnimResource = nextAnimField.getInt(fragment);
int nextAnimResourceID = nextAnimField.getInt(fragment);

Log.v(TAG, "nextAnimResourceID: " + nextAnimResourceID);
if (nextAnimResourceID < 1) {
return sDefaultChildAnimationDuration;
}

// load the animator
Animator nextAnim = AnimatorInflater.loadAnimator(fragment.getActivity(), nextAnimResource);
Animator nextAnim = AnimatorInflater.loadAnimator(fragment.getActivity(), nextAnimResourceID);

Log.v(TAG, "nextAnim.getDuration(): " + nextAnim.getDuration());

// return its duration
return(nextAnim == null || nextAnim.getDuration() < 0) ? sDefaultChildAnimationDuration : nextAnim.getDuration();

} catch (NoSuchFieldException|IllegalAccessException|Resources.NotFoundException ex) {
Log.e(TAG, ex.getMessage(), ex);
return sDefaultChildAnimationDuration;
}
}
Expand Down

0 comments on commit 3d5189d

Please sign in to comment.