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

quarta-feira, 31 de julho de 2013

ActionBar na API de Compatibilidade

Olá povo,

No último 18/07 a Google finalmente adicionou o suporte à ActionBar na API de compatibilidade. Pelo menos para API v7 (Android 2.1 ou superior). Para usá-la, vá até o SDK Manager e atualize a API de compatibilidade marcando a opção Extras > Android Support Library.



Feito isso, vá até o Eclipse e importe o projeto appcompat disponível no diretório SDK_DIR/extras/android/support/v7. Crie um novo projeto e referencie o projeto importado anteriormente clicando com o botão direito sobre o projeto e selecionando Properties.  Em seguida, selecione a opção Android no lado esquerdo e na parte inferior, clique em Add. Selecione o projeto android-support-v7-appcompat. Agora apague o arquivo android-support-v4.jar da pasta lib do projeto, pois o projeto que acabamos de importar já tem esse arquivo.

Nossa Activity agora herdará ActionBarActivity (que herda de FragmentActivity).
import android.support.v7.app.ActionBarActivity;

public class MainActivity extends ActionBarActivity {
  // Implementação normal da Activity
}

Obrigatoriamente, nossa Activity tem que ter o estilo da R.style.Theme.AppCompat. Para tal, vá até o arquivo values/styles.xml e modifique o tema da aplicação conforme abaixo.

<style name="AppBaseTheme" 
  parent="@style/Theme.AppCompat">

Pronto! Agora é só usar a ActionBar como se estivesse no Android 3.0 ou superior :)

EDITADO em 05/08/2013

Para usar a SearchView nesse nova biblioteca, adicione o arquivo res/menu/main.xml.
<menu 
  xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:suaapp="http://schemas.android.com/apk/res-auto">

  <item
    android:id="@+id/action_settings"
    android:orderInCategory="100"
    android:showAsAction="always|withText"
    android:icon="@android:drawable/ic_menu_info_details"
    android:title="@string/action_settings"/>

  <item android:id="@+id/search"
    android:title="Search"
    android:icon="@android:drawable/ic_menu_search"
    suaapp:showAsAction="collapseActionView|ifRoom"
    suaapp:actionViewClass="android.support.v7.widget.SearchView" />
</menu>
Agora deixe o código da sua Activity como abaixo:
public class MainActivity extends ActionBarActivity 
  implements SearchView.OnQueryTextListener {

  private MenuItem mSearchItem;
  private SearchView mSearchView;
  private TextView mTextTeste;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mTextTeste =
     (TextView)findViewById(R.id.textView1);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
  
    mSearchItem = menu.findItem(R.id.search); 
    mSearchView = (SearchView) 
      MenuItemCompat.getActionView(mSearchItem); 
    mSearchView.setOnQueryTextListener(this);
  
    return true;
  }

  @Override
  public boolean onQueryTextChange(String text) {
    mTextTeste.setText(text);
    return false;
  }

  @Override
  public boolean onQueryTextSubmit(String text) {
    mTextTeste.setText(text +" GO!");
    MenuItemCompat.collapseActionView(mSearchItem);
    return true;
  }
}


4br4ç05,
nglauber

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