quarta-feira, 19 de junho de 2013

SlidingPaneLayout: um menu lateral estilo Facebook

Olá povo,

Uma das coisas boas de mexer a muito tempo com uma tecnologia, é que as pessoas sempre perguntam a você, mas obviamente, nem sempre você sabe a resposta. Meu amigo Rodrigo Jardim (a.k.a. Praieiro), me perguntou se eu conhecia o SlidingPaneLayout, e eu nunca tinha visto o dito cujo. Mas ele permite implementarmos um padrão de interface gráfica amplamente utilizada nas aplicações Android: menus laterais. Abaixo, coloquei um screenshot de duas "pequenas" aplicações que usam essa abordagem.


Como podemos notar, o Facebook e o GMail são dois bons exemplos de grandes aplicações que usam essa abordagem. Nesse post vou mostrar como dar os primeiros passos para construir um menu desse tipo. Vamos começar pelo arquivo de layout da aplicação, mostrado abaixo.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SlidingPaneLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/sliding_pane_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

  <!-- Menu Lateral -->
  <ListView
    android:id="@+id/left_pane"
    android:layout_width="280dp"
    android:layout_height="match_parent"
    android:layout_gravity="left" />
    
  <!-- Conteúdo da tela -->
  <RelativeLayout
    android:id="@+id/rightPane"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff333333" >

    <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentLeft="true"
      android:layout_alignParentTop="true"
      android:text="MENU" 
      android:onClick="abrirMenu"/>

    <ImageView
      android:id="@+id/imageView1"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:src="@drawable/ic_launcher" />

  </RelativeLayout>

</android.support.v4.widget.SlidingPaneLayout>
No SlidingPanelLayout temos apenas duas partes, a primeira (uma ListView) será o menu da aplicação. Enquanto que a segunda, será o conteúdo da tela em si. Abaixo temos o código da Activity.
public class MainActivity extends FragmentActivity 
  implements OnItemClickListener, PanelSlideListener {

  private SlidingPaneLayout mSlidingLayout;
  private ListView mList;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Colocando a Activity em tela cheia (opcional)
    requestWindowFeature(Window.FEATURE_NO_TITLE);

    setContentView(R.layout.activity_main);
  

    mSlidingLayout = (SlidingPaneLayout) 
      findViewById(R.id.sliding_pane_layout);
    mSlidingLayout.setPanelSlideListener(this);
  
    String[] opcoes = new String[] { 
      "Opção 1", "Opção 2", "Opção 3",
      "Opção 4", "Opção 5" };

    mList = (ListView) findViewById(R.id.left_pane);
    mList.setAdapter(new ArrayAdapter<String>(
      this, 
      android.R.layout.simple_list_item_1, 
      opcoes));
    mList.setOnItemClickListener(this);
  }

  // Evento de clique do botão
  public void abrirMenu(View v){
    // Se estive aberto, feche. Senão abra.
    if (mSlidingLayout.isOpen()){
      mSlidingLayout.closePane();
    } else {
      mSlidingLayout.openPane();
    }
  }

  @Override
  public void onItemClick(AdapterView<?> adapterView, 
    View view, int position, long id) {
    // TODO Tratar opções de Menu (ListView) aqui!
  }

  @Override
  public void onPanelClosed(View arg0) {
    // TODO Ao fechar o painel
  }

  @Override
  public void onPanelOpened(View arg0) {
    // TODO Ao abrir o painel
  }

  @Override
  public void onPanelSlide(View arg0, float arg1) {
    // TODO Enquanto o painel desliza
  }
}
O código acima é bem simples e está comentado.
O mais legal desse componente é que ao deslizar o dedo sobre a tela da esquerda para direita, o menu exibido (da direita para esquerda, ele fecha). Ou seja, o botão acima é opcional, mas indicado, como vemos nas aplicações em geral (como a do Facebook e Gmail) para facilitar a visualização do usuário.
Abaixo podemos ver nossa aplicações em execução com o menu lateral aberto.

É isso pessoal! Temos um menu no padrão de aplicações profissionais. Vale salientar que esse componente funciona em todas as versões do Android.

EDITADO em 29/11/2013

Várias pessoas me perguntaram (por email.... deixem os comentários aqui povo!) o que fazer com o clique do item da lista. O mais legal aqui é usar Fragments e sua stack. Ou seja, criar uma pilha de Fragments como o Android faz com as Activities. Ou seja, ao invés de irmos chamando Activities, chamamos Fragments e vamos empilhando-os no layout da direita.
Vamos ao exemplo... Crie a classe MeuFragment conforme abaixo:
public class MeuFragment extends Fragment {

  public static MeuFragment newInstance(String s){
    Bundle args = new Bundle();
    args.putString("texto", s);
  
    MeuFragment f = new MeuFragment();
    f.setArguments(args);
    return f;
  }
 
  @Override
  public View onCreateView(LayoutInflater inflater, 
    ViewGroup container, Bundle savedInstanceState) {

    View layout = inflater.inflate(
      R.layout.meu_fragment, container, false);

    TextView txt = (TextView)
      layout.findViewById(R.id.textView1);

    txt.setText(getArguments().getString("texto"));
    return layout;
  }
}
O arquivo de layout do Fragment acima, só tem um RelativeLayout com um TextView centralizado (nada de mais, vocês conseguem :)
Agora, no clique de cada um dos itens, instanciamos esse fragment passando o texto da opção clicada.
@Override
public void onItemClick(AdapterView<?> adapterView, 
  View view, int position, long id) {

  String opcao = (String)
    mList.getAdapter().getItem(position);
  
  MeuFragment f = MeuFragment.newInstance(opcao);
  
  FragmentManager fm = getSupportFragmentManager();
  // Opcional: isso removerá o fragment anterior 
  // da pilha.
  fm.popBackStack(); 
  
  fm.beginTransaction()
    .replace(R.id.rightPane, f, "frag1")
    .addToBackStack(null)
    .commit();  
}
O método addToBackStack vai adicionar o Fragment a uma pilha, dessa forma, ao clicar no botão back do aparelho, o fragment será removido automaticamente e anterior será exibido.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

Fonte: http://androidtrainningcenter.blogspot.com.br/2013/06/slidingpanelayout-android-making.html