Android backstack 喜欢 instagram

Android backstack like instagram

简短版本:我正在尝试实现像 Instagram 应用程序一样的后台堆栈。

他们的导航如何工作

在 instagram 中,他们使用底部栏的导航。不知道代码的样子。他们创造了某种 4 通道后叠。

假设我点击主页(第 1 道)。然后我设法点击一个 post -> 点击一个用户 -> 点击另一个 post -> 最后点击一个标签

然后我在该通道的后台堆栈中添加了 4 页。

然后我做一些类似于另一个通道的事情(假设是帐户页面)。

我知道有 2 个通道,每个通道都有 4 页。如果我此时按下返回按钮。我会在打开它们时返回相同的页面。

但是如果我改为单击返回主页(从底部导航)并从那里单击后退按钮。我将从第 1 道而不是第 2 道返回。

大问题

我怎样才能像backstacking一样实现这条车道?有没有我没有想到的简单方法?

到目前为止我做了什么

我做的不多。我创建了一个测试项目,我在其中试验了这种类型的导航。到目前为止我设法做的是为我所有的底部栏导航页面创建一个巨大的后台堆栈

我猜测我将如何实现这种功能是将 backstack 的某些部分移到顶部并将其他部分移回。但这怎么可能?

我设法解决了我遇到的问题。不要给我满分。我有一个我看过的参考代码。请检查下面。

这可能不是一个可靠的解决方案。但它可能会帮助您理解其工作原理背后的逻辑。因此,您可以创建适合自己需求的解决方案。

希望这对您有所帮助:)

我的应用程序的工作背景

首先。我只想让大家知道我的应用程序是如何工作的。所以你知道我为什么选择我的路线来实现这个导航。我有一个 activity 保留对它正在使用的所有根片段的引用。 activity 一次只添加一个根片段。取决于用户点击的按钮。

当 activity 创建新的根片段时。它将把它推送到处理它的管理 class。

activity 本身会覆盖 onBackPressed() 并调用组成的 backstacks onBackpressed() 函数。

activity 还将向管理 class 传递一个监听器。此侦听器将告诉 activity 何时关闭应用程序。以及何时刷新当前活动片段

BackStackManager.java

这是管理 class。它保留对所有不同后台堆栈的引用。它还将任何片段事务职责委托给其 FragmentManager class.

public class BackStackManager {

    //region Members
    /** Reference to the made up backstack */
    private final LinkedList<BackStack> mBackStacks;
    /** Reference to listener */
    private final BackStackHelperListener mListener;
    /** Reference to internal fragment manager */
    private final BackStackFragmentManager mFragmentManager;
    //endregion


    //region Constructors
    public BackStackManager(@NonNull final BackStackHelperListener listener,
                            @NonNull final FragmentManager fragmentManager) {
        mBackStacks = new LinkedList<>();
        mListener = listener;
        mFragmentManager = new BackStackFragmentManager(fragmentManager);
    }
    //endregion


    //region Methods


    /** When adding a new root fragment
     * IMPORTANT: Activity his holding the reference to the root. */
    public void addRootFragment(@NonNull final Fragment fragment,
                                final int layoutId) {
        if (!isAdded(fragment)) {
            addRoot(fragment, layoutId);
        }
        else if (isAdded(fragment) && isCurrent(fragment)) {
            refreshCurrentRoot();
        }
        else {
            switchRoot(fragment);
            mFragmentManager.switchFragment(fragment);
        }
    }

    /** When activity is calling onBackPressed */
    public void onBackPressed() {
        final BackStack current = mBackStacks.peekLast();
        final String uuid = current.pop();

        if (uuid == null) {
            removeRoot(current);
        }
        else {
            mFragmentManager.popBackStack(uuid);
        }
    }

    /** Adding child fragment */
    public void addChildFragment(@NonNull final Fragment fragment,
                                 final int layoutId) {

        final String uuid = UUID.randomUUID().toString();
        final BackStack backStack = mBackStacks.peekLast();

        backStack.push(uuid);

        mFragmentManager.addChildFragment(fragment, layoutId, uuid);
    }

    /** Remove root */
    private void removeRoot(@NonNull final BackStack backStack) {
        mBackStacks.remove(backStack);


        //After removing. Call close app listener if the backstack is empty
        if (mBackStacks.isEmpty()) {
            mListener.closeApp();
        }
        //Change root since the old one is out
        else {
            BackStack newRoot = mBackStacks.peekLast();
            mFragmentManager.switchFragment(newRoot.mRootFragment);
        }
    }

    /** Adding root fragment */
    private void addRoot(@NonNull final Fragment fragment, final int layoutId) {

        mFragmentManager.addFragment(fragment, layoutId);

        //Create a new backstack and add it to the list
        final BackStack backStack = new BackStack(fragment);
        mBackStacks.offerLast(backStack);
    }

    /** Switch root internally in the made up backstack */
    private void switchRoot(@NonNull final Fragment fragment) {

        for (int i = 0; i < mBackStacks.size(); i++) {
            BackStack backStack = mBackStacks.get(i);
            if (backStack.mRootFragment == fragment) {
                mBackStacks.remove(i);
                mBackStacks.offerLast(backStack);
                break;
            }
        }
    }

    /** Let listener know to call refresh */
    private void refreshCurrentRoot() {
        mListener.refresh();
    }

    /** Convenience method */
    private boolean isAdded(@NonNull final Fragment fragment) {
        for (BackStack backStack : mBackStacks) {
            if (backStack.mRootFragment == fragment) {
                return true;
            }
        }
        return false;
    }

    /** Convenience method */
    private boolean isCurrent(@NonNull final Fragment fragment) {
        final BackStack backStack = mBackStacks.peekLast();
        return backStack.mRootFragment == fragment;
    }

    //endregion
}

BackStackFragmentManager.java

这 class 处理所有片段事务。比如adding/removing/hiding/showing。这个 class 位于 BackStackManager class 中。

public class BackStackFragmentManager {

    //region Members
    /** Reference to fragment manager */
    private final FragmentManager mFragmentManager;
    /** Last added fragment */
    private Fragment mLastAdded;
    //endregion


    //region Constructors
    public BackStackFragmentManager(@NonNull final FragmentManager fragmentManager) {
        mFragmentManager = fragmentManager;
    }
    //endregion


    //region Methods

    /** Switch root fragment */
    public void switchFragment(@NonNull final Fragment fragment) {
        final FragmentTransaction transaction = mFragmentManager.beginTransaction();

        transaction.show(fragment);
        transaction.hide(mLastAdded);

        transaction.commit();

        mLastAdded = fragment;
    }

    /** Adding child fragment to a root */
    public void addChildFragment(@NonNull final Fragment fragment,
                                 final int layoutId,
                                 @NonNull final String tag) {


        final FragmentTransaction transaction = mFragmentManager.beginTransaction();
        transaction.add(layoutId, fragment, tag);
        transaction.commit();
    }


    /** Add a root fragment */
    public void addFragment(@NonNull Fragment fragment, int layoutId) {
        final FragmentTransaction transaction = mFragmentManager.beginTransaction();

        //since we hide/show. This should only happen initially
        if (!fragment.isAdded()) {
            transaction.add(layoutId, fragment, fragment.getClass().getName());
        }
        else {
            transaction.show(fragment);
        }

        if (mLastAdded != null) {
            transaction.hide(mLastAdded);
        }

        transaction.commit();

        mLastAdded = fragment;
    }

    /** Pop back stack
     * Function is removing childs that is not used!
     */
    public void popBackStack(@NonNull final String tag) {
        final Fragment fragment = mFragmentManager.findFragmentByTag(tag);
        final FragmentTransaction transaction = mFragmentManager.beginTransaction();
        transaction.remove(fragment);
        transaction.commit();
    }

    //endregion
}

BackStack.java

这是一个简单的 class,它只处理对根的内部引用和对所有后台堆栈子条目的标记。以及处理这些子条目

public class BackStack {

    //region Members
    public final Fragment mRootFragment;
    final LinkedList<String> mStackItems;
    //endregion


    //region Constructors
    public BackStack(@NonNull final Fragment rootFragment) {
        mRootFragment = rootFragment;
        mStackItems = new LinkedList<>();
    }

    //endregion


    //region Methods
    public String pop() {
        if (isEmpty()) return null;
        return mStackItems.pop();
    }

    public void push(@NonNull final String id) {
        mStackItems.push(id);
    }


    public boolean isEmpty() {
        return mStackItems.isEmpty();
    }
    //endregion
}

监听器

这个就不多说了。它由 activity

实现
public interface BackStackHelperListener {
    /** Let the listener know that the app should close. The backstack is depleted */
    void closeApp();

    /** Let the listener know that the user clicked on an already main root. So app can do
     * a secondary action if needed
     */
    void refresh();

}

参考资料

https://blog.f22labs.com/instagram-like-bottom-tab-fragment-transaction-android-389976fb8759