¿Cómo agregar un separador de sección para el cajón de navegación en Android?

90

Tengo un cajón de navegación como esta imagen. Quiero agregar un separador de sección (como la línea que separa a Neptune). Parece simple, pero no encuentro nada en la web que sea útil para mi caso.

Aquí está mi MainActivity:

public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    private ActionBarDrawerToggle mDrawerToggle;

    private CharSequence mDrawerTitle;
    private CharSequence mTitle;
    private String[] mPlanetTitles;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTitle = mDrawerTitle = getTitle();
        mPlanetTitles = getResources().getStringArray(R.array.planets_array);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        // set a custom shadow that overlays the main content when the drawer opens
        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
        // set up the drawer's list view with items and click listener
        mDrawerList.setAdapter(new ArrayAdapter<String>(this,
                R.layout.drawer_list_item, mPlanetTitles));
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        // enable ActionBar app icon to behave as action to toggle nav drawer
        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);

        // ActionBarDrawerToggle ties together the the proper interactions
        // between the sliding drawer and the action bar app icon
        mDrawerToggle = new ActionBarDrawerToggle(
                this,                  /* host Activity */
                mDrawerLayout,         /* DrawerLayout object */
                R.drawable.ic_drawer,  /* nav drawer image to replace 'Up' caret */
                R.string.drawer_open,  /* "open drawer" description for accessibility */
                R.string.drawer_close  /* "close drawer" description for accessibility */
                ) {
            public void onDrawerClosed(View view) {
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            public void onDrawerOpened(View drawerView) {
                getActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }
        };
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        if (savedInstanceState == null) {
            selectItem(0);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    /* Called whenever we call invalidateOptionsMenu() */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // If the nav drawer is open, hide action items related to the content view
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
         // The action bar home/up action should open or close the drawer.
         // ActionBarDrawerToggle will take care of this.
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        // Handle action buttons
        switch(item.getItemId()) {
        case R.id.action_websearch:
            // create intent to perform web search for this planet
            Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
            intent.putExtra(SearchManager.QUERY, getActionBar().getTitle());
            // catch event that there's no activity to handle intent
            if (intent.resolveActivity(getPackageManager()) != null) {
                startActivity(intent);
            } else {
                Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /* The click listner for ListView in the navigation drawer */
    private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
        }
    }

    private void selectItem(int position) {
        // update the main content by replacing fragments
        Fragment fragment = new PlanetFragment();
        Bundle args = new Bundle();
        args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
        fragment.setArguments(args);

        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

        // update selected item and title, then close the drawer
        mDrawerList.setItemChecked(position, true);
        setTitle(mPlanetTitles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

    @Override
    public void setTitle(CharSequence title) {
        mTitle = title;
        getActionBar().setTitle(mTitle);
    }

    /**
     * When using the ActionBarDrawerToggle, you must call it during
     * onPostCreate() and onConfigurationChanged()...
     */

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Pass any configuration change to the drawer toggls
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    /**
     * Fragment that appears in the "content_frame", shows a planet
     */
    public static class PlanetFragment extends Fragment {
        public static final String ARG_PLANET_NUMBER = "planet_number";

        public PlanetFragment() {
            // Empty constructor required for fragment subclasses
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_planet, container, false);
            int i = getArguments().getInt(ARG_PLANET_NUMBER);
            String planet = getResources().getStringArray(R.array.planets_array)[i];

            int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()),
                            "drawable", getActivity().getPackageName());
            ((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId);
            getActivity().setTitle(planet);
            return rootView;
        }
    }
}

activity_main.xml:

<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
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ListView
        android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#111"/>
</android.support.v4.widget.DrawerLayout>

drawer_list_item.xml:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceListItemSmall"
    android:gravity="center_vertical"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:textColor="#fff"
    android:background="?android:attr/activatedBackgroundIndicator"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"/>

Quiero que esto sea simple, pero no puedo encontrar nada bueno en la web. ¿Hay alguna manera de aislar Neptune y crear una sección para él? ¿Alguien tiene alguna sugerencia? Gracias.

ingrese la descripción de la imagen aquí

Redson
fuente
no podrá hacerlo así con un adaptador estándar. deberá crear un adaptador personalizado y tener un inicio de sesión simple en getView para ocultar o mostrar el separador
Tomer Shemesh
@TomerShemesh ¿Tiene un ejemplo de cómo puedo hacer esto?
Redson
hay bibliotecas en github que te ayudarán a lograr lo mismo. MaterialDrawer de mikepenz es bueno y se mantiene bien. github.com/mikepenz/MaterialDrawer . También puede seguir la aplicación de programación de google io en github. El separador es sólo una vista con 1px altura y anchura match_parent
Raghunandan
@Raghunandan Sé que el separador es un Viewpero necesito de alguna manera encontrar una manera de llamarlo solo para Neptune
Redson
@Alias ​​sigue la aplicación de programación de google io o usa la biblioteca que acabo de publicar el enlace. Ambos deberían funcionar
Raghunandan

Respuestas:

325

Asegúrese de definir cada grupo con una ID única , el separador no aparecerá sin la ID.

Por ejemplo, este es mi drawer_menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group
        android:id="@+id/menu_top"
        android:checkableBehavior="single">
        <item
            android:checked="true"
            android:id="@+id/drawer_item_timeline"
            android:icon="@drawable/ic_timer_grey600_24dp"
            android:title="@string/drawer_timeline"/>
        <item
            android:id="@+id/drawer_item_reports"
            android:icon="@drawable/ic_timetable_grey600_24dp"
            android:title="@string/drawer_reports"/>
    </group>

    <group
        android:id="@+id/menu_bottom"
        android:checkableBehavior="none">

        <item
            android:id="@+id/drawer_item_settings"
            android:icon="@drawable/ic_settings_black_24dp"
            android:title="@string/drawer_settings" >
        </item>
    </group>
</menu>

Cajón de muestra

Gabriel agrega a continuación en los comentarios que si el grupo no tiene una identificación, el separador no aparecerá.

espinchi
fuente
142
Es importante decir que si el grupo no tiene una identificación, ¡el separador no aparecerá!
Gabriel Gómez
1
Se ve muy bien hasta ahora, pero ¿cómo solucionarlo android:checkableBehavior? Si selecciono el último elemento del grupo2 y hago clic en un elemento del grupo1 hacia adelante, ambos elementos se resaltan. @espinchi, ¿podría agregar una solución para esto a su respuesta?
Tomblarom
3
¿Cómo aplicar este menú a NavigationDrawer?
Yar
@ GabrielGómez esto es tan feo. ¿Por qué se requiere identificación para mostrar un separador, es tan malo ...
USER25
Me alegro de haber encontrado esto, no tengo que usar ese trabajo de pirateo que tenía antes.
lasec0203
7

Para separar los elementos del menú por una línea divisoria, solo agrupe los elementos con una identificación única como el siguiente ejemplo:

activity_main_drawer.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <item
        android:id="@+id/nav_apps_and_games"
        android:icon="@drawable/ic_apps_black_24dp"
        android:title="@string/my_apps_and_games" />

    <item
        android:id="@+id/nav_bookmarked_apps"
        android:icon="@drawable/ic_add_bookmark_black_24dp"
        android:title="@string/bookmarked_apps" />

    <item
        android:id="@+id/nav_manage_downloads"
        android:icon="@drawable/ic_downloading_file_black_24dp"
        android:title="@string/manage_downloads" />

    <!-- SET A UNIQUE ID TO THE BELOW GROUP -->
    <group android:id="@+id/group1">

        <item
            android:id="@+id/nav_settings"
            android:icon="@drawable/ic_settings_black_24dp"
            android:title="@string/settings" />

        <item
            android:id="@+id/nav_sign_up"
            android:icon="@drawable/ic_card_membership_black_24dp"
            android:title="@string/sign_up_login" />

    </group>

</menu>

Resultado visual:

ingrese la descripción de la imagen aquí

aminografía
fuente
2

Mi método hacky es similar al de Mostrapotski.

En mi Diseño para mi adaptador personalizado, agrego un separador horizontal al principio de cada elemento y configuro su visibilidad para que desaparezca.

Para los elementos que marcan el comienzo de un nuevo grupo, configuro su visibilidad como visible para que el separador aparezca encima.

Leye Eltee Taiwo
fuente
1

Tienes dos opciones

  1. Sus elementos se pueden separar (una lista en la parte superior y vistas clásicas en la parte inferior). Luego, en lugar de la vista de lista en su diseño principal (android: id = "@ + id / left_drawer"), puede tener un LinearLayout bastante complejo que incluya esos 3 elementos (lista, separador y vistas inferiores)
  2. Sus elementos deben ser exactamente como en su ejemplo, luego necesita el separador en la lista, puede usar algo de lógica en su adaptador para dibujar una vista en la parte superior del elemento de la lista donde necesita el separador. (lo que significa que su elemento de lista ya no será una vista de texto única, sino un LinearLayout con un separador desaparecido (y visible a veces, de acuerdo con la lógica de su adaptador).

Para ayudarlo con un código de muestra, ¿puede publicar todos los elementos que necesita en su menú? Necesitamos saber exactamente qué será estático y qué será desplazable.

Editar : si quieres que funcione con el ejemplo, deshazte de la línea

mDrawerList.setAdapter(new ArrayAdapter<String>(this, ...)

Debe proporcionar un adaptador casero como este: https://github.com/codepath/android_guides/wiki/Using-an-ArrayAdapter-with-ListView

Como dije en 2, en su adaptador, tendrá lógica y, por lo tanto, puede decir en el método getView ()

if(myPlanet.isNeptune()) 
    holder.mSepatator.setVisibility(View.VISIBLE);
else 
    holder.mSepatator.setVisibility(View.GONE);
Mostrapotski
fuente
Este es el fragmento principal de código que estoy usando. El resto son todos archivos xml como string.xml ...
Redson
Sí, pero esta es la muestra de Android para el diseño del cajón, ¿qué estás dispuesto a tener?
Mostrapotski