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

时间:2013-06-23 05:42:04

标签: android android-fragments android-actionbar navigation-drawer

当使用导航抽屉时,Android开发人员建议在ActionBar中“只有那些在导航抽屉中表示的屏幕应该实际上具有导航抽屉图像”并且“所有其他屏幕都具有传统的向上克拉”。

详情请见此处: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();

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


解决方案

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

MainActivity

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

在准备新片段以替换其他片段时,我将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&lt; 时,它会调用活动中的onBackPressed()向上移动层次结构中的一个级别并显示导航抽屉图像:< / p>

@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;
        … 
    }

11 个答案:

答案 0 :(得分:83)

这很容易1-2-3。

如果你想实现:

1)抽屉指示器 - 当后堆栈中没有碎片或抽屉被打开时

2)箭头 - 当某些碎片位于后堆栈中时

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);
    }
}

P.S。有关3行指标行为的其他提示,请参阅Creating a Navigation Drawer on Android Developers

答案 1 :(得分:28)

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

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

答案 2 :(得分:12)

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

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

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

答案 3 :(得分:10)

尝试根据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);
    }
}

答案 4 :(得分:6)

关注

@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,在我的情况下需要这样做,否则我有时会出现上角点的奇怪行为。

答案 5 :(得分:2)

我为托管活动创建了一个界面来更新汉堡包菜单的视图状态。对于顶级片段,我将切换设置为true,并将片段设置为我要显示的&lt; arrow我将切换设置为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);
    }

}

答案 6 :(得分:2)

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);
        }

    }

答案 7 :(得分:1)

逻辑清晰。 如果片段后栈是清除的,则显示后退按钮。 如果片段堆栈不清楚,则显示材料汉堡包动画。

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;
}

答案 8 :(得分:1)

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

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

  • NavigationDrawer - &gt; Listview包含子片段。

  • 子片段将按此列出

  • firstFragment == position 0 ---&gt;这将有subfragments - &gt;片段

  • secondFragment
  • thirdFragment等等....

在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以再次启用导航列表图标。

谢谢, Pusp

答案 9 :(得分:1)

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

答案 10 :(得分:0)

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

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