A nova onda é “Material Design”. Um novo formato de criar aplicações para celular, sem “fru frus”, uma tela mais simples, com menos informação.
Bom, como alguns sabem eu tenho uma app publicada na play (algumas na verdade) e estou migrando-a para o novo padrão. Não completamente pois minha app atende a alguns requisitos que não me permitiria subir completamente a versão.
Mas deixando de enrolar, vamos ver o que é esse cara, como implementar e como trabalhar com fragmentos.
DrawerLayout é o menu lateral que expande ao clicar no ícone da sua aplicação ou simplesmente quando você arrasta a tela da esquerda para a direita. Bom, sem delongas, vamos lá.
Primeira coisa a entender, o DrawerLayout somente aceita dois componentes em sua activity. Um frame layout e uma lista.
Essa lista será o teu menu e o frame será onde seus fragmentos serão exibidos. A diferença básica é que aqui vamos trabalhar com fragmentos de tela e não com activities.
Entendido que o drawerlayout só poderá ter dois objetos vamos lá.
Primeira coisa a fazer. Vamos criar um projeto do zero e vamos chamar de DrawerLayoutProject. Eu trabalho com o eclipse então minhas dicas serão voltadas para a forma como o eclipse dispõe o projeto ok? Adequem de acordo com a necessidade de vc’s.
Criado o projeto Android, teremos nossa primeira activity, MainActivity e nossa primeira tela, activity_main. Vamos alterar para que ela fique com a seguinte aparência.
<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="220dp" android:layout_height="match_parent" android:layout_gravity="start" android:background="@color/white" android:choiceMode="singleChoice" android:divider="@android:color/darker_gray" android:dividerHeight="0.1dp" android:listSelector="@drawable/navdrawer_listselector"/> </android.support.v4.widget.DrawerLayout>
Para não dar erro de compilação teremos que criar o arquivo navdrawer_listselector.xml dentro da pasta drawable, e para nosso exemplo, iremos criar um arquivo chamado colors.xml dentro da pasta values.
O arquivo colors.xml terá a seguinte aparência:
<resources> <color name="black_overlay">#66000000</color> <color name="white">#FFFFFFFF</color> <color name="black">#FF000000</color> <color name="red">#FF4444</color> <color name="green">#0E8F31</color> <color name="dark_blue">#000066</color> <color name="blue_light">#405B8A</color> <drawable name="semitransparent_white">#77ffffff</drawable> <color name="purple_light">#a276eb</color> <color name="purple_middle">#8658ce</color> <color name="purple_dark">#6a3ab2</color> </resources>
E o nosso arquivo navdrawer_listselector.xml terá a seguinte aparência:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="@android:integer/config_mediumAnimTime"> <item android:drawable="@color/blue_light" android:state_pressed="true"/> <item android:drawable="@color/blue_light" android:state_selected="true"/> <item android:drawable="@color/blue_light" android:state_activated="true"/> </selector>
Pronto, nossa estrutura básica esta montada. Agora precisamos montar nosso menu. Para isso nossa activity vai ter que ter alguns métodos que irão tratar nosso menu ok? A primeira coisa a se fazer é criar uma classe que irá tratar nosso menu.
public class NavMenuSection implements NavDrawerItem { public static final int SECTION_TYPE = 0; private int id; private String label; private NavMenuSection() { } public static NavMenuSection create( int id, String label ) { NavMenuSection section = new NavMenuSection(); section.setLabel(label); return section; } @Override public int getType() { return SECTION_TYPE; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } @Override public boolean isEnabled() { return false; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public boolean updateActionBarTitle() { return false; } }
Notem que ela implementa uma interface. Esta interface é responsável por exibir as informações de label, tipo, etc, em nosso menu. Abaixo temos nossa interface:
public interface NavDrawerItem { public int getId(); public String getLabel(); public int getType(); public boolean isEnabled(); public boolean updateActionBarTitle(); }
Além da interface teremos as seguintes classes que irão servir de apoio. Não irei me ater as funcionalidades dela.
public class NavMenuItem implements NavDrawerItem { public static final int ITEM_TYPE = 1 ; private int id ; private String label ; private int icon ; private boolean updateActionBarTitle ; private NavMenuItem() { } public static NavMenuItem create( int id, String label, String icon, boolean updateActionBarTitle, Context context ) { NavMenuItem item = new NavMenuItem(); item.setId(id); item.setLabel(label); item.setIcon(context.getResources().getIdentifier(icon, "drawable", context.getPackageName())); item.setUpdateActionBarTitle(updateActionBarTitle); return item; } @Override public int getType() { return ITEM_TYPE; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public int getIcon() { return icon; } public void setIcon(int icon) { this.icon = icon; } @Override public boolean isEnabled() { return true; } @Override public boolean updateActionBarTitle() { return this.updateActionBarTitle; } public void setUpdateActionBarTitle(boolean updateActionBarTitle) { this.updateActionBarTitle = updateActionBarTitle; } }
<pre>public class NavDrawerAdapter extends ArrayAdapter<NavDrawerItem> { private LayoutInflater inflater; public NavDrawerAdapter(Context context, int textViewResourceId, NavDrawerItem[] objects ) { super(context, textViewResourceId, objects); this.inflater = LayoutInflater.from(context); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = null ; NavDrawerItem menuItem = this.getItem(position); if ( menuItem.getType() == NavMenuItem.ITEM_TYPE ) { view = getItemView(convertView, parent, menuItem ); } else { view = getSectionView(convertView, parent, menuItem); } return view ; } public View getItemView( View convertView, ViewGroup parentView, NavDrawerItem navDrawerItem ) { NavMenuItem menuItem = (NavMenuItem) navDrawerItem ; NavMenuItemHolder navMenuItemHolder = null; if (convertView == null) { convertView = inflater.inflate( R.layout.navdrawer_item, parentView, false); TextView labelView = (TextView) convertView .findViewById( R.id.navmenuitem_label ); ImageView iconView = (ImageView) convertView .findViewById( R.id.navmenuitem_icon ); navMenuItemHolder = new NavMenuItemHolder(); navMenuItemHolder.labelView = labelView ; navMenuItemHolder.iconView = iconView ; convertView.setTag(navMenuItemHolder); } if ( navMenuItemHolder == null ) { navMenuItemHolder = (NavMenuItemHolder) convertView.getTag(); } navMenuItemHolder.labelView.setText(menuItem.getLabel()); navMenuItemHolder.iconView.setImageResource(menuItem.getIcon()); return convertView ; } public View getSectionView(View convertView, ViewGroup parentView, NavDrawerItem navDrawerItem) { NavMenuSection menuSection = (NavMenuSection) navDrawerItem ; NavMenuSectionHolder navMenuItemHolder = null; if (convertView == null) { convertView = inflater.inflate( R.layout.navdrawer_section, parentView, false); TextView labelView = (TextView) convertView .findViewById( R.id.navmenusection_label ); navMenuItemHolder = new NavMenuSectionHolder(); navMenuItemHolder.labelView = labelView ; convertView.setTag(navMenuItemHolder); } if ( navMenuItemHolder == null ) { navMenuItemHolder = (NavMenuSectionHolder) convertView.getTag(); } navMenuItemHolder.labelView.setText(menuSection.getLabel()); return convertView ; } @Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { return this.getItem(position).getType(); } @Override public boolean isEnabled(int position) { return getItem(position).isEnabled(); } private static class NavMenuItemHolder { private TextView labelView; private ImageView iconView; } private class NavMenuSectionHolder { private TextView labelView; } }
public class NavDrawerActivityConfiguration { private int mainLayout; private int drawerShadow; private int drawerLayoutId; private int leftDrawerId; private int[] actionMenuItemsToHideWhenDrawerOpen; private NavDrawerItem[] navItems; private int drawerOpenDesc; private int drawerCloseDesc; private BaseAdapter baseAdapter; public int getMainLayout() { return mainLayout; } public void setMainLayout(int mainLayout) { this.mainLayout = mainLayout; } public int getDrawerShadow() { return drawerShadow; } public void setDrawerShadow(int drawerShadow) { this.drawerShadow = drawerShadow; } public int getDrawerLayoutId() { return drawerLayoutId; } public void setDrawerLayoutId(int drawerLayoutId) { this.drawerLayoutId = drawerLayoutId; } public int getLeftDrawerId() { return leftDrawerId; } public void setLeftDrawerId(int leftDrawerId) { this.leftDrawerId = leftDrawerId; } public int[] getActionMenuItemsToHideWhenDrawerOpen() { return actionMenuItemsToHideWhenDrawerOpen; } public void setActionMenuItemsToHideWhenDrawerOpen( int[] actionMenuItemsToHideWhenDrawerOpen) { this.actionMenuItemsToHideWhenDrawerOpen = actionMenuItemsToHideWhenDrawerOpen; } public NavDrawerItem[] getNavItems() { return navItems; } public void setNavItems(NavDrawerItem[] navItems) { this.navItems = navItems; } public int getDrawerOpenDesc() { return drawerOpenDesc; } public void setDrawerOpenDesc(int drawerOpenDesc) { this.drawerOpenDesc = drawerOpenDesc; } public int getDrawerCloseDesc() { return drawerCloseDesc; } public void setDrawerCloseDesc(int drawerCloseDesc) { this.drawerCloseDesc = drawerCloseDesc; } public BaseAdapter getBaseAdapter() { return baseAdapter; } public void setBaseAdapter(BaseAdapter baseAdapter) { this.baseAdapter = baseAdapter; } }
e por último teremos nosso drawer que será um fragmento de nosso sistema.
public abstract class AbstractNavDrawerActivity extends FragmentActivity { private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; public ListView mDrawerList; private CharSequence mDrawerTitle; private CharSequence mTitle; private NavDrawerActivityConfiguration navConf ; protected abstract NavDrawerActivityConfiguration getNavDrawerConfiguration(); protected abstract void onNavItemSelected( int id ); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); navConf = getNavDrawerConfiguration(); setContentView(navConf.getMainLayout()); mTitle = mDrawerTitle = getTitle(); mDrawerLayout = (DrawerLayout) findViewById(navConf.getDrawerLayoutId()); mDrawerList = (ListView) findViewById(navConf.getLeftDrawerId()); mDrawerList.setAdapter(navConf.getBaseAdapter()); mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); this.initDrawerShadow(); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); mDrawerToggle = new ActionBarDrawerToggle( this, mDrawerLayout, getDrawerIcon(), navConf.getDrawerOpenDesc(), navConf.getDrawerCloseDesc() ) { public void onDrawerClosed(View view) { getActionBar().setTitle(mTitle); invalidateOptionsMenu(); } public void onDrawerOpened(View drawerView) { getActionBar().setTitle(mDrawerTitle); invalidateOptionsMenu(); } }; mDrawerLayout.setDrawerListener(mDrawerToggle); } protected void initDrawerShadow() { mDrawerLayout.setDrawerShadow(navConf.getDrawerShadow(), GravityCompat.START); } protected int getDrawerIcon() { return R.drawable.ic_drawer; } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); } @Override public boolean onPrepareOptionsMenu(Menu menu) { if ( navConf.getActionMenuItemsToHideWhenDrawerOpen() != null ) { boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); for( int iItem : navConf.getActionMenuItemsToHideWhenDrawerOpen()) { menu.findItem(iItem).setVisible(!drawerOpen); } } return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } else { return false; } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ( keyCode == KeyEvent.KEYCODE_MENU ) { if ( this.mDrawerLayout.isDrawerOpen(this.mDrawerList)) { this.mDrawerLayout.closeDrawer(this.mDrawerList); } else { this.mDrawerLayout.openDrawer(this.mDrawerList); } return true; } return super.onKeyDown(keyCode, event); } protected DrawerLayout getDrawerLayout() { return mDrawerLayout; } protected ActionBarDrawerToggle getDrawerToggle() { return mDrawerToggle; } private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectItem(position); } } public void selectItem(int position) { NavDrawerItem selectedItem = navConf.getNavItems()[position]; this.onNavItemSelected(selectedItem.getId()); mDrawerList.setItemChecked(position, true); if ( selectedItem.updateActionBarTitle()) { setTitle(selectedItem.getLabel()); } if ( this.mDrawerLayout.isDrawerOpen(this.mDrawerList)) { mDrawerLayout.closeDrawer(mDrawerList); } } @Override public void setTitle(CharSequence title) { mTitle = title; getActionBar().setTitle(mTitle); } }
Esse fragmento é o nosso menu, literalmente, falando. Vejam que ele possui uma Lista ‘mDrawerList’ e possui a configuração o Titulo que será exibido e o Titulo do item.
Feito isso, Sua activity principal irá estender de AbstractNavDrawerActivity e você deverá sobrescrever os métodos:
protected NavDrawerActivityConfiguration getNavDrawerConfiguration() protected void onNavItemSelected(int id)
O primeiro método irá criar sua configuração de navegação, que irá ser re-utilizada em todos os fragmentos. O segundo irá validar qual item foi clicado.
Vamos aos métodos:
@Override protected NavDrawerActivityConfiguration getNavDrawerConfiguration() { NavDrawerItem[] menu = new NavDrawerItem[] { NavMenuItem.create(1,this.getString(R.string.start), "start", false, this), NavMenuSection.create( 100, this.getString(R.string.menu_section_1)), NavMenuItem.create(101,this.getString(R.string.menu_item_1), "", false, this), }; NavDrawerActivityConfiguration navDrawerActivityConfiguration = new NavDrawerActivityConfiguration(); navDrawerActivityConfiguration.setMainLayout(R.layout.activity_main); navDrawerActivityConfiguration.setDrawerLayoutId(R.id.drawer_layout); navDrawerActivityConfiguration.setLeftDrawerId(R.id.left_drawer); navDrawerActivityConfiguration.setNavItems(menu); navDrawerActivityConfiguration.setDrawerShadow(R.drawable.drawer_shadow); navDrawerActivityConfiguration.setDrawerOpenDesc(R.string.drawer_open); navDrawerActivityConfiguration.setDrawerCloseDesc(R.string.drawer_close); navDrawerActivityConfiguration.setBaseAdapter( new NavDrawerAdapter(this, R.layout.navdrawer_item, menu )); return navDrawerActivityConfiguration; }
Percebam que o meu NavDrawerItem é um array e ele irá manter minhas Seções, no caso seriam as separações dos menus, e os meus itens, no caso as funcionalidades de cada seção. Percebam também que o adapter recebe o nosso menu criado e que nossa configuração informa qual o layout principal.
Agora vamos para o segundo o método, o que é chamado sempre que o menu é clicado:
@Override protected void onNavItemSelected(int id) { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); String toBackStack = "start"; Fragment fragment = null; switch ((int)id) { case 1: fragment = new StartFragment(); break; case 101: fragment = new MenuItemFragment(); break; } transaction.addToBackStack(toBackStack); transaction.replace(R.id.content_frame, fragment).commit(); }
Percebam que é navegado pelo itemID do objeto e de acordo com o item é criado um fragmento específico. Dessa forma a aplicação vai realizar a mudança de um fragmento para o outro.
Essa é apenas uma forma de se fazer isso, achei bastante simples e prático visto que não há necessidade de trazer bibliotecas de terceiros e o código é bem limpo. O que importa agora é colocar em prática e criar algo no padrão.
Abraços e até a próxima.
O projeto completo encontra-se na seção Arquivos.