在新的 Navigation Drawer Activity 模板中使用 onNavigationItemSelected 在 Fragments 之间切换(Android Studio 1.4 及更

Posted

技术标签:

【中文标题】在新的 Navigation Drawer Activity 模板中使用 onNavigationItemSelected 在 Fragments 之间切换(Android Studio 1.4 及更高版本)【英文标题】:Switch between Fragments with onNavigationItemSelected in new Navigation Drawer Activity template (Android Studio 1.4 onward) 【发布时间】:2016-01-01 21:14:38 【问题描述】:

IntelliJ 对 android Studio 中的 Navigation Drawer 模板 Activity 进行了更改,减少了 Activity 类中的代码行。新的 Activity 类如下所示:

public class MainActivity extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener 

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View view) 
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        
    );

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.setDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);


@Override
public void onBackPressed() 
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) 
        drawer.closeDrawer(GravityCompat.START);
     else 
        super.onBackPressed();
    



@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) 
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    if (id == R.id.nav_camara) 
        // Handle the camera action
     else if (id == R.id.nav_gallery) 

     else if (id == R.id.nav_slideshow) 

     else if (id == R.id.nav_manage) 

     else if (id == R.id.nav_share) 

     else if (id == R.id.nav_send) 

    

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;


这里最显着的变化之一是方法:

onNavigationItemSelected(MenuItem item)

旧的 Navigation Drawer 模板对此方法的定义是:

onNavigationItemSelected(int position, long itemId)

您可以通过删除内部 PlaceHolderFragment 类、创建自己的片段和布局并执行以下操作来修改旧模板:

Fragment fragment = null;
switch (position) 
    case 0:
        fragment = new FragmentA();
        break;
    case 1:
        fragment = new FragmentB();
        break;
    default:
        break;


if (fragment != null) 
    FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.beginTransaction()
            .replace(R.id.frame_container, fragment).commit();


但这不适用于新模板(至少据我所知不是)。我试过了:

 public boolean onNavigationItemSelected(MenuItem item) 
    // Handle navigation view item clicks here.
    int id = item.getItemId();
    Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), item.getTitle() + " clicked", Snackbar.LENGTH_SHORT);
    Fragment fragment = null;
    switch (id) 
        case R.id.nav_home:
            fragment = HomeFragment.getFragInstance();
            break;

        case R.id.nav_news:

            fragment = NewsFragment.getFragInstance();
            break;


        default:
            break;
    

    if (fragment != null) 
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.addToBackStack(null);
        transaction.replace(R.id.drawer_layout, fragment);
        transaction.commit();


        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);

    
    return true;

home 布局的布局也显示在news 布局中。 这可能是因为以下行:

transaction.replace(R.id.drawer_layout, fragment);

片段应该在FrameLayout 中被替换,旧的导航抽屉布局看起来像这样:

<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_
android:layout_>

<!-- Framelayout to display Fragments -->
<FrameLayout
    android:id="@+id/frame_container"
    android:layout_
    android:layout_ />

<!-- Listview to display slider menu -->


<ListView
    android:id="@+id/list_sliderMenu"
    android:layout_
    android:layout_
    android:layout_gravity="start"
    android:choiceMode="singleChoice"
    android:divider="@color/white"
    android:dividerHeight="1dp"
    android:listSelector="@drawable/list_selector"
    android:background="@color/list_background"/>

但新的看起来像这样:

 <?xml version="1.0" encoding="utf-8"?>
  <android.support.v4.widget.DrawerLayout      
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/drawer_layout"
   android:layout_
   android:layout_
   android:fitsSystemWindows="true"
   tools:openDrawer="start">

<include
    layout="@layout/app_bar_base"
    android:layout_
    android:layout_ />

<android.support.design.widget.NavigationView
    android:id="@+id/nav_view"
    android:layout_
    android:layout_
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:headerLayout="@layout/nav_header_base"
    app:menu="@menu/activity_base_drawer" />

    </android.support.v4.widget.DrawerLayout>

长话短说,如何修改新模板才能在片段之间切换?

【问题讨论】:

【参考方案1】:

所以,根据@L.L. 的回答,我能够解决这个问题。

首先,将您的 FrameLayout 添加到您的 content_main.xml 文件中:

<FrameLayout
    android:layout_
    android:layout_ android:id="@+id/content_frame"/>

在你的MainActivity(或任何你用导航抽屉命名的活动)中定义一个名为displayView的方法

 public void displayView(int viewId) 

    Fragment fragment = null;
    String title = getString(R.string.app_name);

    switch (viewId) 
        case R.id.nav_news:
            fragment = new NewsFragment();
            title  = "News";

            break;
        case R.id.nav_events:
            fragment = new EventsFragment();
            title = "Events";
            break;

    

    if (fragment != null) 
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.content_frame, fragment);
        ft.commit();
    

    // set the toolbar title
    if (getSupportActionBar() != null) 
        getSupportActionBar().setTitle(title);
    

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);


在 3 个自定义片段之间切换; NewsFragment、EventsFragment 和 GalleryFragment。

在我的菜单activity_main_drawer我已将内容更改为:

<menu xmlns:android="http://schemas.android.com/apk/res/android">

<group android:checkableBehavior="single">
    <item android:id="@+id/nav_news"  
        android:icon="@android:drawable/ic_menu_news"
        android:title="News" />
    <item android:id="@+id/nav_events" 
        android:icon="@android:drawable/ic_menu_events"
        android:title="Events" />
    <item android:id="@+id/nav_gallery" 
        android:icon="@android:drawable/ic_menu_gallery"
        android:title="Gallery" />
  </group>
</menu>

回到 Activity 类,在您的 onNavigationItemSelected 方法中 这样做:

@Override
public boolean onNavigationItemSelected(MenuItem item) 
    displayView(item.getItemId());
    return true;

最后,您的 onCreate 方法中的最后一条语句:

 @Override
protected void onCreate(Bundle savedInstanceState) 
    ....
    ....
    displayView(R.id.nav_news);

这是因为我希望我的用户看到的第一个视图是新闻。将其更改为您选择的任何内容。

处理后按事件:

就目前而言,如果您从任何片段按下后退按钮,应用程序将退出。我希望我的应用程序在用户按下后退按钮时返回新闻片段(我的主页片段)。所以我这样做了:

声明一个布尔变量:

private boolean viewIsAtHome;

然后在displayView() 方法中我这样做了:

 public void displayView(int viewId)
    Fragment fragment = null;
    String title = getString(R.string.app_name);

    switch (viewId) 
        case R.id.nav_news:
            fragment = new NewsFragment();
            title  = getString(R.string.news_title);
            viewIsAtHome = true;

            break;
        case R.id.nav_events:
            fragment = new EventsFragment();
            title = getString(R.string.events_title);
            viewIsAtHome = false;
            break;

        case R.id.nav_gallery:
            fragment = new GalleryFragment();
            title = getString(R.string.gallery_title);
            viewIsAtHome = false;
            break;

最后,删除旧的onBackPressed 方法并在onCreate() 方法外部创建一个类似这样的新方法:

@Override
public void onBackPressed() 
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) 
        drawer.closeDrawer(GravityCompat.START);
    
    if (!viewIsAtHome)  //if the current view is not the News fragment
        displayView(R.id.nav_news); //display the News fragment
     else 
        moveTaskToBack(true);  //If view is in News fragment, exit application
    

为我工作。

【讨论】:

为我工作。谢谢:) 但是如何从Fragment 中选择onNavigationItemSelected 项目?任何人都可以帮忙。 感谢您花时间清楚地解释您所做的事情。尽管我不需要帮助,但我还是对你的 cmets 投了赞成票。 我有一个问题/错误。当我在片段之间切换时,我的 HomeFragment 的内容总是会显示出来,这意味着它也会显示在其他片段上。 也许您正在为片段加载相同的布局?【参考方案2】:

DrawerLayout不要改,只需要在“content_main.xml”中添加一个框架即可。

按照以下步骤操作:

    打开位于“layout”文件夹中的“content_main.xml”文件。

    使用下面的代码:

    <?xml version="1.0" encoding="utf-8"?> 
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_
        android:layout_
        app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/app_bar_main"
        tools:context=".MainActivity">    
        <FrameLayout
            android:layout_
            android:layout_ android:id="@+id/mainFrame">
        </FrameLayout>   
    </RelativeLayout>
    

    转到onNavigationItemSelected 方法:

    public boolean onNavigationItemSelected(MenuItem item) 
      int id = item.getItemId();
      Fragment fragment;
    
    if (id == R.id.nav_camara) 
        fragment = new YourFragment();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.mainFrame, fragment);
        ft.commit();
    
    else if (id == R.id.nav_gallery) 
    
    
    else if (id == R.id.nav_slideshow) 
    
    
    else if (id == R.id.nav_manage) 
    
     else if (id == R.id.nav_share) 
    
     else if (id == R.id.nav_send) 
    
    
    
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
    
    

【讨论】:

谢谢。将 FrameLayout 放在 content_main 中是我需要的主要更改。【参考方案3】:

另一种方法是:

public boolean onNavigationItemSelected(MenuItem item) 
    // Handle navigation view item clicks here.
    Fragment fragment;
    int id = item.getItemId();

    if (id == R.id.nav_camera) 
        // Handle the camera action
        fragment = new BlankFragment();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.container_new, fragment);
        ft.commit();
     else if (id == R.id.nav_gallery) 
        fragment = new HorizontalView();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.container_new,fragment);
        ft.commit();
     else if (id == R.id.nav_slideshow) 
        fragment = new FragmentVerticleView();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.container_new,fragment);
        ft.commit();

     else if (id == R.id.nav_manage) 

     else if (id == R.id.nav_share) 

     else if (id == R.id.nav_send) 

    

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;

然后您需要将所有片段实现到您的活动中。 在我的情况下是

MainActivity.java

    public class MainActivity extends AppCompatActivity
    implements BlankFragment.OnFragmentInteractionListener, HorizontalView.OnFragmentInteractionListener,FragmentVerticleView.OnFragmentInteractionListener,OnNavigationItemSelectedListener
      
    //coding stuff
    

以及处理片段java文件中抛出的异常

    "must implement OnFragmentInteractionListener"

你只需添加下面的方法

      public void    onFragmentInteraction(Uri uri)
    //We can keep this empty

你去吧!一切就绪

Thanks to this Tutorial to strike this

【讨论】:

【参考方案4】:

不确定我是否能提供帮助,但如果您想修改 NavigationDrawer Activity 的布局模板,您可以在 &lt;Path\to\Program_Files&gt;\Android\Android Studio\plugins\android\lib\templates\activities\NavigationDrawerActivity\root\res\layout 中找到它

我相信,最好有类似下面的布局 -

<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_
    android:layout_
    tools:context="$relativePackage.$activityClass">

    <!-- As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions. -->
    <FrameLayout
        android:id="@+id/container"
        android:layout_
        android:layout_ />

    <!-- android:layout_gravity="start" tells DrawerLayout to treat
         this as a sliding drawer on the left side for left-to-right
         languages and on the right side for right-to-left languages.
         If you're not building against API 17 or higher, use
         android:layout_gravity="left" instead. -->
    <!-- The drawer is given a fixed width in dp and extends the full height of
         the container. -->
    <NavigationView android:id="@+id/navigation_drawer"
        android:layout_
        android:layout_
        android:layout_gravity="<#if buildApi gte 17>start<#else>left</#if>" />

</android.support.v4.widget.DrawerLayout>

这将适用于您之前替换 R.id.container 中的 Fragments 的方式。

【讨论】:

这不是我要更改的布局。我想添加自己的片段并从导航抽屉导航到它们。【参考方案5】:

我也使用了Android Studio 1.4自动创建的模板,遇到了和你一样的困难。

首先,我换了activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout"
android:layout_ android:layout_
android:fitsSystemWindows="true" tools:openDrawer="start">

<!--Main-->
<LinearLayout
    android:layout_
    android:layout_
    android:orientation="vertical">
    <!-- The ActionBar -->
    <include
        layout="@layout/toolbar"
        android:layout_
        android:layout_ />
    <FrameLayout
        android:id="@+id/content"
        android:layout_
        android:layout_>
    </FrameLayout>
</LinearLayout>

<!--Drawer-->
<android.support.design.widget.NavigationView
    android:id="@+id/nav_view"
    android:layout_
    android:layout_
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:headerLayout="@layout/nav_header"
    app:menu="@menu/drawer" /></android.support.v4.widget.DrawerLayout>

一个包含两个部分的DrawerLayout

    主屏幕 (LinearLayout) 抽屉 (NavigationView)

主屏幕包含 ActionBar,FrameLayout (R.id.content) 将被片段替换。 Drawer 包含标题和菜单。

所以,我们现在覆盖 onNavigationItemSelected 并将R.id.content 替换为片段。

这是我的 MainActivity 代码:

    @Override
public boolean onNavigationItemSelected(MenuItem item) 
    Fragment fragment;
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    if (id == R.id.nav_A) 
        fragment = new AFragment();
        ft.replace(R.id.content, fragment).commit();        
     else if (id == R.id.nav_B)
        fragment = new BFragment();
        ft.replace(R.id.content, fragment).commit();
     else if (id == R.id.nav_settings) 
        Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
        startActivity(intent);
    

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;

主页也是一个片段。 我在onCreate方法中设置了主页,虽然我不确定是否有更好的方法来设置主页。

//MainPageFragment
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.content, new MainPageFragment()).commit();

附言。这里是toolbar.xml,ActionBar

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_
android:layout_ android:fitsSystemWindows="true"
tools:context="jean.yang.MainActivity">
<android.support.design.widget.AppBarLayout android:layout_
    android:layout_ android:theme="@style/AppTheme.AppBarOverlay">
    <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
        android:layout_ android:layout_
        android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

【讨论】:

【参考方案6】:

尝试删除以前的片段或删除所有历史记录而不是添加新片段。

//Here we are clearing back stack fragment entries
int backStackEntry = getSupportFragmentManager().getBackStackEntryCount();
if (backStackEntry > 0) 
    for (int i = 0; i < backStackEntry; i++) 
        getSupportFragmentManager().popBackStackImmediate();
    


//Here we are removing all the fragment that are shown here
if (getSupportFragmentManager().getFragments() != null && getSupportFragmentManager().getFragments().size() > 0) 
    for (int i = 0; i < getSupportFragmentManager().getFragments().size(); i++) 
        Fragment mFragment = getSupportFragmentManager().getFragments().get(i);
        if (mFragment != null) 
            getSupportFragmentManager().beginTransaction().remove(mFragment).commit();
        
    

更多帮助from here

【讨论】:

以上是关于在新的 Navigation Drawer Activity 模板中使用 onNavigationItemSelected 在 Fragments 之间切换(Android Studio 1.4 及更的主要内容,如果未能解决你的问题,请参考以下文章

向@react-navigation/drawer 组件发送道具

有条件的 Drawer.Navigator 的 openByDefault 属性无法正常工作(“@react-navigation/drawer”:“^5.12.5”)

android navigation drawer怎么用?

VueJs - Vuetify - v-navigation-drawer 与智能手机的迷你变体

v-navigation-drawer 在窗口调整大小时掉入失控循环

怎么自定义有navigation drawer效果的Android控件