Mostrando postagens com marcador Fragment. Mostrar todas as postagens
Mostrando postagens com marcador Fragment. Mostrar todas as postagens

sexta-feira, 11 de outubro de 2013

Adeus AlertDialog! Bem-vindo DialogFragment

Olá povo,

Até a versão 2.3 do Android (API Level 10), a forma padrão de exibir mensagens para os usuários era através da classes AlertDialog, mas partir da versão 3 do Android (Honeycomb API Level 11) uma nova abordagem foi adotada, a utilização da classe DialogFragment.
Apesar de ter sido lançada na versão 3, a API de compatibilidade do Android nos permite usar esse recurso em versões anteriores. Umas das vantagens dessa abordagem é o maior controle sobre o ciclo de vida do Dialog, uma vez que ele nada mais é do que um Fragment.
Apenas para relembrar, até o 2.3 fazíamos isso:

int x = 0;
 
@Override
protected Dialog onCreateDialog(int id) {
  AlertDialog dialog = new AlertDialog.Builder(this)
    .setTitle("Título")
    .setMessage("Deseja exibir "+ x++)
    .setPositiveButton("Sim", null) // Listener aqui
    .setNegativeButton("Não", null) // e aqui
    .create();
  return dialog;
}
 
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
  ((AlertDialog)dialog).setMessage("Mensagem "+ x++);
  super.onPrepareDialog(id, dialog);
}
 
public void abrirAlert(View v){
  showDialog(0);
}
A abordagem acima era a recomendada pois, se abríssimos um dialog e girássemos a tela, o mesmo era perdido. No método onCreateDialog, é criado o dialog (dããã), mas se precisássemos alterar o texto mudando apenas um detalhe (no exemplo acima, o valor de x) deveríamos usar o método onPrepareDialog conforme acima. Todos os dialogs da Acvity deviam ser criados no onCreateDialog e cada um tem o seu ID que é recebido por parâmetro. Para exibir o dito-cujo, era só chamar showDialog(ID).

A classe DialogFragment envolve (wraps) um AlertDialog, ou pode ter qualquer view. Podemos dizer que seria como um "Fragment Modal". No exemplo abaixo, temos um exemplo de um DialogFragment que recebe um título, uma mensagem e os (de 1 a 3) botões.

import android.app.AlertDialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;

public class SimpleDialog extends DialogFragment 
  implements OnClickListener {
 
  private static final 
    String EXTRA_ID      = "id";
  private static final 
    String EXTRA_MESSAGE = "message";
  private static final 
    String EXTRA_TITLE   = "title";
  private static final 
    String EXTRA_BUTTONS = "buttons";
  private static final 
    String DIALOG_TAG    = "SimpleDialog";
 
  private int dialogId;
 
  public static SimpleDialog newDialog(int id, 
    String title, String message, int[] buttonTexts){
    // Usando o Bundle para salvar o estado
    Bundle bundle  = new Bundle();
    bundle.putInt(EXTRA_ID, id);
    bundle.putString(EXTRA_TITLE, title); 
    bundle.putString(EXTRA_MESSAGE, message);
    bundle.putIntArray(EXTRA_BUTTONS, buttonTexts);
  
    SimpleDialog dialog = new SimpleDialog();
    dialog.setArguments(bundle);
    return dialog; 
  }
 
  @Override
  public Dialog onCreateDialog(
    Bundle savedInstanceState) {

    String title = getArguments()
      .getString(EXTRA_TITLE);
    String message = getArguments()
      .getString(EXTRA_MESSAGE);
    int[] buttons = getArguments()
      .getIntArray(EXTRA_BUTTONS);
     
    AlertDialog.Builder alertDialogBuilder = 
      new AlertDialog.Builder(getActivity());
    alertDialogBuilder.setTitle(title);
    alertDialogBuilder.setMessage(message);
        
    switch (buttons.length) {
      case 3:
        alertDialogBuilder.setNeutralButton(
          buttons[2], this);

      case 2:
        alertDialogBuilder.setNegativeButton(
          buttons[1], this);
   
      case 1:
        alertDialogBuilder.setPositiveButton(
          buttons[0], this);
    }    
    return alertDialogBuilder.create();
  }
    
  @Override
  public void onClick(
    DialogInterface dialog, int which) {
    // Sua Activity deve implementar essa interface
    ((FragmentDialogInterface)getActivity())
      .onClick(dialogId, which);
  }

  public void openDialog(
    FragmentManager supportFragmentManager) {

    if (supportFragmentManager.findFragmentByTag(
      DIALOG_TAG) == null){

      show(supportFragmentManager, DIALOG_TAG);
    }  
  }
  // Interface que erá chamada ao clicar no bot"ao
  public interface FragmentDialogInterface {
    void onClick(int id, int which);
  }
}
Podemos notar no código acima que estamos herdando da classe DialogFragment. Criei um método estático para inicializar o Dialog, lembrando que essa é uma boa prática uma vez que sempre devemos ter o construtor padrão e o Bundle armazenará o estado do Fragment. No método onCreateDialog, inicializamos o AlertDialog com os dados passados no "método construtor".
Nossa Activity deve implementar essa interface, pois não podemos manter a referência dela, uma vez que ela pode ter sido destruída ao girar o aparelho. Para abrir o dialog, verificamos se ele já foi adicionado ao FragmentManager, caso contrário, exibimos.
Pra exibir o dialog, basta fazer conforme abaixo:
public void abrirSimpleDialog(View v) {
  SimpleDialog dialog = SimpleDialog.newDialog(
    0,              // Id do dialog
    "Alerta",       // título
    "Mensagem",     // mensagem
    new int[]{      // texto dos botões
      android.R.string.ok, 
      android.R.string.cancel });
    dialog.openDialog(getSupportFragmentManager());
  }

  @Override
  public void onClick(int id, int which) {
    Toast.makeText(MainActivity.this, 
      "Botão clicado "+ which, Toast.LENGTH_SHORT)
        .show();
  }
Agora se quisermos um Dialog customizado, para por exemplo, receber input do usuário, podemos criar um arquivo de layout e associá-lo ao DialogFragment.
 
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/edtName"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="center"
  android:orientation="vertical"
  android:padding="16dp" >

  <TextView
    android:id="@+id/lbl_your_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Your name" />

  <EditText
    android:id="@+id/txt_your_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:imeOptions="actionDone"
    android:inputType="text" />

</LinearLayout>
O Layout é simple e contém apenas um TextView e um EditText. Vamos ver como ficará o DialogFragment que usaria esse layout.
public class EditNameDialog 
  extends DialogFragment 
  implements OnEditorActionListener {

  private static final 
    String DIALOG_TAG = "editDialog";
  private static final 
    String EXTRA_INPUT_TEXT = "message";
  private static final 
    String EXTRA_TITLE = "inputText";

  private EditText mEditText;

  // Sua Activity deve implementar essa Interface
  public interface EditNameDialogListener {
    void onFinishEditDialog(String inputText);
  }

  public static EditNameDialog newInstance(
    String title, String inputText) {

    Bundle bundle = new Bundle();
    bundle.putString(EXTRA_TITLE, title);
    bundle.putString(EXTRA_INPUT_TEXT, inputText);

    EditNameDialog dialog = new EditNameDialog();
    dialog.setArguments(bundle);
    return dialog;
  }

  @Override
  public View onCreateView(LayoutInflater inflater, 
    ViewGroup container, Bundle savedInstanceState){

    String title = getArguments()
      .getString(EXTRA_TITLE);
    String inputText = getArguments()
      .getString(EXTRA_INPUT_TEXT);

    View view = inflater.inflate(
      R.layout.layout_dialog, container);

    TextView txtView = (TextView)
      view.findViewById(R.id.lbl_your_name);
    txtView.setText(inputText);
    // Exibe o teclado virtual ao exibir o Dialog
    getDialog().getWindow().setSoftInputMode(
      WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);

    getDialog().setTitle(title);

    mEditText = (EditText)
      view.findViewById(R.id.txt_your_name);
    mEditText.requestFocus();
    // Listener para quando clicarmos 
    // em 'Done' no teclado
    mEditText.setOnEditorActionListener(this);

    return view;
  }

  @Override
  public boolean onEditorAction(TextView v, 
    int actionId, KeyEvent event) {
    // Se clicou em 'Done'
    if (EditorInfo.IME_ACTION_DONE == actionId) {
      // Notifique a Activity
      EditNameDialogListener activity = 
        (EditNameDialogListener) getActivity();
      activity.onFinishEditDialog(
        mEditText.getText().toString());
      // Feche o dialog
      dismiss();
      return true;
    }
    return false;
  }

  public void openDialog(FragmentManager fm) {
    if (fm.findFragmentByTag(DIALOG_TAG) == null) {
      show(fm, DIALOG_TAG);
    }
  }
}
Para exibir e tratar esse dialog usamos o código abaixo:
public void abrirEditDialog(View v){
  EditNameDialog editNameDialog = 
    EditNameDialog.newInstance(
      "Informação", "Digite seu nome");

  editNameDialog.openDialog(
    getSupportFragmentManager());
}
 
@Override
public void onFinishEditDialog(
  String inputText) {

  Toast.makeText(this, "Olá, " + inputText, 
    Toast.LENGTH_SHORT).show();  
}
O resultado ficará conforme a figura.


[EDITADO em (11/10/2013 às 11:20)]
Como você devem ter notado, em ambos os exemplo estamos usando a Activity para tratar o retorno do Dialog. Mas e se quisermos chamar o Dialog a partir de um Fragment?
Nesse caso podemos utilizar a propriedade Target Fragment. Ela funciona como o startActivityForResult, só que para Fragments.
// A partir de um Fragment
public void abrirSimpleDialog() {
  SimpleDialog dialog = SimpleDialog.newDialog(
    0, // Id do dialog
    "Alerta", // título
    "Mensagem", // mensagem
    new int[] { // texto dos botões
      android.R.string.ok, 
      android.R.string.cancel });
  // Segredo do sucesso! :)
  // 1 = RequestCode
  dialog.setTargetFragment(this, 1); 
  dialog.openDialog(
    getActivity().getSupportFragmentManager());
}

@Override
public void onActivityResult(int requestCode, 
  int resultCode, Intent data) {

  super.onActivityResult(
    requestCode, resultCode, data);

  int which = data.getIntExtra("which", -1);
  // Tratar dialog  
}
Com essa alteração, a interface FragmentDialogInterface pode ser eliminada (uma vez que a Activity não precisa mais implementá-la), e o código do onClick da classe SimpleDialog, ficaria dessa forma:
@Override
public void onClick(DialogInterface dialog, int which) {
  Intent it = new Intent();
  it.putExtra("which", which);
  // Chamando o onActivityResult do targetFragment
  getTargetFragment().onActivityResult(
    getTargetRequestCode(), Activity.RESULT_OK, it);
}

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

segunda-feira, 5 de novembro de 2012

ActionBar no Android 2.x

Olá povo,

Quem me conhece saber que não sou muito fã de frameworks e bibliotecas, gosto de procurar ao máximo uma solução nativa da plataforma ao qual estou trabalhando. E uma coisa que me deixava muito chateado no Android era a não retrocompatibilidade da ActionBar com versões anteriores à 3.0 (quando ela foi lançada).
Felizmente esse problema acabou com a biblioteca SherlockActionBar, que traz o recurso da ActionBar para aparelhos com Android 2.3 ou inferior, e mantém os nomes de métodos iguais à plataforma nativa. E o que é melhor, quando a aplicação está rodando em versões 3.0 ou superior, o Sherlock só faz chamar a API padrão, ou seja, utiliza a ActionBar nativa.

Vou fazer um exemplo que utiliza dois recursos da ActionBar: Menus e Abas. Faça o download em http://actionbarsherlock.com e descompacte em algum local do seu computador. No Eclipse, importe o projeto library para dentro do seu workspace, para tal acesse File/New.../Project, e na janela que for exibida selecione Android/Android Project From existing code selecione o diretório onde você descompactou o Sherlock. Aparecerá além do projeto library, os demos do Sherlock, por agora só importe o projeto library.

Crie um novo projeto selecionando o Build SDK 4.1 (o Sherlock utiliza Temas e Estilos dessa versão do Android) e o Minimum Required para 2.2. Depois de criado, vá até a pasta lib do projeto e apague o arquivo android-support-v4.jar (o Sherlock já inclui esse arquivo). Agora adicione a referência ao projeto library do Sherlock, para tal, clique com o botão direito no projeto e selecione Properties.

Selecione Android, e na parte inferior direita, clique em Add... e selecione o projeto library e clique em OK. Agora, deixe a Activity do seu projeto conforme abaixo:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.actionbarsherlock.app.*;
import com.actionbarsherlock.app.ActionBar.*;
import com.actionbarsherlock.view.Menu;

public class ExemploSherlockActivity 
  extends SherlockFragmentActivity 
  implements TabListener {

  private Fragmento1 f1;
  private Fragmento2 f2;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTheme(R.style.Theme_Sherlock_Light);
    setContentView(R.layout.activity_exemplo_sherlock);

    f1 = (Fragmento1)getSupportFragmentManager()
         .findFragmentById(R.id.fragmento1);
    f2 = (Fragmento2)getSupportFragmentManager()
         .findFragmentById(R.id.fragmento2);
        
    getSupportActionBar().setNavigationMode(
      ActionBar.NAVIGATION_MODE_TABS);
        
    Tab aba1 = getSupportActionBar().newTab()
               .setText("Aba 1")
               .setTabListener(this);

    Tab aba2 = getSupportActionBar().newTab()
               .setText("Aba 2")
               .setTabListener(this);        
        
    getSupportActionBar().addTab(aba1);
    getSupportActionBar().addTab(aba2);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getSupportMenuInflater().inflate(
      R.menu.activity_exemplo_sherlock, menu);
    return super.onCreateOptionsMenu(menu);
  }

  @Override
  public void onTabSelected(Tab tab, 
    FragmentTransaction ft) {

    if (tab.getPosition() == 0){
      ft.show(f1).hide(f2);
    } else {
      ft.show(f2).hide(f1);
    }
  }

  @Override
  public void onTabUnselected(
    Tab tab, FragmentTransaction ft) {}

  @Override
  public void onTabReselected(
    Tab tab, FragmentTransaction ft) {}
    
  public static class Fragmento1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater infltr, 
      ViewGroup container, Bundle savedState){

      TextView txt = new TextView(getActivity()); 
      txt.setText("Fragmento 1");
      return txt;
    }
  }
 
  public static class Fragmento2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater infltr, 
      ViewGroup container, Bundle savedState){

      TextView txt = new TextView(getActivity());
      txt.setText("Fragmento 2");
      return txt;
    }
  }
}
Como estamos trabalhando com Fragment, nossa classe herda de SherlockFragmentActivity. Implementamos a interface TabChangeListener para detectarmos quando houver a alternância entre abas. Declaramos dois fragmentos que serão nas abas, essa classes estão declaradas no final do arquivo, obviamente o ideal é que cada fragmento fique em um arquivo separado.
No onCreate setamos o tema do Sherlock, e em seguida setamos o arquivo de layout da Activity (que está logo abaixo). Depois inicializamos os fragmentos e definimos a forma de navegação da ActionBar para o modo de abas. E por fim, criamos e adicionamos as abas na ActionBar.

Abaixo temos o arquivo de layout da aplicação (res/layout/activity_exemplo_sherlock.xml):
<FrameLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/FrameLayout1"
  android:layout_width="match_parent"
  android:layout_height="match_parent" >

  <fragment
    android:id="@+id/fragmento1"
    android:name="ngvl.android.exemplosherlock.ExemploSherlockActivity$Fragmento1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

  <fragment
    android:id="@+id/fragmento2"
    android:name="ngvl.android.exemplosherlock.ExemploSherlockActivity$Fragmento2"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

</FrameLayout>

Note que o nome da classe é composto do pacote da aplicação + o nome da classe. Como estamos usando uma Inner Class, o sinal de $ é usado. Se você definir os Fragmentos em arquivos separados (o que é o normal) coloque apenas o nome completo da classe.

E abaixo o arquivo de menu da aplicação (res/menu/activity_exemplo_sherlock.xml) ele é carregado no método onCreateOptionsMenu. Para tratar cada opção adicione o método onMenuItemSelected.
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
  <item
    android:id="@+id/menu_new"
    android:showAsAction="always|withText"
    android:icon="@android:drawable/ic_menu_add"
    android:title="Novo"/>
  <item
    android:id="@+id/menu_edit"
    android:showAsAction="ifRoom|withText"
    android:icon="@android:drawable/ic_menu_edit"
    android:title="Editar"/>
  <item
    android:id="@+id/menu_listar"
    android:showAsAction="ifRoom"
    android:icon="@android:drawable/ic_menu_my_calendar"
    android:title="Listar Itens"/>
</menu>
O primeiro item sempre aparece com ícone e texto. No segundo, o ícone sempre aparece, mas texto só aparece (na barra) se houver espaço. O último só aparecerá se houver espaço, para vê-lo, você deverá pressionar a tecla menu (ou nos dispositivo que não tenham, esse item ficará "colapsed" mas a direita).

Ao mandar executar a aplicação devemos ter uma tela similar a abaixo.

Esse post é em homenagem aos meus alunos da FA7, que me obrigaram a aprender essa biblioteca durante a aula :) Abraço a todos!

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

domingo, 16 de setembro de 2012

Paginação com Swipe no Android

Olá povo,

O versão 20 do plugin ADT (Android Development Toolkit) trouxe algumas novidades, entre elas, alguns templates de projetos que podemos optar na criação de um novo projeto Android. Hoje vou  explicar cada um desses templates e ver como utilizar o template que permite fazer uma paginação similar a do Google Play mostrada abaixo.


Note que a tela "Mais rentáveis" está em evidência, mas podemos notar que há uma opção a esquerda e outra a direita na parte superior. Esse é o intuito da paginação, que permitirá ao usuário exibir a próxima informação apenas deslizando para os lados. Vamos mostrar como fazer isso na sua aplicação.
Inicie a criação de um novo projeto e preencha os campos conforme abaixo e pressione Next.


Na segunda tela, há a opção de criarmos uma Activity em branco ou do tipo Mestre/Detalhe. A primeira cria uma Activity simples,  já a segunda, cria uma Activity que exibe uma listagem e os respectivos detalhes. Se a aplicação estiver executando em um tablet, a listagem e os detalhes de um item aparecem na mesma Activity, caso seja um smartphone, a listagem ficará em uma Activity e os detalhes serão exibidos em outra.
Selecione Blank Activity e clique em Next.
A próxima tela do assistente, solicita o nome da Activity que queremos criar, o nome do seu respectivo arquivo de layout e o título da Activity. Mas a informação mais interessante para nós aqui é o campo Navigation Type, que nos dá as seguintes opções:
A opção None não cria nenhuma opção pré-definida de navegação;
Selecionando Tabs, será criada uma estrutura básica de um Activity com abas usando uma ActionBar (que requer Android 4.1);
Tabs + Swipe é igual a anterior, mas será possível alternar entre as abas com o gesto de swipe (também requer Android 4.1);
Swipe Views + Title Strip permite alternar entre views usando swipe, e o título da view corrente é mostrado em um componente PageStrip;
A última opção é a Dropdown, que configura uma ActionBar onde o usuário poderá selecionar, através de um combobox, a tela que deseja abrir. Ao selecionar o item da lista, um Fragment será carregado (também requer Android 4.1)
Vamos utilizar a opção Swipe Views + Title Strip, pois funcionará em todas as versões do Android. Selecione-a e clique em Finish.
Uma coisa curiosa, é que para utilizar os templates acima, o assistente exige que seja selecionada a versão 4.0.1 do Android na primeira tela do assistente. Mas no caso do template que selecionamos, é possível fazer algumas alterações no projeto para que o mesmo funcione em aparelhos com versões anteriores. Tudo isso, graças a API de compatibilidade do Google (que fica na pasta libs do projeto).
1) Clique com o botão direito sobre o projeto e selecione Properties. Na tela que for exibida, selecione Android do lado esquerdo e em seguida, selecione Android 2.3 (API Level 10) e clique em OK.
2) A Activity dará erro, pois estamos importando duas classes que não existem no Android 2.3: android.app.ActionBar e android.app.FragmentTransaction. Remova esses dois imports.
3) Agora vá no AndroidManifest.xml e modifique o minSdkVersion para 10 (Android 2.3).
<uses-sdk
  android:minSdkVersion="10"
  android:targetSdkVersion="15"/>
4) Dará erro agora nas pastas values-v11, values-v14 e menu. Remova as três.
5) E para finalizar, remova o método onCreateOptionsMenu da sua Activity.

Pronto! Agora é só rodar no seu dispositivo 2.3, o resultado ficará como abaixo:
Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

P.S.: Post relacionado: http://nglauber.blogspot.com.br/2011/09/pagescroll.html

terça-feira, 31 de janeiro de 2012

Fragmentos para todos

Olá povo,

Uma das novidades do Android 3 (Honeycomb), foi a adição do conceito de Fragments. Com essa versão foi escrita exclusivamente para tablets, os "fragmentos" permitem a separação de uma Activity em pedaços de modo a aproveitar os grande tamanho de tela desse tipo de dispositivo. Os Fragments funcionam como sub-activities (dentro de uma Activity), e têm seu próprio ciclo de vida, tratando também os eventos dos componentes nele contidos.

Você pode estar se perguntando: ótimo, mas isso é só pro Android 3 ou superior, então não roda no 2.x. Você estaria certo se a Google não tivesse disponibilizado uma API de compatibilidade, que permite usufruir de alguns recursos do Honeycomb em aparelhos 1.6 (Donut) ou superior. Essa biblioteca está disponível no Android SDK Manager, o mesmo lugar onde você baixa os pacotes necessários para o desenvolvimento Android. Essa API é basicamente um JAR que deve ser adicionado no seu projeto Android.

A Figura abaixo mostra a opção para baixar a API de compatibilidade no Android SDK Manager.

EDITADO EM 10/08/2012
Na versão atual do plugin, essa lib é adicionada automaticamente ao projeto.

Uma vez baixado, o JAR vá até a pasta do SDK e procure pelo arquivo android-support-v4.jar. Ele pode estar na pasta extras/android/compatibility/v4 ou android-compatibility/v4 (dependendo do S.O.).
Crie um novo projeto Android selecionando a versão 1.6 ou superior, e na raiz do projeto, crie uma pasta lib. Copie o JAR para essa pasta e depois adicione-o ao Build-Path do projeto (botão direito sobre JAR, Build Path > Add to build path). Em seguida, clique com o botão direito sobre o projeto e clique em propriedades. Por fim, selecione Java Build Path e na aba Order and Export, marque o android-support-v4.jar.

Antes de começarmos a codificar, vou explicar como será o exemplo. Será uma aplicação clássica de Fragments, onde teremos uma lista, e ao clicar em um dos elementos dessa lista, os detalhes daquele item serão exibidos. Como exemplo, listarei as versões do Android e ao clicar, exibir os detalhes da respectiva versão.

Vou começar criando o layout principal da aplicação. Crie o arquivo res/layout/titles.xml e deixe-o conforme abaixo:
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="horizontal" >

  <fragment
    android:id="@+id/titles"
    android:layout_width="0px"
    android:layout_height="fill_parent" 
    android:layout_weight="1"
    class="ngvl.android.fragments2.TitlesFragment" />

  <FrameLayout
    android:id="@+id/details"
    android:layout_width="0px"
    android:layout_height="fill_parent"
    android:layout_weight="1" />
</LinearLayout>

Aqui temos um LinearLayout horizontal com um Fragment que está apontando para a classe TitlesFragment e está ocupando metade da tela (determinado pela propriedade layout_weight). E um FrameLayout para exibir os detalhes da opção selecionada.

O arquivo acima dará erro porque não criamos a classe TitlesFragment. Ela é mostrada abaixo:
public class TitlesFragment extends ListFragment {

  private final String EXTRA_INDEX = "index";
  private int selectedIndex;
 
  @Override
  public void onActivityCreated(
    Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
  
    ListView lv = getListView();
  
    setListAdapter(
      new ArrayAdapter<String>(
      getActivity(),
      android.R.layout.simple_list_item_checked,
      Dados.titles));
  
    lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  
    if (savedInstanceState != null){
      selectedIndex = 
        savedInstanceState.getInt(EXTRA_INDEX);
      lv.setSelection(selectedIndex);
    }

    if (isDualPane()){
      showDetails(selectedIndex);
    } else {
      getActivity().findViewById(
        R.id.details).setVisibility(View.GONE);
    }
  
    lv.setItemChecked(selectedIndex, true);  
  }

  private void showDetails(int index) {
    selectedIndex = index;
  
    if (isDualPane()) {  
      DetailsFragment details = 
        (DetailsFragment)getFragmentManager()
          .findFragmentById(R.id.details);
   
      if (details == null || 
        details.getShowIndex() != index){

        details = DetailsFragment.newInstance(index);
    
        FragmentTransaction fragTrans =
          getFragmentManager().beginTransaction();
        fragTrans.replace(R.id.details, details);
        fragTrans.setTransition(
          FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        fragTrans.commit();
      }
    } else {
      Intent it = new Intent(getActivity(), 
        DetailsActivity.class);

      it.putExtra(EXTRA_INDEX, index);
      startActivity(it);
    }
  }
 
  @Override
  public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(EXTRA_INDEX, selectedIndex);
  }
 
  @Override
  public void onListItemClick(ListView l, 
    View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
    showDetails(position);
  }
 
  private boolean isDualPane(){
    Configuration config =
      getActivity().getResources().getConfiguration();

    return config.orientation == 
      Configuration.ORIENTATION_LANDSCAPE;
  }
}

Nosso fragmento herda de ListFragment, que é bem similar a classe ListActivity, e exibe informações em formato de lista. Então, no método onActivityCreated (o nome por sí só já diz o que ele faz :) obtemos a referência para a ListView e setamos um Adapter para ela. Os dados que são passados para esse Adapter vêm de um array de Strings contendo as versões do Android definidos na classe Dados. Parte dessa classe é mostrada abaixo:
public class Dados {
  static public String[] titles = {
    "Android 1.5",
    "Android 1.6",
    // Mais itens aqui
  }

  static public String[] description = {
    "Primeira versão do Android, Cupcake",
    "Donut já suporta Fragments",
    // Mais descrições aqui
  }
}

Em seguida, verificamos se existe um estado salvo da nossa Activity. Quando entramos a primeira vez, esse estado estará vazio, mas quando girarmos o aparelho, o método onSaveInstance será chamado e nós salvaremos a posição da lista que está checada. Se já existir um estado saldo, marcamos a opção que estava selecionada antes de girar o aparelho e fazemos o scroll até ela.
Logo após, verificamos se a aplicação é dualPane (dois painéis) esse método (que está no fim da classe) verifica se a orientação do aparelho está como landscape. Em caso positivo exibiremos a lista e os detalhes no mesmo momento, caso contrário exibiremos apenas a lista, então ocultamos o FrameLayout de detalhes.
O método showDetails verifica se a tela é dualPane, em caso positivo, instancia o fragmento de detalhes e o exibe utilizando a class FragmentTransition. Caso não seja dualPane, ou seja, está em Portrait, exibimos a Activity de detalhes.

Com a utilização de Fragments, a Activity passa a controlar o Fragment, e o Fragment trata eventos de UI. Com isso a Activity fica bem simples como podemos ver abaixo:
public class TitleActivity extends FragmentActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.titles);
  }
}

O único detalhe é que essa clase herda de FragmentActivity. Lembrando que ela é uma classe da API de compatibilidade, no Android 3.x podemos continuar usando Activity mesmo. Agora vamos analisar o fragmento de detalhes
public class DetailsFragment extends Fragment {

  private static final String EXTRA_INDEX = "index";
 
  public static DetailsFragment newInstance(int index) {
    DetailsFragment fragment = new DetailsFragment();
  
    Bundle params = new Bundle();
    params.putInt(EXTRA_INDEX, index);
    fragment.setArguments(params);
  
    return fragment;
  }
 
  public int getShowIndex() {
    return getArguments().getInt(EXTRA_INDEX);
  }

  @Override
  public View onCreateView(LayoutInflater inflater,
    ViewGroup container, Bundle savedInstanceState) {
  
    TextView txt = new TextView(getActivity());
    txt.setText(Dados.description[getShowIndex()]);
  
    return txt;
  }
}

A classe tem um Factory Method (chamado newInstance) que cria um DetailsFragment já passando os "extras" necessários. O método onCreateView define o que será exibido no Fragment, para simplificar, criamos apenas um TextView, mas você poderia usar o parâmetro inflater para carregar um arquivo de Layout que desejasse.
Por fim, vamos mostrar a Activity de detalhes.
public class DetailsActivity extends FragmentActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  
    if (isDualPane()){
      finish();
      return;
   
    } else if (savedInstanceState == null) {
  
      DetailsFragment details = new DetailsFragment();
      details.setArguments(getIntent().getExtras());
   
      getSupportFragmentManager()
        .beginTransaction()
        .add(android.R.id.content, details)
        .commit();
    }
  }
 
  private boolean isDualPane(){
    Configuration config =
      getResources().getConfiguration();

    return config.orientation ==
      Configuration.ORIENTATION_LANDSCAPE;
  }
}

Nessa Activity, basicamente verificamos se a orientação é landscape, em caso positivo, essa Activity é fechada, e consequentemente a TitlesActivity é exibida mostrando os títulos e os detalhes do mesmo. Já se Activity não é dual pane (ou seja, é portrait) e foi passado o índice para ela, carregamos o Fragment e o adicionamos à tela.

A imagem abaixo mostra nossa aplicação rodando em landscape.

Esse artigo foi baseado nesse post do Blog do Android Developers.

Qualquer dúvida, deixem seus comentários,

4br4ç05,
nglauber