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.content.Intent; | ||||||
| import android.os.Bundle; | import android.os.Bundle; | ||||||
| import android.support.v4.app.Fragment; | import android.support.v4.app.Fragment; | ||||||
| import android.support.v4.app.FragmentTransaction; |  | ||||||
| import android.support.v7.widget.Toolbar; | import android.support.v7.widget.Toolbar; | ||||||
| import android.widget.FrameLayout; | 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.download.DownloadFragment; | ||||||
| import eu.kanade.mangafeed.ui.library.LibraryFragment; | import eu.kanade.mangafeed.ui.library.LibraryFragment; | ||||||
| import eu.kanade.mangafeed.ui.setting.SettingsActivity; | import eu.kanade.mangafeed.ui.setting.SettingsActivity; | ||||||
|  | import nucleus.view.ViewWithPresenter; | ||||||
| 
 | 
 | ||||||
| public class MainActivity extends BaseActivity { | public class MainActivity extends BaseActivity { | ||||||
| 
 | 
 | ||||||
| @ -29,6 +29,7 @@ public class MainActivity extends BaseActivity { | |||||||
|     FrameLayout container; |     FrameLayout container; | ||||||
| 
 | 
 | ||||||
|     private Drawer drawer; |     private Drawer drawer; | ||||||
|  |     private FragmentStack fragmentStack; | ||||||
| 
 | 
 | ||||||
|     private final static String SELECTED_ITEM = "selected_item"; |     private final static String SELECTED_ITEM = "selected_item"; | ||||||
| 
 | 
 | ||||||
| @ -40,6 +41,12 @@ public class MainActivity extends BaseActivity { | |||||||
| 
 | 
 | ||||||
|         setupToolbar(toolbar); |         setupToolbar(toolbar); | ||||||
| 
 | 
 | ||||||
|  |         fragmentStack = new FragmentStack(this, getSupportFragmentManager(), R.id.content_layout, | ||||||
|  |                 fragment -> { | ||||||
|  |                     if (fragment instanceof ViewWithPresenter) | ||||||
|  |                         ((ViewWithPresenter)fragment).getPresenter().destroy(); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|         drawer = new DrawerBuilder() |         drawer = new DrawerBuilder() | ||||||
|                 .withActivity(this) |                 .withActivity(this) | ||||||
|                 .withRootView(container) |                 .withRootView(container) | ||||||
| @ -103,17 +110,7 @@ public class MainActivity extends BaseActivity { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void setFragment(Fragment fragment) { |     public void setFragment(Fragment fragment) { | ||||||
|         try { |         fragmentStack.replace(fragment); | ||||||
|             if (fragment != null && getSupportFragmentManager() != null) { |  | ||||||
|                 FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); |  | ||||||
|                 if (ft != null) { |  | ||||||
|                     ft.replace(R.id.content_layout, fragment); |  | ||||||
|                     ft.commit(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } catch (Exception e) { |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										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