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

terça-feira, 11 de março de 2014

Dicas de Android 10

Olá povo,

Mais um post da série "Dicas de Android". Aproveitem!

Dica 1. Densidades de tela no Android e seus DPIs.
Atualmente temos dispositivos Android de diversos tamanhos e com qualidade de tela diferentes. É importante sabermos a tabela abaixo para usar imagens adequadamente para cada um deles.
DensidadeDots per inchProporção
LDPI120dpi0.75
MDPI160dpi1.00
HDPI240dpi1.50
XHDPI320dpi2.00
XXHDPI480dpi3.00
XXXHDPI640dpi4.00

Dica 2. Obter as polegadas de um aparelho. Dica de Maurício Taumaturgo.
public double getInch(Activity act) {
  DisplayMetrics displayMetrics = new DisplayMetrics();
  act.getWindowManager().getDefaultDisplay()
    .getMetrics(displayMetrics);

  double x = Math.pow(
    displayMetrics.widthPixels/displayMetrics.xdpi,2);
  double y = Math.pow(
    displayMetrics.heightPixels/displayMetrics.ydpi,2);
  double screenInch = Math.sqrt(x+y);
  return screenInch; 
}

Dica 3. HTML no TextView. Dica de Diego Nascimento (aluno do TECDAM)
O Android suporta algumas tags HTML dentro do componente TextView. Mas se quisermos carregar as imagens da tag <img /> usamos a classe ImageGetter.
TextView tvText = (TextView) findViewById(R.id.text);
final String textoEmHtml = 
  "<html><body>Html em "
  + "<b>Negrito</b> e <i>Itálico</i>"
  + "<img src='mario.png' />"
  + "Mais um texto qualquer"
  + "<img src='luigi.png' />"
  + " texto depois da imagem</body></html>";

ImageGetter imgGetter = new ImageGetter() {
  public Drawable getDrawable(String source) {
    BitmapDrawable drawable = null;
    try {
      Bitmap bmp = BitmapFactory.decodeStream(
        getAssets().open(source));

      drawable = new BitmapDrawable(
        getResources(), bmp);

      drawable.setBounds(
        0, 0, bmp.getWidth(), bmp.getHeight());

    } catch (IOException e) {
      e.printStackTrace();
    }
    return drawable;
  }
};

meuTextView.setText(
  Html.fromHtml(textoEmHtml, imgGetter, null));

Dica 4. Customizando ActionBar
A ActionBar é utilizada como padrão navegacional do Android. Entretanto, customiza-la para com as cores da sua aplicação requer um certo esforço. Para minizar esse trabalho, é possível gerar os arquivos de recurso para personalizar a ActionBar através do site Android ActionBar Style Generator.

Dica 5. EditText com imeOptions e inpuType
A propriedade inputType do EditText indica qual o formato do teclado virtual mais adequado para a caixa de texto, enquanto a propriedade imeOptions indica o botão de ação do teclado.
<EditText
  android:id="@+id/editText1"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:inputType="textEmailAddress"
  android:imeOptions="actionDone" />
final EditText edtEmail = 
  (EditText)findViewById(R.id.editText1);
edtEmail.setOnEditorActionListener(
  new OnEditorActionListener() {
    @Override
    public boolean onEditorAction(
      TextView v, int actionId, KeyEvent event) {

      if (v == edtEmail && 
        EditorInfo.IME_ACTION_DONE == actionId) {

        String email = edtEmail.getText().toString();
        if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()){
          edtEmail.setError("Email inválido!");
        }
        return true;
      }
      return false;
    }
});
Dica 6. Carregando HTML com CSS e JavaScript no WebView
Se você for carregar um arquivo HTML que está dentro do seu APK, e esse arquivo usa arquivos de CSS e JavaScript, aconselho coloca-los na pasta assets do seu projeto e carregá-los da seguinte forma:
mWebView = (WebView) v.findViewById(R.id.webView1);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
mWebView.loadDataWithBaseURL(
  "file:///android_asset/", 
  "Meu Html que usa CSS e JS",
   "text/html", "UTF-8", null);

4br4ç05,
nglauber

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

segunda-feira, 15 de julho de 2013

ListView com seleção múltipla + ActionBar + ActionMode

Olá povo,

Depois de quase um mês sem postar, resolvi aproveitar o feriadão para colocar um post colaborativo e que me tirou um trauma. Senta que lá vem história...
Um dia desses, meu amigo André Melo (a.k.a. Deco Balaca) me perguntou como fazia uma ListView com seleção múltipla, e que os itens selecionados ficassem com o background diferente. Quatro anos mexendo com o danado do Android e me enrolei pra fazer isso.
A classe ListView tem uma propriedade android:listSelector que nos permite modificar o visual dos itens da lista de acordo com o seu estado (eu falei de selector nesse post aqui).
O problema é que apesar de haver a propriedade android:state_checked (usado também em componentes como Checkbox e ToggleButton), devemos usar a propriedade android:state_activated para indicar que um item da lista está checado (#tenso).
Muito bem! Tudo resolvido? Sim, se você não quiser dar suporte a versões anteriores à 3.0 (Honeycomb), caso contrário você terá que fazer as coisas manualmente.
Meu aluno do TECDAM, Carlos Eduardo Carneiro, me deu essa dica. Abaixo temos um adapter que verifica se a linha está checada, em caso positivo, ele muda o background da View da linha correspondente.
class MultiSelectAdapter extends ArrayAdapter<String>{

  public MultiSelectAdapter(
    Context context, 
    int textViewResourceId,
    List<String> objects) {

    super(context, textViewResourceId, objects);
  }

  @Override
  public View getView(int position, 
    View convertView, ViewGroup parent) {

    View v = super.getView(
      position, convertView, parent);

    ListView listView = (ListView) parent;
    int color = listView.isItemChecked(position) ? 
      Color.argb(0xFF, 0x31, 0xB6, 0xE7) : 
      Color.TRANSPARENT;

    v.setBackgroundColor(color);

    return v;
  }
}
Estou usando um ArrayAdapter simples, mas essa abordagem funcionaria para um adapter customizado também, só que você setaria o background do layout que você carregou...
Resolvido esse problema, resolvi aproveitar esse post para mostrar como utilizar um recurso muito utilizado em aplicações Android: o Action Mode. Essa é uma característica da ActionBar do Android que permite exibir opções de menu de acordo com a necessidade.
Se tornou praticamente padrão, excluir múltiplos itens de uma lista dando um clique longo em um dos itens e depois selecionar outros (podemos ver isso no Gmail, galeria de mídia, etc.). No nosso exemplo, vou mostrar como permitir excluir múltiplos itens de uma ListView. Mas você pode estar se perguntando: ActionBar não é só pro Android 3.0 ou superior? Dê uma olhada nesse link e conheça o Sherlock, pois vamos usá-lo aqui.
O exemplo consta apenas de uma Activity mostrada (uma parte) abaixo:
public class MainActivity 
  extends SherlockActivity 
  implements 
    OnItemClickListener, 
    OnItemLongClickListener, 
    ActionMode.Callback {

  private ListView listView;
  private List<String> nomes;
  private ActionMode actionMode;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTheme(R.style.Theme_Sherlock_Light_DarkActionBar);

    nomes = new ArrayList<String>();
    nomes.add("nglauber");
    nomes.add("CESAR.edu");
    nomes.add("CESAR");
    nomes.add("Unibratec");

    listView = new ListView(this);
    listView.setOnItemClickListener(this);
    listView.setOnItemLongClickListener(this);
    listView.setAdapter(new MultiSelectAdapter(this,
      android.R.layout.simple_list_item_1, nomes));
      setContentView(listView);
  }

  @Override
  public void onItemClick(
    AdapterView<?> adapterView, View view,

    int position, long id) {
    if (actionMode == null) {
      // Faça algo ao clicar no item normalmente
    } else {
      int checkedCount =  
        atualizarItensMarcados(listView, position);

      if (checkedCount == 0) {
        actionMode.finish();
      }
    }
  }

  @Override
  public boolean onItemLongClick(
    AdapterView<?> adapterView, View view,
    int position, long id) {

    boolean consumed = (actionMode == null);

    if (consumed) {
      actionMode = startActionMode(this);
      listView.setChoiceMode(
        ListView.CHOICE_MODE_MULTIPLE);

      listView.setItemChecked(position, true);
      atualizarItensMarcados(listView, position);
    }
    return consumed;
  }
A nossa classe implementa três interfaces, onde as duas primeiras são bem conhecidas. A última é que vamos detalhar mais no próximo trecho de código. O atributo actionMode é que vai exibir a opção de excluir e mostrar quantos itens estão selecionados. Esse atributo é inicializado no método onItemLongClick através da chamada do método startActionMode. Nesse momento, habilitamos a seleção múltipla na ListView e já checamos o item da posição onde clicamos.
Sendo assim, no método onItemClick, nós verificamos se actionMode é igual a null, nesse caso, não estamos com o ActionMode ativo na ActionBar e devemos processar o clique normalmente. Caso contrário, atualizamos a lista com novo item que foi clicado. Mas se não houver mais itens selecionados, desativamos o ActionMode chamando o método finish.

Vamos agora ver o código da interface ActionMode.Callback e o método atualizarItensMarcados.
  @Override
  public boolean onCreateActionMode(
    ActionMode mode, Menu menu) {

    getSupportMenuInflater().inflate(
      R.menu.menu_delete_list, menu);

    return true;
  }

  @Override
  public boolean onPrepareActionMode(
    ActionMode mode, Menu menu) {

    return false;
  }

  @Override
  public boolean onActionItemClicked(
    ActionMode mode, MenuItem item) {

    if (item.getItemId() == R.id.action_delete) {
      SparseBooleanArray checked = 
        listView.getCheckedItemPositions();

      for (int i = checked.size()-1; i >= 0; i--) {
        if (checked.valueAt(i)) {
          nomes.remove(checked.keyAt(i));
        }
      }
      actionMode.finish();
      return true;
    }
    return false;
  }

  @Override
  public void onDestroyActionMode(ActionMode mode) {
    actionMode = null;
    listView.clearChoices();
    ((BaseAdapter) listView.getAdapter())
      .notifyDataSetChanged();

    listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
  }

  private int atualizarItensMarcados(
    ListView l, int position) {

    SparseBooleanArray checked = 
      l.getCheckedItemPositions();

    l.setItemChecked(position, 
      l.isItemChecked(position));

    int checkedCount = 0;
    for (int i = 0; i < checked.size(); i++) {
      if (checked.valueAt(i)) {
        checkedCount++;
      }
    }

    actionMode.setTitle(
      checkedCount + " selecionados");

    return checkedCount;
  }
}
O método onCreateActionMode permite carregar um arquivo de menu para actionMode, note que ele retorna true para informar que o actionMode pode ser criado. Já o método onPrepareActionMode é usado para quando queremos atualizar o actionMode após sua criação, retornamos falso para indicar que ele não foi atualizado. Quando uma opção de menu do ActionMode é selecionada, o método onActionItemClicked é chamado. Nesse método, checamos se a opção selecionada foi a de excluir, em caso positivo, obtemos a lista das posições da lista que estão marcadas. Removemos essas posições da lista e depois finalizamos o ActionMode.
Ao chamar o método finish do ActionMode, o método onDestroyActionMode é chamado. Nesse momento, setamos o atributo actionMode para null, desmarcamos os itens (caso haja algum), chamamos o notifyDatasetChanged para que a ListView seja redesenhada, e por fim, voltamos o tipo de seleção da lista como nenhum.
O método atualizarItensMarcados apenas retorna a quantidade de itens que estão checados e seta o título da actionMode.

O arquivo de menu utilizado no método onCreateActionMode é mostrado abaixo:
<menu 
  xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:id="@+id/action_delete"
    android:icon="@android:drawable/ic_menu_delete"
    android:orderInCategory="100"
    android:showAsAction="ifRoom"/>
</menu>

E o resultado, podemos ver na figura a seguir:

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