A maioria dos aplicativos móveis hoje em dia exibem as famosas splash screens (telas de abertura) como primeiro contato com o usuário. Ao abrir o app, o mesmo pode executar vídeos, animações via XML, ou até mesmo imagens animadas por um pequeno período de tempo e, ao mesmo tempo, aproveitar para inicializar quaisquer recursos que a aplicação necessite para começar a ser usada, como o acesso a um Web Service ou subir para a nuvem os dados de quando o app foi acessado off-line.
A grande porcentagem dos apps que fazem uso desse recurso são jogos, mas também é possível criar animações mais simplistas, com a logo da empresa e alguns efeitos de cores e iluminação. Como no Android não temos nenhum mecanismo nativo para implementar uma splash screen, neste artigo trataremos de exibir um pequeno tutorial de como atingir isso, com a logo da sua empresa e um fundo iluminado. O resultado deste pode ser visto na Figura 1.
Para contextualizar melhor dividiremos nossa implementação em duas partes:
- A primeira terá como foco exibir a splash screen quando o usuário entrar no aplicativo;
- A segunda quando o mesmo tentar efetuar qualquer comunicação via HTTP, como baixar uma imagem, baixar e armazenar dados no SQLite, enviar requisições via JSON/XML, dentre outros.
Para isso, criaremos duas activities: uma para exibir a tela de abertura e, uma vez que esta encerre, carregamos a activity principal. Para o exemplo, faremos uso do Android Studio 1.2.2 e Android SDK 17.
Splash Screen ao entrar no app
O primeiro passo é criar um novo projeto. Portanto, acesse o menu “File > New > New Project...” e preencha o nome da aplicação com "SplashDevMedia" e o nome do pacote de domínio com “br.edu.devmedia.splashdev”. Depois de clicar em Next duas vezes e em Finish, a IDE criará a estrutura com uma activity de nome MainActivity, que só será usada para a segunda activity.
Para criar a segunda activity siga os mesmos passos da primeira e dê o nome de SplashDevMedia.java. Antes de adicionar o conteúdo Java e criar os arquivos de resources, vamos configurar as permissões de acesso aos serviços e controladores que o aplicativo precisará no AndroidManifest.xml. Abra o arquivo na raiz do projeto e modifique-o conforme a Listagem 1.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas
.android.com/apk/res/android"
package="br.edu.devmedia.splashscreen"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<!-- Splash screen -->
<activity
android:name="br.edu.devmedia.splashscreen
.SplashDevMedia"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@android:style/Theme
.Black.NoTitleBar" >
<intent-filter>
<action android:name="android
.intent.action.MAIN" />
<category android:name="android
.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Activity Principal -->
<activity
android:name="br.edu.devmedia.splashscreen.ActivityPrincipal"
android:label="@string/app_name" >
</activity>
</application>
</manifest>
A maioria das configurações já são trazidas pelas wizards de criação do Android Studio, mas é importante averiguar sempre a versão do SDK que ele pôs na tag uses-sdk, além de mapear as duas activities que usaremos, definindo a primeira como principal. As labels de cada uma na ActionBar serão o nome do aplicativo padrão do arquivo strings.xml.
Vamos criar agora o arquivo de layout da tela de splash screen, indo até o diretório “res > layout” para criar um novo arquivo XML de nome “activity_splash.xml”. Adicione o conteúdo da Listagem 2 ao mesmo.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/gradient_background" >
<ImageView
android:id="@+id/imgLogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/dev_logo" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:textSize="12dp"
android:textColor="#454545"
android:gravity="center_horizontal"
android:layout_alignParentBottom="true"
android:text="www.devmedia.com.br" />
</RelativeLayout>
Esse arquivo é composto basicamente de dois componentes: uma ImageView que carregará a imagem da logo centralizada no meio da tela, e uma label de texto que exibirá o texto “devmedia.com.br” na base da página. O arquivo da imagem pode ser qualquer um de sua preferência, desde que atenda aos requisitos de dimensões mínimas: 400x282 pixels. O leitor pode ficar à vontade para substituir por qualquer imagem de logo que deseje e ver como o app se comporta.
Também precisamos criar um arquivo de gradiente que guardará as configurações de cores de fundo da tela que vimos na Figura 1. Para isso, vá até o diretório “res > drawable” e crie um novo arquivo XML de gradiente chamado “gradient_background.xml” com o código presente na Listagem 3.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient android:angle="90" android:endColor="#ffed1a06"
android:startColor="#ffede23c" android:type="linear" />
</shape>
</item>
</selector>
A tag define um ângulo de 90 graus para as dobras das cores, o que confere uma característica mais circular. As cores de início e fim também podem ser customizadas à vontade. Se desejar, o leitor pode usar a ferramenta de cores que aparece na barra numérica esquerda para selecionar as cores via paleta dinâmica.
Agora vamos adicionar o conteúdo Java da activity SplashDevMedia, conforme a Listagem 4.
package br.edu.devmedia.splashscreen;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
public class SplashDevMedia extends Activity {
// Timer da splash screen
private static int SPLASH_TIME_OUT = 3000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
new Handler().postDelayed(new Runnable() {
/*
* Exibindo splash com um timer.
*/
@Override
public void run() {
// Esse método será executado sempre que o timer acabar
// E inicia a activity principal
Intent i = new Intent(SplashDevMedia.this,
ActivityPrincipal.class);
startActivity(i);
// Fecha esta activity
finish();
}
}, SPLASH_TIME_OUT);
}
}
No início da classe criamos uma constante que servirá como medidor de tempo, um timer em relação a quanto durará a exibição da splash screen. O valor 3000 (em milissegundos) será convertido em três segundos. Já no método onCreate() criamos um novo objeto Handler que se encarrega de criar esse delay, passando como primeiro argumento a thread que executará em segundo plano enquanto a aplicação inicializa, e como segundo argumento o valor do timeout. Quando o timeout acabar, dentro do método run() de Runnable simplesmente iniciamos a nova activity e encerramos a atual.
Agora vamos criar a activity que receberá o fluxo da anterior. Abra a classe autogerada “ActivityPrincipal” (ou MainActivity) e adicione o código da Listagem 5 à mesma.
package br.edu.devmedia.splashscreen;
import android.app.Activity;
import android.os.Bundle;
public class ActivityPrincipal extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Veja que a única coisa que ela faz é associar o arquivo de layout activity_main ao contexto do Android. Agora dê o build no projeto e execute o mesmo no emulador, e você verá a tela apresentada na Figura 1 do início do artigo.
Para que o exemplo possa funcionar, ao terminar de exibir a splash devemos criar também a página de layout correspondente à primeira tela da aplicação. Vamos criar uma tela simples de login com um botão ao final. Inclua na mesma o código da Listagem 6.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ImageView
android:id="@+id/bgImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/bg_main" />
<LinearLayout
android:id="@+id/formLogin"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Email"
android:textColor="#000000" />
<EditText
android:id="@+id/txtEmail"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="0dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Senha"
android:textColor="#000000" />
<EditText
android:id="@+id/txtPassword"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="0dp"
android:password="true"/>
<Button
android:id="@+id/btnLogin"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="Login" />
</LinearLayout>
</RelativeLayout>
No código é possível observar o uso da imagem via tag no início da implementação. Esse XML traz a representação de uma tela de login, com duas labels (TextView) dois campos de input (EditText) e um botão (Button). Nenhum dos botões terá ações associadas e você pode usar qualquer outra tela para testar o efeito do Splash. O resultado ao reexecutar a aplicação pode ser visto na Figura 2.
Splash Screen ao fazer requisições HTTP
Nesse segundo exemplo precisaremos fazer uso de alguns processamentos assíncronos no Android e a melhor forma de fazer isso é usando a classe nativa AsynTask, que permite sincronizar a execução da tela (GUI) e qualquer outra ação (requisições HTTP, acesso a banco local, diretórios de armazenamento, ou simples regras de negócio) ao mesmo tempo.
A primeira alteração no projeto consiste no arquivo AndroidManifest.xml, que agora precisará de permissão para acesso à Internet. Acesse-o e altere e inclua o seguinte trecho:
<uses-permission android:name="android.permission.INTERNET"/>
Também precisaremos de um recurso externo (Web Service ou URL) para efetuar a conexão HTTP. Para simplificar, usaremos uma URL fixa que contém conteúdo JSON para consumirmos via objetos da API do Android, em vez de criar toda uma estrutura de Web Services do zero. Na seção Links você encontra a URL para isso hospedada no GitHub. Caso prefira, crie a sua própria e acesse-a via função Raw do site.
Trata-se de um JSON simples, com duas informações básicas para exibirmos dentro de um diálogo estilo modal quando a splash terminar:
"site":{
"descricao" : "DevMedia",
"url" : "www.devmedia.com.br"
}
Como a atividade de carregar os dados da URL ocorrerá de forma assíncrona, é importante que passemos essas informações para a próxima tela de alguma forma, logo usaremos os Intents para isso. Você também precisa se certificar de passar os dados somente quando a atividade assíncrona acabar, pois não sabemos quanto tempo isso vai durar. No exemplo anterior definimos um tempo limite de três segundos para, assim, fechar a activity de splash. Dessa vez, esse fechamento terá de ser feito dentro da AsyncTask no método onPostExecut, para garantir que a tela de splash continuará ativa até que todos os dados tenham sido recuperados. Você também pode, opcionalmente, criar um timeout para suas requisições HTTP, assim nos asseguramos de dar alguma resposta ao usuário caso algo de errado aconteça e a resposta não retorne do Web Service/URL.
Para abraçar tudo isso, vamos modificar a activity SplashDevMedia tal como temos na Listagem 7.
private String descricao, url;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
new CarregaDadosAsync().execute();
}
private class CarregaDadosAsync extends AsyncTask<Void, Void, Void> {
@Override
protected void onPreExecute() {super.onPreExecute();}
@Override
protected Void doInBackground(Void... arg0) {
JsonParser jsonParser = new JsonParser();
String json = jsonParser.getJSONFromUrl
("https://raw.githubusercontent.com/DiogoSouza/devmedia/master
/devmedia_splash.json");
if (json != null) {
try {
JSONObject jObj = new JSONObject(json)
.getJSONObject("site");
descricao = jObj.getString("descricao");
url = jObj.getString("url");
} catch (JSONException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
Intent i = new Intent(SplashDevMedia.this,
ActivityPrincipal.class);
i.putExtra("descricao", descricao);
i.putExtra("url", url);
startActivity(i);
finish();
}
}
Para que o exemplo funcione você precisará importar a classe do JsonParser que está contida no projeto original. Baixe-o e adicione a mesma no pacote de activities. Veja o código do onPostExecute para o que dissemos sobre manter o splash ativo enquanto o resultado da requisição assíncrona não retornar.
Agora, para finalizar vamos modificar também a activity principal para receber os dados que setamos no Intent e exibi-los num diálogo de alerta, como mostra o código da Listagem 8.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent i = getIntent();
String descricao = i.getStringExtra("descricao");
String url = i.getStringExtra("url");
AlertDialog.Builder alertDialogBuilder = new
AlertDialog.Builder(ActivityPrincipal.this);
alertDialogBuilder.setTitle("Bem vindo(a)!");
alertDialogBuilder
.setMessage("Dados do site:\nDescricao: "
+ descricao + "\nURL: " + url)
.setCancelable(false)
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface
dialog, int id) {
dialog.cancel();
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
alertDialog.show();
}
}, 3000);
}
Veja que também estamos usando a classe Handler para impor um delay na exibição da modal, mas você pode removê-lo se desejar exibi-la assim que a tela abrir. No exemplo implementamos somente um botão (OK) para fechar a janela, mas fique à vontade para criar mais opções. O resultado pode ser visualizado na Figura 3.
Contudo, essa foi apenas uma pequena demonstração desse tipo de recurso muito comum às aplicações Android. Você pode ainda adicionar mais efeitos através da API de animação nativa do Android ou via bibliotecas de terceiros para deixar o seu app mais profissional. A maioria delas também permite temporizar a exibição, bem como disparar eventos quando a mesma finalizar.
Saiba mais: Curso de Android
Links Úteis
- FluentValidation: Como validar dados em .NET: Neste curso você aprenderá a realizar validações de uma forma simples e eficiente utilizando a biblioteca FluentValidation.
- Bean Validation: Validação de dados em Java: Neste curso você aprenderá a implementar validação com Bean Validation.
Saiba mais sobre Android ;)
- Cursos para Mobile: Aqui você encontra os melhores cursos sobre programação mobile. Aprenda a criar aplicativos mobile nativos e híbridos para Android, iOS e Windows usando Java, Cordova, Xamarin, Ionic e mais.
- Guias de referência Android: O Android é a plataforma oficial para desenvolvimento de aplicativos mobile do Google. Neste guia você encontrará artigos, vídeos e cursos para aprender tudo sobre esse SDK, que domina o mundo dos dispositivos móveis.