Destroy fragment's presenter when they aren't needed using FragmentStack class from Nucleus' examples
This commit is contained in:
		
							parent
							
								
									11563e6f95
								
							
						
					
					
						commit
						b389db9773
					
				
							
								
								
									
										179
									
								
								app/src/main/java/eu/kanade/mangafeed/ui/main/FragmentStack.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								app/src/main/java/eu/kanade/mangafeed/ui/main/FragmentStack.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,179 @@ | ||||
| package eu.kanade.mangafeed.ui.main; | ||||
| 
 | ||||
| import android.app.Activity; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v4.app.Fragment; | ||||
| import android.support.v4.app.FragmentManager; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import eu.kanade.mangafeed.R; | ||||
| 
 | ||||
| /** | ||||
|  * Why this class is needed. | ||||
|  * | ||||
|  * FragmentManager does not supply a developer with a fragment stack. | ||||
|  * It gives us a fragment *transaction* stack. | ||||
|  * | ||||
|  * To be sane, we need *fragment* stack. | ||||
|  * | ||||
|  * This implementation also handles NucleusSupportFragment presenter`s lifecycle correctly. | ||||
|  */ | ||||
| public class FragmentStack { | ||||
| 
 | ||||
|     public interface OnBackPressedHandlingFragment { | ||||
|         boolean onBackPressed(); | ||||
|     } | ||||
| 
 | ||||
|     public interface OnFragmentRemovedListener { | ||||
|         void onFragmentRemoved(Fragment fragment); | ||||
|     } | ||||
| 
 | ||||
|     private Activity activity; | ||||
|     private FragmentManager manager; | ||||
|     private int containerId; | ||||
|     @Nullable private OnFragmentRemovedListener onFragmentRemovedListener; | ||||
| 
 | ||||
|     public FragmentStack(Activity activity, FragmentManager manager, int containerId, @Nullable OnFragmentRemovedListener onFragmentRemovedListener) { | ||||
|         this.activity = activity; | ||||
|         this.manager = manager; | ||||
|         this.containerId = containerId; | ||||
|         this.onFragmentRemovedListener = onFragmentRemovedListener; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the number of fragments in the stack. | ||||
|      * | ||||
|      * @return the number of fragments in the stack. | ||||
|      */ | ||||
|     public int size() { | ||||
|         return getFragments().size(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Pushes a fragment to the top of the stack. | ||||
|      */ | ||||
|     public void push(Fragment fragment) { | ||||
| 
 | ||||
|         Fragment top = peek(); | ||||
|         if (top != null) { | ||||
|             manager.beginTransaction() | ||||
|                     .setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right) | ||||
|                     .remove(top) | ||||
|                     .add(containerId, fragment, indexToTag(manager.getBackStackEntryCount() + 1)) | ||||
|                     .addToBackStack(null) | ||||
|                     .commit(); | ||||
|         } | ||||
|         else { | ||||
|             manager.beginTransaction() | ||||
|                     .add(containerId, fragment, indexToTag(0)) | ||||
|                     .commit(); | ||||
|         } | ||||
| 
 | ||||
|         manager.executePendingTransactions(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Pops the top item if the stack. | ||||
|      * If the fragment implements {@link OnBackPressedHandlingFragment}, calls {@link OnBackPressedHandlingFragment#onBackPressed()} instead. | ||||
|      * If {@link OnBackPressedHandlingFragment#onBackPressed()} returns false the fragment gets popped. | ||||
|      * | ||||
|      * @return true if a fragment has been popped or if {@link OnBackPressedHandlingFragment#onBackPressed()} returned true; | ||||
|      */ | ||||
|     public boolean back() { | ||||
|         Fragment top = peek(); | ||||
|         if (top instanceof OnBackPressedHandlingFragment) { | ||||
|             if (((OnBackPressedHandlingFragment)top).onBackPressed()) | ||||
|                 return true; | ||||
|         } | ||||
|         return pop(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Pops the topmost fragment from the stack. | ||||
|      * The lowest fragment can't be popped, it can only be replaced. | ||||
|      * | ||||
|      * @return false if the stack can't pop or true if a top fragment has been popped. | ||||
|      */ | ||||
|     public boolean pop() { | ||||
|         if (manager.getBackStackEntryCount() == 0) | ||||
|             return false; | ||||
|         Fragment top = peek(); | ||||
|         manager.popBackStackImmediate(); | ||||
|         if (onFragmentRemovedListener != null) | ||||
|             onFragmentRemovedListener.onFragmentRemoved(top); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Replaces stack contents with just one fragment. | ||||
|      */ | ||||
|     public void replace(Fragment fragment) { | ||||
|         List<Fragment> fragments = getFragments(); | ||||
| 
 | ||||
|         manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); | ||||
|         manager.beginTransaction() | ||||
|                 .replace(containerId, fragment, indexToTag(0)) | ||||
|                 .commit(); | ||||
|         manager.executePendingTransactions(); | ||||
| 
 | ||||
|         if (onFragmentRemovedListener != null) { | ||||
|             for (Fragment fragment1 : fragments) | ||||
|                 onFragmentRemovedListener.onFragmentRemoved(fragment1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the topmost fragment in the stack. | ||||
|      */ | ||||
|     public Fragment peek() { | ||||
|         return manager.findFragmentById(containerId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns a back fragment if the fragment is of given class. | ||||
|      * If such fragment does not exist and activity implements the given class then the activity will be returned. | ||||
|      * | ||||
|      * @param fragment     a fragment to search from. | ||||
|      * @param callbackType a class of type for callback to search. | ||||
|      * @param <T>          a type of callback. | ||||
|      * @return a back fragment or activity. | ||||
|      */ | ||||
|     @SuppressWarnings("unchecked") | ||||
|     public <T> T findCallback(Fragment fragment, Class<T> callbackType) { | ||||
| 
 | ||||
|         Fragment back = getBackFragment(fragment); | ||||
| 
 | ||||
|         if (back != null && callbackType.isAssignableFrom(back.getClass())) | ||||
|             return (T)back; | ||||
| 
 | ||||
|         if (callbackType.isAssignableFrom(activity.getClass())) | ||||
|             return (T)activity; | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     private Fragment getBackFragment(Fragment fragment) { | ||||
|         List<Fragment> fragments = getFragments(); | ||||
|         for (int f = fragments.size() - 1; f >= 0; f--) { | ||||
|             if (fragments.get(f) == fragment && f > 0) | ||||
|                 return fragments.get(f - 1); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     private List<Fragment> getFragments() { | ||||
|         List<Fragment> fragments = new ArrayList<>(manager.getBackStackEntryCount() + 1); | ||||
|         for (int i = 0; i < manager.getBackStackEntryCount() + 1; i++) { | ||||
|             Fragment fragment = manager.findFragmentByTag(indexToTag(i)); | ||||
|             if (fragment != null) | ||||
|                 fragments.add(fragment); | ||||
|         } | ||||
|         return fragments; | ||||
|     } | ||||
| 
 | ||||
|     private String indexToTag(int index) { | ||||
|         return Integer.toString(index); | ||||
|     } | ||||
| } | ||||
| @ -3,7 +3,6 @@ package eu.kanade.mangafeed.ui.main; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.support.v4.app.Fragment; | ||||
| import android.support.v4.app.FragmentTransaction; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.widget.FrameLayout; | ||||
| 
 | ||||
| @ -19,6 +18,7 @@ import eu.kanade.mangafeed.ui.catalogue.SourceFragment; | ||||
| import eu.kanade.mangafeed.ui.download.DownloadFragment; | ||||
| import eu.kanade.mangafeed.ui.library.LibraryFragment; | ||||
| import eu.kanade.mangafeed.ui.setting.SettingsActivity; | ||||
| import nucleus.view.ViewWithPresenter; | ||||
| 
 | ||||
| public class MainActivity extends BaseActivity { | ||||
| 
 | ||||
| @ -29,6 +29,7 @@ public class MainActivity extends BaseActivity { | ||||
|     FrameLayout container; | ||||
| 
 | ||||
|     private Drawer drawer; | ||||
|     private FragmentStack fragmentStack; | ||||
| 
 | ||||
|     private final static String SELECTED_ITEM = "selected_item"; | ||||
| 
 | ||||
| @ -40,6 +41,12 @@ public class MainActivity extends BaseActivity { | ||||
| 
 | ||||
|         setupToolbar(toolbar); | ||||
| 
 | ||||
|         fragmentStack = new FragmentStack(this, getSupportFragmentManager(), R.id.content_layout, | ||||
|                 fragment -> { | ||||
|                     if (fragment instanceof ViewWithPresenter) | ||||
|                         ((ViewWithPresenter)fragment).getPresenter().destroy(); | ||||
|                 }); | ||||
| 
 | ||||
|         drawer = new DrawerBuilder() | ||||
|                 .withActivity(this) | ||||
|                 .withRootView(container) | ||||
| @ -103,17 +110,7 @@ public class MainActivity extends BaseActivity { | ||||
|     } | ||||
| 
 | ||||
|     public void setFragment(Fragment fragment) { | ||||
|         try { | ||||
|             if (fragment != null && getSupportFragmentManager() != null) { | ||||
|                 FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); | ||||
|                 if (ft != null) { | ||||
|                     ft.replace(R.id.content_layout, fragment); | ||||
|                     ft.commit(); | ||||
|                 } | ||||
|             } | ||||
|         } catch (Exception e) { | ||||
| 
 | ||||
|         } | ||||
|         fragmentStack.replace(fragment); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								app/src/main/res/anim/enter_from_left.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/src/main/res/anim/enter_from_left.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:shareInterpolator="false"> | ||||
|     <translate | ||||
|         android:duration="400" | ||||
|         android:fromXDelta="-100%" | ||||
|         android:toXDelta="0%" /> | ||||
| </set> | ||||
							
								
								
									
										8
									
								
								app/src/main/res/anim/enter_from_right.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/src/main/res/anim/enter_from_right.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:shareInterpolator="false"> | ||||
|     <translate | ||||
|         android:duration="400" | ||||
|         android:fromXDelta="100%" | ||||
|         android:toXDelta="0%" /> | ||||
| </set> | ||||
							
								
								
									
										8
									
								
								app/src/main/res/anim/exit_to_left.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/src/main/res/anim/exit_to_left.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:shareInterpolator="false"> | ||||
|     <translate | ||||
|         android:duration="400" | ||||
|         android:fromXDelta="0%" | ||||
|         android:toXDelta="-100%" /> | ||||
| </set> | ||||
							
								
								
									
										8
									
								
								app/src/main/res/anim/exit_to_right.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/src/main/res/anim/exit_to_right.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <set xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:shareInterpolator="false"> | ||||
|     <translate | ||||
|         android:duration="400" | ||||
|         android:fromXDelta="0%" | ||||
|         android:toXDelta="100%" /> | ||||
| </set> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 inorichi
						inorichi