Criando layout sem utilizar XML

Recentemente alguns leitores me fizeram uma pergunta ao ler os tutorias de criação de uma aplicação android: “É possível criar uma aplicação Android sem utilizar XML?” E eu lhes respondo: “Sim!”. Se é recomendável ou não vai do gosto e da necessidade de cada um.

Nesse post vou além de demonstrar como criar uma pequena aplicação sem a utilização de nenhum .xml de layout demostrar como criar componentes gráficos dinamicamente.

Certo de que você já tem seu ambiente preparado no Eclipse, vamos começar criando nossa aplicação no Eclipse em File > New > Android Project. Dê um nome para sua aplicação como AndroidSemXML e clique em Next. Agora selecione a versão da SDK escolha Android 2.1 mesmo, pois não precisaremos de muitos recursos para rodar essa aplicação. Na próxima tela preencha com:

Agora temos nossa aplicação criada no nosso Eclipse:

 

Como não utilizaremos XML para definição de layout de nossa aplicação podemos apagar o arquivo main.xml presente na pasta res/layout de nossa aplicação e agora substituímos o método OnCreate na nossa Activity por:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    final LinearLayout linearLayout = new LinearLayout(this);
    linearLayout.setOrientation(LinearLayout.VERTICAL);
 
    final ScrollView scrollView = new ScrollView(this);
    scrollView.addView(linearLayout);
 
    final TextView textView = new TextView(this);
    textView.setText("TextView Dinâmico!");
    textView.setBackgroundColor(Color.GREEN);
    textView.setTextSize(15,5);
    linearLayout.addView(textView);
 
    final EditText editText = new EditText(this);
    editText.setText("");
    editText.setHeight(25);
    linearLayout.addView(editText);
 
    Button button = new Button(this);
    button.setText("Criar novo botão!");
    button.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            textView.setText("Criando mais um botão...");
            Button dinamicButton = new Button(AndroidSemXMLActivity.this);
            dinamicButton.setText(editText.getText());
            linearLayout.addView(dinamicButton);
            textView.setText("Novo botão criado!");
        }
    });
    linearLayout.addView(button);
 
    this.setContentView(scrollView);
}

O que fizemos?

1. Criamos um LinearLayout que define como será o layout de nossa aplicação e setamos sua orientação para vertical.

final LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);

2. Criamos um ScrollView para que nossa tela tivesse rolagem caso a quantidade de elementos excedesse o tamanho da tela. E adicionamos o nosso LinearLayout criado anteriormente a esse ScrollView.

scrollView.addView(linearLayout);

3. Criamos um TextView, um EditView e um Button e adicionamos ao nosso layout:

linearLayout.addView(textView);
linearLayout.addView(editText);
linearLayout.addView(button);

4. No final adicionamos nossa ScrollView a nossa Activity:

this.setContentView(scrollView);

Observe o que fiz quando criei nosso button

    Button button = new Button(this);
    button.setText("Criar novo botão!");
    button.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            textView.setText("Criando mais um botão...");
            Button dinamicButton = new Button(AndroidSemXMLActivity.this);
            dinamicButton.setText(editText.getText());
            linearLayout.addView(dinamicButton);
            textView.setText("Novo botão criado!");
        }
    });

O que isso faz? Cria botões dinâmicamente! A cada clique desse botão ele criará um novo botão com o texto contido no nosso Edit Text.

Agora rode sua aplicação e veja como ficou!

Espero tê-los ajudado! Obrigado a todos e até a próxima!

, , , , , , ,

Nenhum comentário.

Publicidade nas apps com AdMob

Uma boa maneira de rentabilizar apps, principalmente no Brasil onde as pessoas tendem a baixar somente o que é gratuito, é através da exibição de publicidade. Também vejo os banners como uma ótima estratégia de permuta com clientes. Dependendo da popularidade que é projetada para a app, podemos negociar um espaço para publicidade no lugar do pagamento de parte, ou mesmo todo o projeto. Aí vai da tolerancia a riscos de cada um.

Neste post mostrarei como implementar um banner que será gerenciado pela rede AdMob da Google.

1. Registro no AdMob

O primeiro passo é registrar-se no site do AdMob ou mesmo logar com a própria conta do Google e em seguida preencher as informações para registro da conta.

2. Definir informações para pagamento

Supondo que você escolheu o idioma Português no registro, clique no botão Adicione seu primeiro site. Claro que você pretende receber o dinheiro arrecadado nas campanhas, então o sistema o redicionanará para o formulário de  Detalhes do pagamento. Aqui tive dúvidas sobre o que seria este Código SWIFT, mas procurando no site do banco, encontrei sem problemas. Em último caso, ligue para o SAC do bando que você descobre.

3. Adicionar SDK

No passo seguinte, faça download do SDK do AdMod e o adicione ao seu projeto.

4. Adicionar Frameworks

Adicione ao seu projeto os seguintes frameworks:

  • AudioToolbox
  • MessageUI
  • SystemConfiguration
  • CoreGraphics

5. Definir chave

Defina a constante MY_BANNER_UNIT_ID com o Id do Editor da sua aplicação. Para obter-lo, no site do AdMob, clique na aba Sites e aplicativos e na opção de mesmo nome. Você verá uma lista com suas aplicações e ao passar o mouse sobre a linha da app, surgirá as opções Relatórios e Gerenciar configurações. Clique na segunda opção. Veja o ID do editor logo abaixo do nome da aplicação.

#define MY_BANNER_UNIT_ID @"<id do editor>"

6. Configure o aplicativo

Na mesma tela, clique na aba Configurações do aplicativo e defina suas preferencias para o banner. Marque a opção para usar o Google AdSense para melhorar a taxa de preenchimento.

7. Implementação

Chegou a hora de implementar de fato o banner. É muito simples. No header da view em que deseja exibi-lo, importe o arquivo GADBannerView.h e crie objeto deste tipo na classe.

#import <UIKit/UIKit.h>

#import "GADBannerView.h"

@interface ViewController : UIViewController {

    GADBannerView *banner;

}

@end

No arquivo de implementação, o banner será adicionado à view no método viewDidLoad.

As constantes com o prefixo GAD_SIZE definem o tamanho do banner. Existem as seguintes opções: 320×50, 300×250, 468×60 e 728×90. A primeira é recomendada para iPhone/iPod e as demais para iPad.

- (void)viewDidLoad
{
    [super viewDidLoad];

    banner = [[GADBannerView alloc]
                   initWithFrame:CGRectMake(10.0,
                                            self.view.frame.size.height -
                                            GAD_SIZE_320x50.height,
                                            GAD_SIZE_320x50.width,
                                            GAD_SIZE_320x50.height)];

    banner.adUnitID = MY_BANNER_UNIT_ID;

    banner.rootViewController = self;
    [self.view addSubview:banner];

    [banner loadRequest:[GADRequest request]];
}

Rode a aplicação.

8. Resultado

9. Conclusão

É extremamente simples implementar o sistema de banners com AdMob. Seu sistema de gerenciamento lhe permite monitorar e gerar relatórios dos resultados. Não deixe de considerar esta possibilidade de obter ganhos relevantes mesmo com sua app free.

10. Download

Caso tenha ficado alguma dúvida, baixe o código deste projeto de teste e o utilize como template.

Obrigado e até a próxima!

, , ,

Nenhum comentário.

Overview do framework Corona

Por quê?

Logo no início da nossa aventura no mundo mobile, nos deparamos com o desafio de desenvolver uma aplicação, com versões para Android e iOS, para a Funceme (Fundação Cearense de Meteorologia e Recursos Hídricos).

O desafio era relativamente complexo para iniciantes. A aplicação deveria exibir informações fornecidas em tempo real através de um serviço web via JSON. Também teríamos duas telas com animações de imagens de satélite e radar.

Neste momento estávamos ensaiando os primeiros “Hello Words” nas respectivas SDK’s nativas de Android e iOS, mas deveríamos mostrar resultados em pouco tempo. A saída foi buscar um framework com curta curva de aprendizado e, de preferência, capaz de gerar aplicações para as duas plataformas a partir do mesmo código escrito.

Após uma rápida googleada, encontramos o Corona. Nos pareceu muito interessante por atender à necessidade de compilar para Android e iOS a partir de um código escrito em Lua, uma linguagem super robusta de de fácil aprendizado. Para quem não conhece esta linguagem nascida na PUC-Rio, pode obter maiores informações no site oficial.

Pra quê?

Se auto-proclamando com a plataforma número 1 na área, o grande apelo do Corona é o desenvolvimento de jogos. As API’s disponibilizadas utilizam como base OpenGL, OpenAL, Box2D e Lua, prometendo performance próxima à nativa nos games desenvolvidos. Também é possível ter acesso a recursos nativos como multitouch, GPS, acelerômetro, giroscópio, camera, Google Maps, WebKit, keyboards e muito mais. Redes sociais também são contempladas com soluções simples para integração com as API’s do Twitter, Facebook, FourSquare e outras.

Quanto?

Existem dois tipos de licença no Corona. A Indie e a Pro. A primeira custa U$199.00/ano e permite compilar para apenas uma das duas plataformas: Android ou iOS. A segunda lhe permite compilar para ambas as plataformas e custa U$349.00/ano.

Como?

Explorando o próprio site oficial do Corona, você encontrará o guia de referência das API’s, documentação de todas as features, templates, vídeos, fórum e até um blog. Tudo para dar suporte aos desenvolvedores.
Vou mostrar alguns trechos do projeto Funceme Tempo para que possam ter uma noção da simplicidade do Corona, já que seria impossível mostrar tudo o que o framework pode fazer em um simples post.

Configurações

No arquivo config.lua, definimos algumas diretrizes do projeto, como dimensões e o modo que deve ser aplicado a escala.

application = {
    content = {
	width = 640,
	height = 960,
	scale = "zoomEven"
    },
}

Main do projeto

O Arquivo main.lua basicamente cria a tabbar e trata o evento de toque nas abas para que sejam carregadas as views correspondentes. Os arquivos ui.lua e viewcontroller.lua, adicionado no main, são helpers de Button e Tabbar respectivamente, fornecidos pela própria Ansca, desenvolvedora do Corona.

local ui = require("ui")
local viewController = require("viewcontroller")

local mainView, currentScreen, tabBar

function loadScreen(newScreen)

	 if currentScreen then
                currentScreen:cleanUp()
     end

    currentScreen = require(newScreen).new()
    tabView:insert(currentScreen)

    return true
end

local function showScreen(event)
	local t = event.target
	local phase = event.phase

	if phase == "ended" then
		if t.id == 1 then
			loadScreen("previsao")
		elseif t.id == 2 then
			loadScreen("tempoagora")
		elseif t.id == 3 then
			loadScreen("radar")
		elseif t.id == 4 then
			loadScreen("satelite")
		end
		tabBar.selected(t)
	end
	return true
end

local function init()
	--Create a group that contains the entire screen and tab bar
	mainView = display.newGroup()	

	--Create a group that contains the screens beneath the tab bar
	tabView = display.newGroup()
	mainView:insert(tabView)

	loadScreen("previsao")

	local tabBar = display.newImage("images/tab_bar2.png")
	tabBar.y=910
	tabBar.x=320
	tabBar.yScale=1.4
	tabBar.xScale=1.3
	mainView:insert(tabBar)

	tabBar = viewController.newTabBar{
			background = "images/tab_bar.png",  --tab bar background
			tabs = {"", "", "", ""},            --names to appear under each tab icon
			onRelease = showScreen              --function to execute when pressed
		}
	mainView:insert(tabBar)
	tabBar.selected()
	return true
end

--Start the program!
init()

Criação de um botão

function criarButtonPrevisaoCeara()

	buttonPrevisaoCearaEvento=function( event )
		buttonCidades:removeSelf(buttonCidades)
		buttonPrevisaoCeara:removeSelf(buttonPrevisaoCeara)
		previsaoCeara.new()
	end

	buttonPrevisaoCeara=ui.newButton{
		default="images/btn_previsao_ceara.png",
		over="images/btn_previsao_ceara_over.png",
		onRelease=buttonPrevisaoCearaEvento,
		text="Previsão Ceará",
		emboss=true
	}
	buttonPrevisaoCeara.xScale=1.30
	buttonPrevisaoCeara.yScale=1.30
	buttonPrevisaoCeara.x=LARGURA*0.76
	buttonPrevisaoCeara.y=ALTURA*0.28

	gPrevisao:insert(buttonPrevisaoCeara)
end

Solicitação HTTP e tratamento de JSON de resposta

function request()
	network.request("http://endereco/servico/parametros/","GET", listener)
end

function listener(event)

	local erro
	local valorTempAtualMin, valorTempAtualMax, valorTempAtualMed

	if(event.isError)
	then
		conexaoFalha.desconectado()
		erro="Erro na Rede"
		print(erro)
	else
		response=json:decode(event.response)

		for k,v in pairs (response) do

			if k=="dataprevisao" then
				previsaoGerada=criarTextoThin(23)
				previsaoGerada.x=LARGURA*0.60
				previsaoGerada.y=ALTURA*0.14
				previsaoGerada.text="Gerada "..v
				cidadesImagensTempo:insert(previsaoGerada)
			elseif k=="diadaprevisao" then
				previsaoDia=criarTextoThin(25)
				previsaoDia.x=LARGURA*0.60
				previsaoDia.y=ALTURA*0.11
				previsaoDia.text="Previsão para o dia "..v
				cidadesImagensTempo:insert(previsaoDia)
			end
		end
		gPrevisaoCeara:insert(cidadesImagensTempo)
	end
end

Animação por sobreposição de imagens

local function networkListener( event )
    if ( event.isError ) then
        conexaoFalha.desconectado()
        print ( "Network error - download failed" )
    else
	function animacao()
            local anim=newObject(imagens, 501*1.30, 522*1.495, LARGURA*0.5, ALTURA*0.5, 0.4, 1, 1, gSatelite)
            anim:startAnimation()
            loader.cancelLoading()
        end
        timer.performWithDelay(2000, animacao)
    end
end
if c=="img" then
    network.download("http://endereco/servico/parametros/"..d, "GET", networkListener, d, system.TemporaryDirectory)
    imagens[cont2]=d
    cont2=cont2+1
end

Criação de Table View

function listaCidades()
	local data = {}

	display.setStatusBar( display.HiddenStatusBar ) 

	local lCidades= display.newGroup()
	local screenOffsetW, screenOffsetH = display.contentWidth -  display.viewableContentWidth, display.contentHeight - display.viewableContentHeight
	local myList, backBtn, detailScreenText, navBar, navHeader

	local backgroundCidades = display.newRect(0, 0, display.contentWidth, display.contentHeight)
	backgroundCidades:setFillColor(77, 77, 77)
	lCidades:insert(backgroundCidades)

	local detailScreen = display.newGroup()

	local detailBg = display.newRect(0,0,display.contentWidth,display.contentHeight-display.screenOriginY)
	detailBg:setFillColor(255,255,255)
	detailScreen:insert(detailBg)

	detailScreenText = display.newText("You tapped item", 0, 0, native.systemFontBold, 24)
	detailScreenText:setTextColor(0, 0, 0)
	detailScreenText.x = math.floor(display.contentWidth/2)
	detailScreenText.y = math.floor(display.contentHeight/2)
	detailScreen:insert(detailScreenText)
	detailScreen.x = display.contentWidth

	local listButtonRelease = function ( event )

		self = event.target
		local id = self.id

		lCidades:removeSelf(lCidades)

		resetarValores(barOpcoes)
		codigo=data[self.id].codigo
		request(codigo)

	end

	local backBtnRelease = function ( event )
		lCidades:removeSelf(lCidades)
		resetarValores(barOpcoes)
		request(codigo)
	end

        local buscaCidades=function ( event )

	     local erro
	     local topBoundary = display.screenOriginY + 90
             local bottomBoundary = display.screenOriginY + 0

	     if(event.isError) then
		  conexaoFalha.desconectado()
		  erro="ERRO NA REDE"
		  print(erro)
	     else
		  response=json:decode(event.response)

		  local cont=1

		  for k,v in pairs (response) do

			for t,j in pairs (v) do
				data[cont] = {}
				for c,d in pairs (j) do
					print("c="..d)
					if c=="cod" then
						data[cont].codigo = d
					elseif c=="nome" then
						data[cont].cidade = d
					elseif c=="cat" then
						data[cont].cat = d
					end
				end
				cont=cont+1
			end
		end
		--specify the order for the groups in each category

		local headers = { "A", "B", "C","D", "E", "F", "G", "H", "I", "J", "L", "H", ...  }

		-- Create a list with header titles
		myList = tableView.newList{
		data=data,
		default="images/listItemBg.png",
		onRelease=listButtonRelease,
		top=topBoundary,
		bottom=bottomBoundary,
		cat="cat",
		order=headers,
		categoryBackground="images/catBg.png",
		backgroundColor={ 243, 251, 247 },
		callback=function(item)
		local t = display.newText(item.cidade, 0, 0, FONTE, 25)
			t:setTextColor(0, 0, 0)
			t.x = math.floor(t.width/2) + 12
			t.y = 40
			return t
		end
		}

	lCidades:insert(myList)
	navBar = display.newImage("images/navBar.png", 0, 0, true)
	lCidades:insert(navBar)

	navHeader = display.newText("Selecione uma cidade", 0, 0, FONTE, 30)
	navHeader:setTextColor(255, 255, 255)
	navHeader.x = display.contentWidth*.5
	navHeader.y = navBar.y
	lCidades:insert(navHeader)

	backBtn = ui.newButton{
		default = "images/backButton.png",
		over = "images/backButton_over.png",
		onRelease = backBtnRelease
	}
	backBtn.x =LARGURA*0.1
	backBtn.y = navBar.y
	--backBtn.alpha = 2
	lCidades:insert(backBtn)

	end
    end
    network.request("http://endereco/servico/parametros","GET", buscaCidades)
    return lCidades
end

Conclusões

A adoção do Corona como solução para o nosso problema acabou por se mostrar uma decisão acertada para o momento, mas não como uma solução definitiva.

Conseguimos concluir o projeto em um tempo razoável, e tivemos um custo aceitável. Também tivemos problemas, claro. Os builds para Android no começo do projeto eram instáveis ou sequer rodavam quando instalávamos no dispositivo. O que não acontecia no iOS. Esse problema nos levou a tomar a decisão de iniciar imediatamente o desenvolvimento usando o SDK nativo do Android, já que tínhamos conhecimentos em Java e mais pessoas que poderiam ajudar, se fosse esse caminho tomado.

Não tivemos problema algum ao passar pela inspeção da Apple na hora de publicar a aplicação na App Store, que foi até bem ágil. Em menos de 10 dias a app estava publicada.

O grande problema que vi ao adotar o Corona, e que seria inerente a qualquer outro framework, é a limitação. É impossível reproduzir 100% da experiência de uma app nativa, principalmente às da plataforma iOS. Quando lidamos com clientes familiarizados com os produtos da maçã, temos que esperar o máximo de expectativa com relação à interface da aplicação. É frustante explicar para seu cliente que aquele efeito de transição, ou aquela atalho de gesture não é possível de ser implementado (não com um esforço aceitável para aquele projeto, ou até de jeito algum). Outro contra que vejo é que perdemos um grande aliado da hora do desenvolvimento propriamente dito. O XCode . A poderosa IDE e seus complementos como Interface Builder e o Instruments são inútes pra quem está desenvolvendo com o Corona. O próprio Eclipse, ou Net Beans, faz muita falta quando nos vemos editando arquivos .lua em editores bem mais modestos.

Enfim, minha conclusão é a de que se você pretende levar o desenvolvimento mobile a sério, investindo tempo e dinheiro para se tornar capaz de atender à crescente demanda do mercado nessa àrea, e estando apto a encarar projetos de níveis de complexidade mais elevados, não há como se esconder atrás de frameworks. Em algum momento eles vão te deixar limitado e você correrá um grande risco que fracassar no projeto. Encare as SDKs nativas! Saiba diferenciar o que as plataformas têm de diferencial e explorar suas potencialidades. Não tem tempo ou não quer quebrar a cabeça pra aprender várias ao mesmo tempo? Escolha uma, se dedique e se torne muito bom nela. Quando aparecerem projetos que precisam ser desenvolvidos para as outras plataformas, procure parcerias com desenvolvedores dessas plataformas e atenda seu cliente da melhor maneira possível.

Agora, se mobile não é, e nem pretende que seja, o seu foco, e quer tocar um projeto de pequeno ou médio porte, o Corona pode ser uma grande alternativa sim. Principalmente para desenvolvimento de jogos.

A app Funceme Tempo está disponível para download na App Store e Android Market. Por enquanto a versão iOS é a desenvolvida com Corona, mas em breve a substituiremos por uma versão nativa. Agradeço se puderem baixar e avaliar, criticar, comentar, enfim.

Espero que gostem do post e que o próximo não demore tanto :) . Abraços.

, , ,

6 Comentários

App iPhone em 10 minutos

Em um vídeo de aproximadamente 10 minutos, fiz uma demonstração de desenvolvimento de uma aplicação bastante simplificada para calcular o IMC.

Apesar de não ser uma aplicação bem acabada, ficou interessante para dar uma pincelada em algumas dúvidas que muitos iniciantes têm para desenvolver seus primeiros projetos.

Download do projeto aqui.

, ,

7 Comentários

Tutorial para criar um painel de preferências de uma app iOS

Algumas aplicações precisam de configurações ou oferecem opções que devem ser definidas antes desta ser inicializada. No navegador Safari por exemplo, pode ser escolhido o buscador padrão para ser utilizado, habilitar/desabilitar o preenchimento automático ou ou a barra de favoritos etc.

Para isso, o iOS disponibiliza um mecanismo relativamente simples para criar um menu painel de preferências para uma aplicação. Deve ser adicionado ao projeto um componente Settings Bundle. Ele fica localizado na aba File Template library, veja na imagem abaixo.

Arraste o componente para dentro do seu projeto no Project Navigator. Deixe o nome dos componentes como Settings mesmo. Quando alterei esse nome não funcionou. Será criado um arquivo Root.plist, onde serão criados os parâmetros da app.

Inicialmente são criados três campos agrupados. Um Text Fild, um Toogle Switch e um Slider. Você pode customizar esses ítens, deletar alguns deles ou até criar novos clicando nos botões que aparecem na linha Preference Itens que aparecem quando é colocado o mouse sobre ela.

Veja a documentação para maiores detalhes.

Depois de definir seus campos de configuração, rode no simulador e veja que agora aparece sua aplicação nos Settings.

O último passo é “resgatar” essas configurações via código na sua aplicação. Veja um exemplo:

NSString *nome = [[NSUserDefaults standardUserDefaults] objectForKey:@"name_preference"];

As preferências podem ser acessadas em qualquer ponto do seu código e usadas conforme sua necessidade.

É importante salientar que as informações que entram para o painel de preferências de sua aplicação devem ser escolhidas com critério. Coloque apenas parâmetros que são determinantes para o comportamento de sua aplicação e que seja realmente necessário que sejam definidos antes da aplicação ser inicializada.

Essa foi a dica da semana do MobileIn. Até a próxima!

, , , , ,

Nenhum comentário.