mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-23 13:23:28 +03:00
Destroy fragment's presenter when they aren't needed using FragmentStack class from Nucleus' examples
This commit is contained in:
parent
11563e6f95
commit
b389db9773
6 changed files with 220 additions and 12 deletions
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…
Reference in a new issue