Переключение между фрагментами с помощью onNavigationItemSelected в новом шаблоне активности навигационного ящика (Android Studio 1.4)

IntelliJ внесла изменения в шаблон шаблона навигационного ящика в Android Studio с меньшим количеством строк кода в классе 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)

Определение шаблона старого навигационного ящика этого метода:

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_width="match_parent"
android:layout_height="match_parent">

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

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


<ListView
    android:id="@+id/list_sliderMenu"
    android:layout_width="240dp"
    android:layout_height="match_parent"
    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_width="match_parent"
   android:layout_height="match_parent"
   android:fitsSystemWindows="true"
   tools:openDrawer="start">

<include
    layout="@layout/app_bar_base"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<android.support.design.widget.NavigationView
    android:id="@+id/nav_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    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_width="match_parent"
    android:layout_height="match_parent" 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
    }
}

Работает для меня.

Ответ 2

Не следует изменять DrawerLayout, вам нужно добавить фрейм в "content_main.xml".

Выполните следующие шаги:

  • откройте файл "content_main.xml", расположенный в папке "layout".

  • используйте следующий код:

    <?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_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:showIn="@layout/app_bar_main"
        tools:context=".MainActivity">    
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent" 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;
    }
    

Ответ 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
}

И вот ты! Все установленные

Благодаря этому учебнику ударить по этому вопросу

Ответ 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_width="match_parent"
    android:layout_height="match_parent"
    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_width="match_parent"
        android:layout_height="match_parent" />

    <!-- 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_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="<#if buildApi gte 17>start<#else>left</#if>" />

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

И это будет работать с тем, как вы раньше заменяли Fragment в R.id.container.

Ответ 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_width="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" tools:openDrawer="start">

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

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

A DrawerLayout, который содержит две части:

  • Основной экран (LinearLayout)
  • Ящик (NavigationView)

Главный экран содержит ActionBar, а FrameLayout (R.id.content) будет заменен фрагментом. Ящик содержит заголовок и меню.

Итак, теперь мы переопределяем 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();

PS. вот 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_width="match_parent"
android:layout_height="match_parent" android:fitsSystemWindows="true"
tools:context="jean.yang.MainActivity">
<android.support.design.widget.AppBarLayout android:layout_height="wrap_content"
    android:layout_width="match_parent" android:theme="@style/AppTheme.AppBarOverlay">
    <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
        android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>