web-dev-qa-db-fra.com

Basculer entre des fragments avec onNavigationItemSelected dans le nouveau modèle d'activité Navigation Drawer (Android Studio 1.4 et versions ultérieures)

IntelliJ a apporté des modifications au modèle de tiroir de navigation Activity dans Android Studio avec moins de lignes de code dans la classe Activity. La nouvelle classe Activity ressemble à ceci:

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

L'un des changements les plus remarquables est la méthode:

onNavigationItemSelected(MenuItem item)

La définition de cette méthode par l'ancien modèle de tiroir de navigation était la suivante:

onNavigationItemSelected(int position, long itemId)

Vous pouvez modifier cet ancien modèle en supprimant la classe interne PlaceHolderFragment, en créant vos propres fragments et présentations et en procédant comme suit:

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

}

Mais cela ne fonctionne pas avec le nouveau modèle (du moins pas à ma connaissance). J'ai essayé:

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

mais la mise en page pour la mise en page home apparaît également dans la mise en page news. Cela se produit probablement à cause de la ligne:

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

Les fragments sont supposés être remplacés dans un FrameLayout et l'ancienne présentation du tiroir de navigation ressemblait à ceci:

<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"/>

Mais le nouveau ressemble à ceci:

 <?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>

Bref, comment modifier le nouveau modèle pour pouvoir basculer entre les fragments?

40

Donc, sur la base de la réponse de @L.L., j'ai pu résoudre ce problème.

Tout d’abord, ajoutez votre FrameLayout à votre fichier content_main.xml:

<FrameLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" Android:id="@+id/content_frame"/>

Dans votre MainActivity (ou ce que vous avez nommé l'activité avec le tiroir de navigation), définissez une méthode nommée 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);

}

Je permute entre 3 fragments personnalisés; NewsFragment, EventsFragment et GalleryFragment.

dans mon menu activity_main_drawer J'ai changé le contenu en ceci:

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

Pour revenir à la classe d'activité, dans votre méthode onNavigationItemSelected procédez comme suit:

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

Enfin, la dernière instruction de votre méthode onCreate:

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

C’est parce que je veux que la première vue de mon utilisateur soit News.Changez-la comme vous le souhaitez.

Gérer les événements de presse:

Dans l'état actuel des choses, si vous appuyez sur le bouton de retour de l'un des fragments, l'application se ferme. Je souhaite que mon application retourne au fragment de nouvelles (mon fragment de maison) lorsque l'utilisateur appuie sur le bouton de retour. Alors j'ai fait ça:

Déclaré une variable booléenne:

private boolean viewIsAtHome;

puis dans la méthode displayView() j'ai fait ceci:

 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;

Enfin, supprimez votre ancienne méthode onBackPressed et créez-en une nouvelle comme celle-ci: extérieur la méthode 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
    }
}

Travaille pour moi.

81
ojonugwa ochalifu

Vous ne devriez pas changer le DrawerLayout, il vous suffit d'ajouter un cadre dans le fichier "content_main.xml".

Suivez les étapes ci-dessous:

  1. ouvrez le fichier "content_main.xml" situé dans le dossier "layout".

  2. utilisez le code ci-dessous:

    <?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>
    
  3. allez à la méthode 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;
    }
    
12
L.L.

une autre façon est:

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

Et ensuite, vous devrez implémenter tous les fragments de votre activité. Dans mon cas c'est

MainActivity.Java

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

Et pour gérer l’exception levée dans fragment Java

    "must implement OnFragmentInteractionListener"

vous ajoutez simplement la méthode ci-dessous

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

Et voilà! Tous ensemble

Merci à ce tutoriel pour trouver ceci

4
Gaurav Karia

Je ne sais pas si je pourrai vous aider, mais si vous souhaitez modifier les modèles de présentation pour NavigationDrawerActivity, vous pouvez le trouver dans <Path\to\Program_Files>\Android\Android Studio\plugins\Android\lib\templates\activities\NavigationDrawerActivity\root\res\layout

Je pense qu’il sera préférable d’avoir une disposition semblable à celle-ci -

<!-- 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>

Et cela fonctionnera avec la façon dont vous avez remplacé le Fragments dans R.id.container plus tôt.

0
jaibatrik

J'ai utilisé un modèle créé automatiquement par Android Studio 1.4 également et j'ai rencontré les mêmes difficultés que vous.

D'abord, j'ai changé 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>

Un DrawerLayout qui contient deux parties:

  1. Écran principal (LinearLayout)
  2. le tiroir (NavigationView)

L’écran principal contient l’ActionBar et le FrameLayout (R.id.content) sera remplacé par un fragment. Le tiroir contient l'en-tête et le menu.

Donc, nous substituons maintenant onNavigationItemSelected et remplaçons R.id.content avec un fragment.

Voici mon code 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;
}

La page principale est aussi un fragment. Et j’ai configuré la page principale dans la méthode onCreate bien que je ne sois pas sûr qu’il existe un meilleur moyen de configurer la page principale.

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

PS Voici la toolbar.xml, la barre d'action

<?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>
0
Jean Y.C. Yang