ChatGPT解决这个技术问题 Extra ChatGPT

使用片段时在 Android 导航抽屉图像和向上插入符号之间切换

在使用 Navigation Drawer 时,Android 开发人员建议在 ActionBar 中“只有那些在 Navigation Drawer 中表示的屏幕实际上应该具有 Navigation Drawer 图像”,并且“所有其他屏幕都具有传统的 up carat”。

有关详细信息,请参见此处:http://youtu.be/F5COhlbpIbY

我正在使用一个活动来控制多个级别的片段,并且可以让导航抽屉图像在所有级别上显示和运行。

创建较低级别的片段时,我可以调用 ActionBarDrawerToggle setDrawerIndicatorEnabled(false) 来隐藏导航抽屉图像并显示向上插入符号

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, 
lowFrag, "lowerFrag").addToBackStack(null).commit();

我遇到的问题是,当我导航回顶层片段时,Up 克拉仍然显示,而不是原始的 Navigation Drawer 图像。关于如何“刷新”顶级片段上的 ActionBar 以重新显示 Navigation Drawer 图像的任何建议?

解决方案

汤姆的建议对我有用。这是我所做的:

主要活动

此活动控制应用程序中的所有片段。

在准备新片段以替换其他片段时,我将 DrawerToggle setDrawerIndicatorEnabled(false) 设置为:

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,   
lowFrag).addToBackStack(null).commit();

接下来,在覆盖 onBackPressed 时,我通过将 DrawerToggle 设置为 setDrawerIndicatorEnabled(true) 来恢复上述内容,如下所示:

@Override
public void onBackPressed() {
    super.onBackPressed();
    // turn on the Navigation Drawer image; 
    // this is called in the LowerLevelFragments
    setDrawerIndicatorEnabled(true)
}

在 LowerLevelFragments

在我这样修改 onCreateonOptionsItemSelected 的片段中:

onCreate 中添加了 setHasOptionsMenu(true) 以启用配置选项菜单。还要设置 setDisplayHomeAsUpEnabled(true) 以启用操作栏中的 <

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // needed to indicate that the fragment would 
    // like to add items to the Options Menu        
    setHasOptionsMenu(true);    
    // update the actionbar to show the up carat/affordance 
    getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}

然后在 onOptionsItemSelected 中,每当按下 < 时,它都会从 Activity 调用 onBackPressed() 以在层次结构中向上移动一级并显示导航抽屉图像:

@Override
public boolean onOptionsItemSelected(MenuItem item) {   
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            getActivity().onBackPressed();
            return true;
        … 
    }
同样在您的 onBackPressed() 方法中,您可以使用 getFragmentManager().getBackStackEntryCount() 方法检查后堆栈中有多少条目,并仅在结果为 0 时启用抽屉指示器。在这种情况下,无需在每个 LowerLevelFragments 中启用 homeAsUpIndicator。
这非常有用!您应该移动帖子的“解决方案”部分并使其成为实际的“答案”。你会得到更多的点赞,毕竟这是一个答案
为什么要在此处替换片段:.replace(R.id.frag_layout。如果这是另一个层次结构级别,我希望您将其.add 放到后台堆栈中。
兄弟,你如何引用片段内的theDrawerToggle.setDrawerIndicatorEnabled(false);?我认为它是在 Main Activity 类文件中声明的。我找不到引用这个的方法。有什么提示吗?
使用工具栏时,我不得不切换显示选项以同时不使用主页。否则,ToolbarWidgetWrapper 中的 setDisplayOptions() 方法(内部 android.support.v7.internal.widget 包)在第二次输入同一片段时不会重新创建图标。当其他人也偶然发现这个问题时,将其留在这里。

r
riwnodennyk

1-2-3 很简单。

如果你想达到:

1) Drawer Indicator - 当 Back Stack 中没有碎片或 Drawer 已打开时

2) 箭头 - 当一些 Fragments 在 Back Stack 中时

private FragmentManager.OnBackStackChangedListener
        mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        syncActionBarArrowState();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    mDrawerToggle = new ActionBarDrawerToggle(
            this,             
            mDrawerLayout,  
            R.drawable.ic_navigation_drawer, 
            0, 
            0  
    ) {

        public void onDrawerClosed(View view) {
            syncActionBarArrowState();
        }

        public void onDrawerOpened(View drawerView) {
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    };

    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}

@Override
protected void onDestroy() {
    getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
    super.onDestroy();
}

private void syncActionBarArrowState() {
    int backStackEntryCount = 
        getSupportFragmentManager().getBackStackEntryCount();
    mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

3) 两个指标根据它们的形状起作用

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (mDrawerToggle.isDrawerIndicatorEnabled() && 
        mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    } else if (item.getItemId() == android.R.id.home && 
               getSupportFragmentManager().popBackStackImmediate()) {
        return true;
    } else {
        return super.onOptionsItemSelected(item);
    }
}

PS 请参阅 Creating a Navigation Drawer on Android Developers,了解有关 3 线指示器行为的其他提示。


我最终得到了和你类似的东西。我认为这个解决方案(使用 BackStackChangedListener 在显示的内容之间切换)是最优雅的。但是我有两个更改:1)我不调用/更改 onDrawerClosed/onDrawerOpened 调用中的抽屉指示器 - 这不是必需的,2)我让 Fragments 自己通过制作一个 AbstractFragment 来处理向上导航,它们都继承了哪个实现 onOptionsItemSelected(..) 并且总是调用 setHasOptionsMenu(true);
您还应该在箭头模式下锁定导航抽屉的滑动手势: mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);并在抽屉指示灯模式下再次启用它
我最终在我的 NavigationDrawer 片段中完成了所有这些工作。顺便说一句,它就像一个魅力,谢谢。
setActionBarArrowDependingOnFragmentsBackStack() ...多么长的名字:P
当我从片段 A 转到 B 并将 A 添加到后台堆栈时,这不会为我显示向上按钮。 :(
J
JJD

您已经写过,要实现较低级别的片段,您将替换现有的片段,而不是在新活动中实现较低级别的片段。

我认为您必须手动实现后退功能:当用户按下后您有弹出堆栈的代码(例如在 Activity::onBackPressed 覆盖中)。因此,无论您在哪里执行此操作,都可以反转 setDrawerIndicatorEnabled


谢谢汤姆,那行得通!我已经用我使用的解决方案更新了原始帖子。
如何在较低级别的片段中引用 theDrawerToggle?它已在主要活动中定义,我无法理解!
您可以从片段中获取活动。因此,只需在您的主要活动中添加一个吸气剂即可访问抽屉切换。
Y
Yuriy Sych

我用了下一个东西:

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if(getSupportFragmentManager().getBackStackEntryCount() > 0){
                    mDrawerToggle.setDrawerIndicatorEnabled(false);
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                }
                else {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    mDrawerToggle.setDrawerIndicatorEnabled(true);
                }
            }
        });

非常感谢,这是唯一真正有效的。添加后,drawerToggle.setToolbarNavigationClickListener( 单击箭头时会调用此侦听器
谢谢@Yuriy,这帮助我解决了我的问题
B
Burrich

如果您的向上操作栏按钮不起作用,请不要忘记添加侦听器:

// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
});

我在使用主页按钮实现抽屉导航时遇到了一些麻烦,除了操作按钮之外,一切正常。


d
dzeikei

尝试根据 DrawerToggle 的状态处理 MainActivity 中的 Home 项选择。这样您就不必为每个片段添加相同的代码。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            onBackPressed();
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

+1整洁的解决方案。为了使您的答案完全可用,您应该在 backstack 上添加一个检查。当它为空时,自动将抽屉指示器设置为 true。
@HpTerm 我在 onBackPressed() 中处理后台堆栈,因为我希望两者具有相同的行为。
J
JJD

跟进

@dzeikei 给出的解决方案很简洁,但可以扩展,当使用片段时,当后台堆栈为空时自动处理设置抽屉指示器。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            // Use getSupportFragmentManager() to support older devices
            FragmentManager fragmentManager = getFragmentManager();
            fragmentManager.popBackStack();
            // Make sure transactions are finished before reading backstack count
            fragmentManager.executePendingTransactions();
            if (fragmentManager.getBackStackEntryCount() < 1){
                mDrawerToggle.setDrawerIndicatorEnabled(true);  
            }
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

编辑

对于@JJD 的问题。

片段在活动中保存/管理。上述代码在该活动中编写一次,但仅处理 onOptionsItemSelected 的向上插入符号。

在我的一个应用程序中,我还需要处理按下后退按钮时向上插入符号的行为。这可以通过覆盖 onBackPressed 来处理。

@Override
public void onBackPressed() {
    // Use getSupportFragmentManager() to support older devices
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.executePendingTransactions();
    if (fragmentManager.getBackStackEntryCount() < 1){
        super.onBackPressed();
    } else {
        fragmentManager.executePendingTransactions();
        fragmentManager.popBackStack();
        fragmentManager.executePendingTransactions();
        if (fragmentManager.getBackStackEntryCount() < 1){
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    }
};

请注意 onOptionsItemSelectedonBackPressed 之间的代码重复,这可以通过创建一个方法并在两个地方调用该方法来避免。

另请注意,我再添加两次 executePendingTransactions ,在我的情况下这是必需的,否则我有时会出现向上插入符号的奇怪行为。


这是我需要添加以实现 Up caret 行为的完整代码,还是您指的是其他帖子之一?请澄清这一点。
感谢您的编辑。实际上,我在 NavigationDrawerFragment 类中维护 mDrawerToggle。为了让它工作,我还需要切换主页按钮/指示器的状态 - 请参阅:NavigationDrawerFragment#toggleDrawerIndicator。此外,我不确定您是否需要在 onOptionsItemSelected 中进行初始检查:我取消了它的注释。 - Simplified example
Revised implementation:您对 onOptionsItemSelected 中的初始检查是正确的。这确保了导航抽屉仍然在顶级层次结构中打开。但是,我将代码移到了 NavigationDrawerFragment#onOptionsItemSelected。这有助于我不将 mDrawerToggle 暴露给 MainActivity
@JJD我记得在层次结构的每个级别都为向上插入符号工作时有点挣扎。只要它也对你有用,那很好。当然,正如您所说,您可以将代码移到别处而不是公开它。
B
Bill Mote

我为托管活动创建了一个界面来更新汉堡菜单的视图状态。对于顶级片段,我将切换设置为 true,对于我想要显示向上 < 的片段箭头 我将切换设置为 false

public class SomeFragment extends Fragment {

    public interface OnFragmentInteractionListener {
        public void showDrawerToggle(boolean showDrawerToggle);
    }

    private OnFragmentInteractionListener mListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            this.mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        mListener.showDrawerToggle(false);
    }
}

然后在我的活动中......

public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {

    private ActionBarDrawerToggle mDrawerToggle;

    public void showDrawerToggle(boolean showDrawerIndicator) {
        mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
    }

}

C
Community

answer 工作正常,但出现了一点问题。 getSupportActionBar().setDisplayHomeAsUpEnabled(false) 没有被显式调用,即使后台堆栈中没有项目,它也会导致抽屉图标被隐藏,因此更改 setActionBarArrowDependingOnFragmentsBackStack() 方法对我有用。

private void setActionBarArrowDependingOnFragmentsBackStack() {
        int backStackEntryCount = getSupportFragmentManager()
                .getBackStackEntryCount();
        // If there are no items in the back stack
        if (backStackEntryCount == 0) {
            // Please make sure that UP CARAT is Hidden otherwise Drawer icon
            // wont display
            getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            // Display the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        } else {
            // Show the Up carat
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            // Hide the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(false);
        }

    }

在我的情况下,严格的解决方案只使用 actionBarDrawerToggle.setDrawerIndicatorEnabled(getSupportFragmentManager().getBackStackEntryCount() < 0);
k
kml_ckr

逻辑很清楚。如果片段返回堆栈已清除,则显示返回按钮。如果片段堆栈不清晰,则显示材质汉堡包动画。

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            syncActionBarArrowState();
        }
    }
);


private void syncActionBarArrowState() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

//add these in Your NavigationDrawer fragment class

public void setDrawerIndicatorEnabled(boolean flag){
    ActionBar actionBar = getActionBar();
    if (!flag) {
        mDrawerToggle.setDrawerIndicatorEnabled(false);
        actionBar.setDisplayHomeAsUpEnabled(true);
        mDrawerToggle.setHomeAsUpIndicator(getColoredArrow());
    } else {
        mDrawerToggle.setDrawerIndicatorEnabled(true);
    }
    mDrawerToggle.syncState();
    getActivity().supportInvalidateOptionsMenu();
}

//download back button from this(https://www.google.com/design/icons/) website and add to your project

private Drawable getColoredArrow() {
    Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp);
    Drawable wrapped = DrawableCompat.wrap(arrowDrawable);

    if (arrowDrawable != null && wrapped != null) {
        // This should avoid tinting all the arrows
        arrowDrawable.mutate();
        DrawableCompat.setTint(wrapped, Color.GRAY);
    }
    return wrapped;
}

E
EngineSense

如果您查看 GMAIL 应用程序并来这里搜索 carret/affordance 图标..

我会要求你这样做,以上答案都不清楚。我能够修改接受的答案。

NavigationDrawer --> Listview 包含子片段。

子片段将像这样列出

firstFragment == 位置 0 ---> 这将有子片段 --> 片段

第二个片段

第三个片段等等....

在 firstFragment 中,您还有其他片段。

在 DrawerActivity 上调用它

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager().getBackStackEntryCount() > 0) {
                mDrawerToggle.setDrawerIndicatorEnabled(false);
            } else {
                mDrawerToggle.setDrawerIndicatorEnabled(true);
            }
        }
    });

并在片段中

    setHasOptionsMenu(true);    

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            activity.onBackPressed();
            return true;
    }
    return false;
}

在 OnBackPressed Drawer 活动方法上,将抽屉切换设置为 true 以再次启用导航列表图标。

谢谢,小猫


o
oskarko

你可以看看这个小例子! https://github.com/oskarko/NavDrawerExample


C
Community

IMO,在 riwnodennyk 或 Tom 的解决方案中使用 onNavigateUp()(如 here 所示)更清洁,似乎效果更好。只需将 onOptionsItemSelected 代码替换为:

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}