segunda-feira, 31 de outubro de 2016

Como usar o método startActivityForResult?


O startActivityForResult funciona com a utilização de 3 métodos em conjunto:

1) startActivityForResult, vai receber uma intenção e um código identificador. Assim, ele poderá abrir uma outra activity solicitando um resultado dela.

Exemplo:
private final static  int CODIGO_IDENTIFICADOR = 1;
Intent intent = new Intent(PrimeiraActivity.this, SegundaActivity.class);
startActivityForResult(intent, CODIGO_IDENTIFICADOR);


2) setResult, vai pegar um intent que possui dados extras e associar ao resultado pelo código identificador setado pela startActivityForResult. O setResult deve estar na SegundaActivity, pois é ela que retornará os dados pedidos pela primeira.

Exemplo:

Intent intent = getIntent();
intent.putExtra("resultado", result.toString());
setResult(Activity.RESULT_OK, intent);
finish();


3) onActivityResult(), vai recuperar o resultado setado de acordo com o código identificador passado. Logicamente o onActivityResult() deve estar na PrimeiraActivity, pois é ela que irá recuperar o resultado.

Exemplo:
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 
            if(requestCode == CODIGO_IDENTIFICADOR) { 
                    String message = data.getStringExtra("resultado");  
                        Result.setText("Resultado: "+message); 
           
}
 

Colocando um botão Up na ActionBar

Depois de aprender a criar uma ActionBar no post Criando e manipulando uma ActionBar no Android, agora vem uma dica rápida para colocar um botão de up (aquela setinha que faz voltar para a activity anterior) na ActionBar.

É muito simples! Basta colocar o seguinte código na Activity:

        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);


Também é necessário sobrescrever o método onOptionsItemSelected:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            finish();
        }
        return super.onOptionsItemSelected(item);
    }


Assim, se o id do item clicado for o android.R.id.home, que corresponde ao botão de up, então é dado um finish na activity para que volte à tela anterior.

 That's it.

Criando e manipulando uma ActionBar no Android

Para colocar um ícone na ActionBar é necessário 3 coisas:

1) A Activity deve herdar de AppCompatActivity;
2) Criar um xml do tipo Menu;
3) Sobrescrever os métodos:
- "onCreateOptionsMenu"
- "onPrepareOptionsMenu"
- "onOptionsItemSelected"

CRIANDO UM XML DO TIPO MENU

1) Clicar em 'res' com o botão direito do mouse e ir em 'New' e depois em 'Android Resource File';

2) Escolher um nome e por 'Menu' como o 'Resource type';

3) Por um item no código xml do menu criado, como no código abaixo:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_add"
        android:title="@string/adicionar"
        android:icon="@drawable/ic_action_add"
        app:showAsAction="always"
    />

</menu>


O valor 'always' do atributo 'showAsAction' significa que o ícone sempre será mostrado. Se escolhesse 'ifRoom', o ícone só apareceria se tivesse espaço na tela e, se escolhesse 'never', o ícone ficaria suprimido dentro de um menu de opções (overflow menu).

Cuidado! Como você está utilizando o  AppCompatActivity, o showAsAction deve ser antecedido de app:, assim como é mostrado no exemplo acima. Se, em vez de app: você utilizar o android:, ou seja, android:showAsAction="always", o ícone não será mostrado e ficará compactado em um menu, como se, em vez de "always" tivesse sido escolhido "never".

Obs. Caso não tenha a imagem de um ícone para colocar, pode-se utilizar algum dos ícones disponívels pelo próprio Android Studio da seguinte maneira: clicar com o botão direito do mouse em cima de drawble, depois indo em 'New', 'Image Asset'. Escolha o 'Icon Type' como 'Action Bar and Tab Icons'. Esse ícone é que poderá ser usado no item do código xml criado acima (android:icon="@drawable/ic_action_add");

SOBRESCREVENDO OS MÉTODOS "onCreateOptionsMenu", "onPrepareOptionsMenu" e "onOptionsItemSelected"

Implementar os métodos "onCreateOptionsMenu", "onPrepareOptionsMenu" e "onOptionsItemSelected" na Activity em que se está criando a ActionBar. Como no código a seguir:

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

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.findItem(R.id.action_add).setVisible(true);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_add) {
            Intent intent = new Intent(MainActivity.this, CadastraPlaylistActivity.class);
            startActivity(intent);
        }
        return super.onOptionsItemSelected(item);
    }


O que faz o onCreateOptionsMenu?
Ele vai inflar o menu em xml que você criou na tela. Por isso você deve indicar o caminho dele que é R.menu.nome_do_menu.

O que faz o onPrepareOptionsMenu?
Esse método é executado sempre antes do menu ser mostrado. Ele pode ser usado para habilitar/desabilitar itens ou modificar algum conteúdo dinamicamente. No exemplo acima, o ícone "action_add" foi setado para ficar sempre visível, sem testar qualquer condição (setVisible(true)), mas é possível fazer com que ele só se torne visível após o teste de alguma condição. Por exemplo, talvez só seja interessante que o ícone apareça após o usuário entrar com alguma string, que será enviada via intent. Nesse caso, o método deveria testar se essa string está vazia ou não, caso esteja, o setVisible ficará false. Ao fazer esse teste condicional, é preciso que esses métodos sejam executados novamente, pois eles só são executados na hora em que a ActionBar está sendo carregada. Assim, caso queira que esses métodos sejam executados novamente é necessário colocar um "supportInvalidateOptionsMenu();" em alguma parte do código.

Outro exemplo, seria o caso de só mostrar um ícone na ActionBar após o preenchimento de um EditText. Poderia-se usar o  supportInvalidateOptionsMenu(), como no exemplo abaixo:

        meuEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { }

            @Override
            public void afterTextChanged(Editable editable) { supportInvalidateOptionsMenu(); }
        });


O que faz o onOptionsItemSelected?
É a ação que ocorre após o ícone da ActionBar ser clicado. No exemplo acima, após o ícone ser clicado, ele verifica se é o ícone que eu estou pensando que é (R.id.action.add). Caso seja, por meio da intent, ele sai da MainActivity e vai para outra activity chamada CadastraPlaylistActivity.

quarta-feira, 12 de outubro de 2016

Intent-filter para link compartilhado

Você está usando um browser em seu celular e deseja compartilhar o link do site que está vendo. Como fazer com que seu aplicativo receba esse link e trate ele para ser usado dentro da aplicação? Basta criar a seguinte intent-filter dentro de seu AndroidManifest.xml:

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>


Isso fará com que a sua activity, no exemplo .MainActivity, enxergue uma intenção de envio no formato texto. Após isso, como receber essa informação e utilizá-la dentro da aplicação? Basta criar um intent e verificar se ela se refere a uma ACTION_SEND. Caso seja, pegue as informações com o método getStringExtra passando como parâmetro Intent.EXTRA_SUBJECT para pegar o título da página e Intent.EXTRA_TEXT para pegar o texto compartilhado que, no caso, é o link da página que está sendo compartilhado:

        Intent meulink = getIntent();
        if(savedInstanceState == null && meulink != null) {
            if(meulink.getAction().equals(Intent.ACTION_SEND)) {
                String mensagem = meulink.getStringExtra(Intent.EXTRA_SUBJECT);
                mensagem += "\n\n";
                mensagem += meulink.getStringExtra(Intent.EXTRA_TEXT);
                textView.setText(mensagem);
            }
        }

terça-feira, 11 de outubro de 2016

Enviando RadioButton escolhido para outra Activity

Criei um app com duas activities. Na main activity, há um radio group com dois radio buttons dentro. Na segunda activity, é necessário receber a informação da main activity dizendo qual radio button foi escolhido. Assim, ao clicar em um botão da main activity, ela enviar uma string que caracteriza qual radio button foi marcado. Veja o código como ficou:

    botaoCalcular.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int idRadioButtonEscolhido = radioGroup.getCheckedRadioButtonId();
                String str = "";

                if (idRadioButtonEscolhido > 0) {
                    switch (idRadioButtonEscolhido) {
                        case R.id.radioButton1Id:
                            str = "button1Text";
                            break;
                        case R.id.radioButton2Id:
                            str = "button2Text";
                            break;
                    }

                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setPackage(getPackageName());
                    intent.putExtra("opcao", str);
                    startActivity(intent);

                } else {
                    Toast.makeText(MainActivity.this, "Escolha uma opção.", Toast.LENGTH_SHORT).show();
                }
            }
        });


Assim, a outra activity pode usar a "opcaoEscolhida" como bem quiser, recuperando essa informação da seguinte maneira:

    Bundle extra = getIntent().getExtras();

        if(extra != null) {
            opcaoEscolhida = extra.getString("opcao");
        }

sexta-feira, 7 de outubro de 2016

Aprendendo Collections Framework

Para entender como as principais estruturas de dados já implementadas na API Java funcionam, escrevi o seguinte código:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

public class Colecoes {
    //Método que remove strings duplicadas de uma Lista de Strings
    public static List<String> removeDuplicates(List<String> lista) {
        List<String> listaCopy = new ArrayList<String>();
        List<String> posicoesCopy = new ArrayList<String>();
       
        for(int i = 0; i < lista.size(); i++) {
            listaCopy.add(lista.get(i));
        }
       
        for(int i = 0; i < lista.size(); i++) {
            for(int j = 0; j < listaCopy.size(); j++) {
                if(lista.get(i).equals(listaCopy.get(j)) && j > i) posicoesCopy.add(String.valueOf(j));
            }
        }
       
        for(int i = 0; i < posicoesCopy.size(); i++) {
            lista.remove(Integer.parseInt(posicoesCopy.get(i)));
        }
       
        return lista;
    }

    public static void main(String[] args) {
        List<String> frutaArrayList = new ArrayList<String>();
        Set<String> frutaTreeSet = new TreeSet<String>();
        HashMap<String, String> frutaHashMap = new HashMap<String, String>();
        List<String> frutaLinkedList = new LinkedList<String>();
        Set<String> frutaHashSet = new HashSet<String>();
        LinkedHashMap<String, String> frutaLinkedHashMap = new LinkedHashMap<String, String>();
       
        String[] frutas = {"banana", "morango", "abacaxi", "damasco",
                "abacaxi", "banana", "abacate"
        };
       
        for(int i = 0; i < frutas.length; i++) {
            frutaArrayList.add(frutas[i]);
            frutaTreeSet.add(frutas[i]);
            frutaHashMap.put(String.valueOf(i), frutas[i]);
            frutaLinkedList.add(frutas[i]);
            frutaHashSet.add(frutas[i]);
            frutaLinkedHashMap.put(String.valueOf(i), frutas[i]);
        }
       
        System.out.println("ArrayList: "+frutaArrayList);
        System.out.println("TreeSet: "+frutaTreeSet);
        System.out.println("HashMap: "+frutaHashMap);
        System.out.println("LinkedList: "+frutaLinkedList);
        System.out.println("HashSet: "+frutaHashSet);
        System.out.println("LinkedHashMap: "+frutaLinkedHashMap);
       
        System.out.println("\nRemovendo duplicadas da ArrayList: ");
        System.out.println(removeDuplicates(frutaArrayList));
    }
}

Como resultado da execução do código acima, obtive a seguinte saída:

ArrayList: [banana, morango, abacaxi, damasco, abacaxi, banana, abacate]
TreeSet: [abacate, abacaxi, banana, damasco, morango]
HashMap: {0=banana, 1=morango, 2=abacaxi, 3=damasco, 4=abacaxi, 5=banana, 6=abacate}
LinkedList: [banana, morango, abacaxi, damasco, abacaxi, banana, abacate]
HashSet: [banana, morango, abacaxi, abacate, damasco]
LinkedHashMap: {0=banana, 1=morango, 2=abacaxi, 3=damasco, 4=abacaxi, 5=banana, 6=abacate}

Removendo duplicadas da ArrayList:
[banana, morango, abacaxi, damasco, abacate]

Com isso, pude aprender que as estruturas do tipo 'Set' eliminam as palavras duplicadas automaticamente, sendo que a TreeSet, além de fazer isso, já põe em ordem alfabética. Também aprendi que não se pode percorrer uma estrutura do tipo 'Set', pois ela não possui um método get(). A HashSet trocou de posição as palavras 'abacate' e 'damasco', o que demonstra que estruturas do tipo 'Set' não respeitam a ordem de adição, organizando os objetos de qualquer maneira ou em ordem alfabética, no caso da TreeSet.

É possível ainda melhorar o método "removeDuplicates", pois a lista está sendo percorrida várias vezes desnecessariamente. Assim, se essa lista fosse muito grande, isso causaria lentidão no processamento. Uma maneira de melhoria pode ser vista logo abaixo:

public static List<String> removeDuplicates(List<String> lista) {
        List<String> result = new ArrayList<String>();
        Set<String> itemsSet = new HashSet<>();
   
        for(int i = 0; i < lista.size(); i++) {
            String s = lista.get(i);
            if (!itemsSet.contains(s)) {
                itemsSet.add(s);
                result.add(s);
            }
        }
        return result;
}

O segredo está no uso de um 'Set', no caso, o HashSet itemsSet, pois, com ele, não é preciso percorrer a lista para encontrar o que se quer. Ele vai diretamente ao ponto por meio de uma tabela hash e verifica com o método contains se existe ou não determinado elemento. Isso evita aquele aninhamento desnecessário de loops. Além disso, impede-se que elementos duplicados sejam adicionados a nova lista (result), pois a adição só ocorre se não existir o item analisado dentro de itemsSet. 

segunda-feira, 3 de outubro de 2016

Ícones gratuitos para projetos Android

A google disponibiliza ícones gratuitos para serem usados no desenvolvimento de seus aplicativos Android, basta ir no link Material icons.

Clique sobre um dos ícones, escolha a cor, tamanho e formato e faça o download. Assim, será baixado um arquivo compactado com diversos tamanhos já adequadamente organizados e prontos para utilização em seu projeto. Vá na pasta "android" e lá estará as seguintes pastas:

drawable-xxxhdpi
drawable-xxhdpi
drawable-xhdpi
drawable-mdpi
drawable-hdpi

Depois de feito o download, visualize o seu projeto no Android Studio por meio de "Project" e cole essas pastas dentro de "res" (src > main > res).

Dica de uso dos ícones: baixe sempre os ícones com a cor branca, depois mude-a via Android Studio utilizando a propriedade "tint" no arquivo .xml.