在新的 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
的布局模板,您可以在 <Path\to\Program_Files>\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
中的 Fragment
s 的方式。
【讨论】:
这不是我要更改的布局。我想添加自己的片段并从导航抽屉导航到它们。【参考方案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”)
VueJs - Vuetify - v-navigation-drawer 与智能手机的迷你变体