【问题标题】:actionbar up navigation with fragments带有片段的操作栏向上导航
【发布时间】:2022-01-16 04:25:24
【问题描述】:

我有一个选项卡式操作栏/viewpager 布局,其中包含三个选项卡,分别是 ABC。在标签 C 标签(片段)中,我正在添加另一个片段,例如片段 D。与

 DFragment f= new DFragment();
 ft.add(android.R.id.content, f, "");
 ft.remove(CFragment.this);
 ft.addToBackStack(null);
 ft.commit();

我在 DFragment 的 onResume 中修改 actionbar 以添加按钮:

ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);

现在在 DFragment 中,当我按下硬件(电话)后退按钮时,我会返回到选择 CFragment 的原始选项卡式(ABC)布局。如何使用操作栏向上按钮实现此功能?

【问题讨论】:

标签: android android-fragments android-actionbar


【解决方案1】:

实现 OnBackStackChangedListener 并将此代码添加到您的 Fragment Activity。

@Override
public void onCreate(Bundle savedInstanceState) {
    //Listen for changes in the back stack
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    //Handle when activity is recreated like on orientation Change
    shouldDisplayHomeUp();
}

@Override
public void onBackStackChanged() {
    shouldDisplayHomeUp();
}

public void shouldDisplayHomeUp(){
   //Enable Up button only  if there are entries in the back stack
   boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
   getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}

@Override
public boolean onSupportNavigateUp() {
    //This method is called when the up button is pressed. Just the pop back stack.
    getSupportFragmentManager().popBackStack();
    return true;
}

【讨论】:

  • 关于onSupportNavigateUp(),“方法不会覆盖其超类中的方法”。
  • 如果您已经有onOptionsItemSelected,也可以检查itemId android.R.id.home,而不是添加onSupportNavigateUp
  • 如果 API 版本 >= 14,使用 onNavigateUp 代替 onSupportNavigateUp @Override public boolean onNavigateUp() { //当按下向上按钮时调用该方法。只是弹回堆栈。 getFragmentManager().popBackStack();返回真; }
  • ActionBar 中的应用程序图标旁边是否应该显示一个向上插入符号?当我实现这段代码时,我没有看到。我只能点击图标,但它什么也没做。安卓 4.0+。
  • 如果 onBackStackChanged() 没有覆盖,请确保您的活动实现了 FragmentManager.OnBackStackChangedListener 接口。
【解决方案2】:

我明白了。只需在托管活动中覆盖 onOptionsItemSelected 并弹出后台堆栈,例如

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home: {
            FragmentManager fm = getSupportFragmentManager();
            if (fm.getBackStackEntryCount() > 0) {
                fm.popBackStack();
                return true;
            }
            break;
        }
    }
    return super.onOptionsItemSelected(item);
}

onBackStackChanged() 中致电getActionBar().setDisplayHomeAsUpEnabled(boolean);getActionBar().setHomeButtonEnabled(boolean);,如下面的答案所述。

【讨论】:

  • 您还必须调用 getActivity().getActionBar().setDisplayHomeAsUpEnabled(false);弹出返回堆栈后删除向上按钮
  • 这不是正确的方法。它一直启用向上按钮。
  • 您应该将该代码放在switch 语句中,大小写为android.R.id.home
【解决方案3】:

如果您有一个父 Activity 并希望此向上按钮用作后退按钮,您可以使用以下代码:

将此添加到主活动类中的 onCreate

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
            if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
                getSupportActionBar().setHomeButtonEnabled(true);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            } else {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                getSupportActionBar().setHomeButtonEnabled(false);
            }
        }

    });

然后像这样添加 onOptionsItemSelected:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
     ....
 }

我通常一直使用这个,看起来很合法

【讨论】:

  • 我尝试在我的Activity 中准确地使用此代码,希望它会返回到它开始的片段。当我转到Activity 时,后退按钮出现在我的应用程序图标附近,但我单击该图标并没有任何反应(它应该返回到片段)。知道为什么吗?谢谢。
  • @Daniel 你的代码是合法的。它确实有效。你可能只想用 try catch 选项来解决它,以防万一......你知道要防止任何不可预见的异常和应用程序崩溃
  • @NoniA。这只会返回到之前的片段(例如,片段 B -> 片段 A),如果您在新活动中膨胀 1 个片段,则不会返回到之前的活动。
【解决方案4】:

你可以像返回按钮一样使用向上按钮返回;

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            super.onBackPressed();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

【讨论】:

    【解决方案5】:

    我使用了Roger Garzon Nieto'ssohailaziz's 的组合答案。我的应用程序有一个 MainActivity,以及加载到其中的片段 A、B、C。我的“家”片段(A)实现了 OnBackStackChangedListener,并检查了 backStack 的大小;如果小于 1,则隐藏 UP 按钮。片段 B 和 C 总是加载返回按钮(在我的设计中,B 从 A 启动,C 从 B 启动)。 MainActivity 本身只是在 UP 按钮点击时弹出 backstack,并具有显示/隐藏按钮的方法,片段调用:

    MainActivity:

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case android.R.id.home:
                getSupportFragmentManager().popBackStack();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
    
    public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
    public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }
    

    fragmentA(实现 FragmentManager.OnBackStackChangedListener):

    public void onCreate(Bundle savedinstanceSate) {
        // listen to backstack changes
        getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);
    
        // other fragment init stuff
        ...
    }
    
    public void onBackStackChanged() {
        // enable Up button only  if there are entries on the backstack
        if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
            ((MainActivity)getActivity()).hideUpButton();
        }
    }
    

    片段B,片段C:

    public void onCreate(Bundle savedinstanceSate) {
        // show the UP button
        ((MainActivity)getActivity()).showUpButton();
    
        // other fragment init stuff
        ...
    }
    

    【讨论】:

      【解决方案6】:

      我知道这个问题很老,但可能有人(比如我)也需要它。

      如果您的 Activity 扩展了 AppCompatActivity,您可以使用更简单(两步)的解决方案:

      1 - 每当您添加非主页片段时,只需在提交片段事务后立即显示向上按钮。像这样:

          // ... add a fragment
          // Commit the transaction
          transaction.commit();
      
          getSupportActionBar().setDisplayHomeAsUpEnabled(true);
      

      2 - 然后当按下 UP 按钮时,将其隐藏。

      @Override
      public boolean onSupportNavigateUp() {
          getSupportActionBar().setDisplayHomeAsUpEnabled(false);        
          return true;
      }
      

      就是这样。

      【讨论】:

        【解决方案7】:

        这对我有用。覆盖 onSupportNavigateUp 和 onBackPressed,例如(Kotlin 中的代码);

        override fun onBackPressed() {
            val count = supportFragmentManager.backStackEntryCount
            if (count == 0) {
                super.onBackPressed()
            } else {
                supportFragmentManager.popBackStack()
            }
        }
        
        override fun onSupportNavigateUp(): Boolean {
            super.onSupportNavigateUp()
            onBackPressed()
            return true
        }
        

        现在在片段中,如果你显示向上箭头

        activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
        

        点击它会带你回到上一个活动。

        【讨论】:

          【解决方案8】:

          科特林:

          class MyActivity : AppCompatActivity() {
          
              override fun onCreate(savedInstanceState: Bundle?) {
                  ...
                  supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
                  setupHomeAsUp()
              }
          
              private fun setupHomeAsUp() {
                  val shouldShow = 0 < supportFragmentManager.backStackEntryCount
                  supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
              }
          
              override fun onSupportNavigateUp(): Boolean = 
                  supportFragmentManager.popBackStack().run { true }
          
              ...
          }
          

          【讨论】:

            【解决方案9】:

            这是一个非常好的和可靠的解决方案:http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

            这家伙制作了一个抽象片段来处理 backPress 行为并使用策略模式在活动片段之间切换。

            对于你们中的一些人来说,抽象类可能有一点缺点......

            简而言之,链接中的解决方案如下所示:

            // Abstract Fragment handling the back presses
            
            public abstract class BackHandledFragment extends Fragment {
                protected BackHandlerInterface backHandlerInterface;
                public abstract String getTagText();
                public abstract boolean onBackPressed();
            
                @Override
                public void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    if(!(getActivity()  instanceof BackHandlerInterface)) {
                        throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
                    } else {
                        backHandlerInterface = (BackHandlerInterface) getActivity();
                    }
                }
            
                @Override
                public void onStart() {
                    super.onStart();
            
                    // Mark this fragment as the selected Fragment.
                    backHandlerInterface.setSelectedFragment(this);
                }
            
                public interface BackHandlerInterface {
                    public void setSelectedFragment(BackHandledFragment backHandledFragment);
                }
            }   
            

            以及在活动中的用法:

            // BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS 
            // IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment
            
            public class TheActivity extends FragmentActivity implements BackHandlerInterface {
                private BackHandledFragment selectedFragment;
            
                @Override
                public void onBackPressed() {
                    if(selectedFragment == null || !selectedFragment.onBackPressed()) {
                        // Selected fragment did not consume the back press event.
                        super.onBackPressed();
                    }
                }
            
                @Override
                public void setSelectedFragment(BackHandledFragment selectedFragment) {
                    this.selectedFragment = selectedFragment;
                }
            }
            

            【讨论】:

            • 虽然此链接可能会回答问题,但最好包含答案here 的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
            • 顺便说一句:如果您发现重复项,请将它们标记为此类。谢谢。
            • 在 onStart 里面 setSelectedFragment 重要吗?
            【解决方案10】:

            如果你想回到你之前的活动,如果这个活动有一个空的片段堆栈:

            如果您有一个 MainActivity 并且您正在导航到例如,这可能会很有用。具有嵌套的prefernceScreens 的 SettingsActivity。 NavigateUp 会弹出片段,直到您可以完成 SettingsActivity 以返回 parentActivity/root。

            /**
             * On actionbar up-button popping fragments from stack until it is empty.
             * @return true if fragment popped or returned to parent activity successfully.
             */
            @Override
            public boolean onSupportNavigateUp() {
                //Pop back stack if the up button is pressed.
                boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
                if (canGoBack) {
                    getSupportFragmentManager().popBackStack();
                } else {
                    finish();
                    return super.onSupportNavigateUp();
                }
                return true;
            }
            

            注意:片段活动中的setDisplayHomeAsUpEnabled(true);onCreate()

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-05-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多