【问题标题】:Android restrict fragments only one time go to back stackAndroid 限制 Fragment 只有一次回栈
【发布时间】:2017-04-07 08:07:57
【问题描述】:

大家好,我正在使用BottomNavigationViewBottomNavigationView 的每一项都会打开一个片段,该片段将存储在后台堆栈中,但如果一个项目多次选择后堆栈将存储该片段的最新实例。我的意思是当按下返回按钮片段时只会打开一次。

比如,有3个片段..A,B,C

片段模式:A-B-C-B-A-C-A-C

后按输出应为:C-A-B-exit

但我会得到这个模式-C-A-C-A-B-C-B-A-exit

这是我正在使用的代码-

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()){
        case R.id.nav_home:
            mFragment = getSupportFragmentManager().findFragmentByTag("Fragment_Home");
            if(!(mFragment!=null && mFragment.isVisible())){
                mFragmentManager.popBackStackImmediate("Fragment_Home", FragmentManager.POP_BACK_STACK_INCLUSIVE);
                mFragmentTransaction = mFragmentManager.beginTransaction();
                mFragmentTransaction.replace(R.id.container, new HomeFragment(), "Fragment_Home").addToBackStack("Fragment_Home").commit();
            }
            return true;
        case R.id.nav_account:
            mFragment = getSupportFragmentManager().findFragmentByTag("Fragment_Account");
            if(!(mFragment != null && mFragment.isVisible())){
                mFragmentManager.popBackStackImmediate("Fragment_Account", FragmentManager.POP_BACK_STACK_INCLUSIVE);
                mFragmentTransaction = mFragmentManager.beginTransaction();
                mFragmentTransaction.replace(R.id.container, new AccountFragment(), "Fragment_Account").addToBackStack("Fragment_Account").commit();
            }
            return true;
        case R.id.nav_category:
            mFragment = getSupportFragmentManager().findFragmentByTag("Fragment_Category");
            if(!(mFragment != null && mFragment.isVisible())){
                mFragmentManager.popBackStackImmediate("Fragment_Category", FragmentManager.POP_BACK_STACK_INCLUSIVE);
                mFragmentTransaction = mFragmentManager.beginTransaction();
                mFragmentTransaction.replace(R.id.container, new CategoryFragment(), "Fragment_Category").addToBackStack("Fragment_Category").commit();
            }
            return true;
    }
    return false;
}

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        mDrawerLayout.closeDrawer(GravityCompat.START);           
    }
    else if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        getSupportFragmentManager().popBackStack();
    }
    else if (doubleBackToExitPressedOnce) {
        finishAffinity();
    }
    else {
        this.doubleBackToExitPressedOnce = true;
        Toast.makeText(this, "Please BACK again to exit", Toast.LENGTH_SHORT).show();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                doubleBackToExitPressedOnce = false;
            }
        }, 2000);
    }
}

告诉我我的代码有什么问题。任何帮助将不胜感激。

【问题讨论】:

    标签: android android-fragments fragment-backstack


    【解决方案1】:

    对于这个问题,我建议你使用 Viewpager 而不是 Fragment back-stack。 首先,我会告诉你为什么不应该使用 Fragment backstack。 我猜在你的代码中,每当你的活动被创建时。您将立即移动到您的第一个片段。例如:A - B - C - C - C - C - C 或 A - B - C - A -B -C - A -C -B。这将像您所说的那样删除 backstack 片段,每当您再次单击片段时,您将创建一个新片段。 这是使用 Viewpager 的其他一些方法,可以让用户体验更好。 首先,我自定义了自己的FragmentStatePagerAdapter 以获得更好的性能

    public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
        // Sparse array to keep track of registered fragments in memory
        private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
    
        public SmartFragmentStatePagerAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
        }
    
        // Register the fragment when the item is instantiated
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment fragment = (Fragment) super.instantiateItem(container, position);
            registeredFragments.put(position, fragment);
            return fragment;
        }
    
        // Unregister when the item is inactive
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            registeredFragments.remove(position);
            super.destroyItem(container, position, object);
        }
    
        // Returns the fragment for the position (if instantiated)
        public Fragment getRegisteredFragment(int position) {
            return registeredFragments.get(position);
        }
    }
    

    让我们创建CustomViewPager

    public class CustomViewPager extends ViewPager {
    
        public CustomViewPager(Context context) {
            super(context);
            setMyScroller();
        }
    
        public CustomViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
            setMyScroller();
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            // Never allow swiping to switch between pages
            return false;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // Never allow swiping to switch between pages
            return false;
        }
    
        //down one is added for smooth scrolling
    
        private void setMyScroller() {
            try {
                Class<?> viewpager = ViewPager.class;
                Field scroller = viewpager.getDeclaredField("mScroller");
                scroller.setAccessible(true);
                scroller.set(this, new MyScroller(getContext()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public class MyScroller extends Scroller {
            public MyScroller(Context context) {
                super(context, new DecelerateInterpolator());
            }
    
            @Override
            public void startScroll(int startX, int startY, int dx, int dy, int duration) {
                super.startScroll(startX, startY, dx, dy, 350 /*1 secs*/);
            }
        }
    }
    

    将其放入您的 XML 文件中,如下所示:

    <YOUR_PACKAGE_NAME.CustomViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
           />
    

    这是 ViewPager 的适配器:

    public class ViewPagerAdapter extends SmartFragmentStatePagerAdapter {
        private static int NUM_ITEMS = 3;
        private BaseFragment mFragment1, mFragment2, mFragment3;
    
        public ViewPagerAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
    
            mFragment1= Fragment1.newInstance(0, FRAGMENT_TAG_1);
            mFragment2= Fragment2.newInstance(1, FRAGMENT_TAG_2);
            mFragment3= Fragment3.newInstance(2, FRAGMENT_TAG_3);
        }
    
    
        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return mFragment1;
                case 1:
                    return mFragment2;
                case 2:
                    return mFragment3;
                default:
                    return null;
            }
        }
    
        @Override
        public int getCount() {
            return NUM_ITEMS;
        }
    
        @Override
        public int getItemPosition(Object object) {
                return POSITION_NONE;
        }
    
        @Override
        public Parcelable saveState() {
            return null;
        }
    
    }
    

    在您的 Activity 中,让其在 onCreateView() 中初始化

            mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
            mViewPagerAdapter = new ViewPagerAdapter(mFragmentManager);
            mViewPager.setAdapter(mViewPagerAdapter);
            mFrameLayout.setVisibility(View.GONE);
            mViewPager.setVisibility(View.VISIBLE);
            mViewPager.setSaveEnabled(true);
    

    当你点击进入一个标签时,你只需要使用

    mViewPager.setCurrentItem(NUMBER_OF_TIME);
    

    【讨论】:

    • 主要问题是我必须使用底部导航视图也对我的导航视图做同样的事情。所以我必须解决这个问题。但无论如何感谢您的帮助
    【解决方案2】:

    您应该做的是将活动 A、B 和 C 声明为“singleTop”

    如果当前任务的顶部已经存在 Activity 的实例,系统会通过调用其 onNewIntent() 方法将 Intent 路由到该实例,而不是创建 Activity 的新实例。 Activity可以被实例化多次,每个实例可以属于不同的任务,一个任务可以有多个实例(但前提是后栈顶的Activity不是该Activity的现有实例)。 例如,假设一个任务的后台堆栈由根活动 A 和顶部的活动 B、C 和 D 组成(堆栈是 A-B-C-D;D 在顶部)。 D 类型的活动的意图到达。如果 D 具有默认的“标准”启动模式,则启动该类的新实例并且堆栈变为 A-B-C-D-D。但是,如果 D 的启动模式是“singleTop”,则 D 的现有实例通过 onNewIntent() 接收意图,因为它位于堆栈的顶部——堆栈仍然是 A-B-C-D。但是,如果针对 B 类型的活动的意图到达,则将 B 的新实例添加到堆栈中,即使它的启动模式是“singleTop”。

    注意:当创建一个新的活动实例时,用户可以 按返回按钮返回上一个活动。但是当一个 活动的现有实例处理新意图,用户不能 按下返回键返回活动之前的状态 新意图到达 onNewIntent()。

    You should read the documentation here 了解有关该主题的更多信息

    【讨论】:

    • 我正在使用片段,因为底部导航视图代码将被编写一次,工具栏和导航代码将被编写一次。
    猜你喜欢
    • 1970-01-01
    • 2012-09-13
    • 1970-01-01
    • 1970-01-01
    • 2021-01-09
    • 1970-01-01
    • 1970-01-01
    • 2022-07-20
    • 1970-01-01
    相关资源
    最近更新 更多