Outra app da Copa do Mundo... Usando JavaFX, FXML, Javascript e CSS
A Copa do mundo Brasil está acontecendo! É empolgante ver pessoas de todas as partes do mundo visitando nosso pais. Hoje eu decidi criar outra app para a Copa do Mundo, usando JavaFX, mas dessa vez eu não irei escrever nenhum código Java.
É uma aplicação muito simples para visualizar todas as partidas da copa do mundo e quando você clica em um jogo, você tem detalhes do mesmo. Eu gastei menos de 3 horas trabalhando no "core" da aplicação e algumas horas mais trabalhando nos detalhes.
Adquirindo os recursos para criar a aplicação
Recursos quer dizer imagens and para adquiri elas eu baixei todas as imagens de bandeiras do site da Fifa. Fiz um pequeno e fácil scrapping. Note que todas as imagens seguem o seguinte padrão de URL: http://img.fifa.com/images/flags/4/{CODE}.png. Então, para baixar todas as bandeiras, fiz o seguinte script python:
import urllib2
codes = open('countries_codes.txt', 'r')
for line in codes:
code = line.replace('\n', '').lower() + '.png'
img_url = 'http://img.fifa.com/images/flags/4/' + code
print img_url
req = urllib2.Request(img_url)
response = urllib2.urlopen(req)
img = response.read()
f = open(code, 'w')
f.write(img)
codes = open('countries_codes.txt', 'r')
for line in codes:
code = line.replace('\n', '').lower() + '.png'
img_url = 'http://img.fifa.com/images/flags/4/' + code
print img_url
req = urllib2.Request(img_url)
response = urllib2.urlopen(req)
img = response.read()
f = open(code, 'w')
f.write(img)
Também utilizei a seguinte imagem como uma inspiração para nossa aplicação:
A view da aplicação
A view foi inteiramente criada no Scene Builder e foi dado estilos e id para os componentes da view, então, do Javascript nós podemos destacar algumas partes de acordo com as informações da copa.
Temos todos os jogos mostrados na aplicação e quando o usuários clica em um jogo, os detalhes do mesmo são mostrados em um painel:
O estilo da aplicação
Para mudarmos a aplicação para termos as cores do Brasil, usamos um simples arquivo CSS que também troca a aparência da partida quando passamos sobre ela:
.root{
-fx-base: CornflowerBlue;
-fx-background: rgb(227,231,239);
}
.TitledPane{
-fx-text-fill: FIREBRICK;
}
#match_details{
-fx-background-color: rgb(177,174,218);
}
.match:hover{
-fx-font: bold 12pt "Calibri";
-fx-effect: dropshadow(three-pass-box , blue, 20, 0.4 , 0 , 0 );
-fx-cursor: hand;
}
-fx-base: CornflowerBlue;
-fx-background: rgb(227,231,239);
}
.TitledPane{
-fx-text-fill: FIREBRICK;
}
#match_details{
-fx-background-color: rgb(177,174,218);
}
.match:hover{
-fx-font: bold 12pt "Calibri";
-fx-effect: dropshadow(three-pass-box , blue, 20, 0.4 , 0 , 0 );
-fx-cursor: hand;
}
A lógica
Como essa é uma aplicação temporal, não precisamos criar classes de modelo para representar os dados do jogo, então usamos somente Javascript para escrever a lógica da aplicação!
Uma boa alma raspou os dados da FIFA e disponibiliza eles em JSON. Nós lemos esse JSON e basicamente preenchemos a view com os dados que nele vem. Notem que para cada partida eu dei um ID(isso foi chato bagario) e do JSON que peguei, eu preencho os dados. Veja:
var matches = downloadMatchesInfo() ... function downloadMatchesInfo(){ print("Trying to download the matches information.........") var out, scanner try{ var urlStream = new java.net.URL(MATCHES_DATA_URL).openStream() scanner = new java.util.Scanner(urlStream, "UTF-8") // TODO: save the latest downloaded JSON }catch(e){ print("Couldn't download the updated information, using a cached one.....") scanner = new java.util.Scanner(new java.io.File(CACHED_DATA_URL), "UTF-8") } scanner.useDelimiter("\\A") out = scanner.next(); scanner.close(); return eval(out); }
Usando a função eval, transformamos a nossa String de resposta em um object Javascript para que possamos acessar os dados JSON diretamente. Para entender melhor, veja como as partidas são preenchidas:
function fillMatch(match){ var viewMatch = $STAGE.scene.lookup("#match_" + match.match_number) if(viewMatch && match.home_team.country){ viewMatch.children[0].image = getImg(match.away_team.code) viewMatch.children[1].text = match.status == "future"? "_": match.home_team.goals; viewMatch.children[3].text = match.status == "future"? "_": match.away_team.goals; viewMatch.children[4].image = getImg(match.home_team.code) } viewMatch.onMouseClicked = function(e){ fillMatchDetails(match) } } function getImg(code){ var imgUrl = code?"./images/teams/" + code.toLowerCase() + ".png":"./images/no_team.png" if(!imgCache[imgUrl]) imgCache[imgUrl] = new javafx.scene.image.Image(new java.io.FileInputStream(imgUrl)) return imgCache[imgUrl] }
Você pode ver no código acima como é fácil usar estruturas do Javascript em um código que lida com bibliotecas Java(veja a variável de cache de imagens chamada imgCache). Notem que quando o usuário clica em uma partida, registramos um listener para preencher os dados da partida em um painel central:
function fillMatchDetails(match){ var s = $STAGE.scene; detailsTransition.playFromStart() var notPlayedScore = match.status == "completed"?"0":"_" s.lookup("#match_home_team").image = getImg(match.home_team.code) s.lookup("#match_away_team").image = getImg(match.away_team.code) s.lookup("#match_home_score").text = match.home_team.goals?match.home_team.goals:notPlayedScore s.lookup("#match_away_score").text = match.away_team.goals?match.away_team.goals:notPlayedScore s.lookup("#match_status").text = "Match " + match.status s.lookup("#match_time").text = match.datetime.substring(0, 16) s.lookup("#match_location").text = match.location }
Por fim, vejam que invés de injetar os componentes em uma classe controller, decidimos pegar os elementos por ID usando o método lookup da Scene.
Rodando a aplicação
A aplicação não é compilada, é interpretada! Para rodar ela, você simplesmente precisa ter Java 8 instalado e no seu PATH assim você pode rodar a aplicação jjs para rodar a aplicação. Como o código está no github, você pode clonar it e já executar:
$ git clone https://github.com/jesuino/another-world-cup-app.git
$ cd another-world-cup-app
$ jjs -fx app.js
Você também pode editar o arquivo run.sh para configurar a variável JAVA_HOME e fazer o arquivo executável para rodar nossa app:
$ chmod +x run.sh
$ ./run.sh
Se você estiver no windows, minha namorada Luana testou a aplicação nesse "sistema operacional" e ela simplesmente criou um arquivo chamado run.bat com o seguinte conteúdo:
"C:\Program Files\Java\jre8\bin\jjs" -fx app.js
Note que você deve mudar o conteúdo desse arquivo de acordo com sua instalação.
Conclusão
Como você pode ver, é muito fácil criar aplicações JavaFX e não precisamos sequer escrever código Java! Usamos Javascript para ler o JSON e interagir com a view, o que se mostrou uma ótima abordagem, já que não precisamos usar nenhuma biblioteca de terceiros para lidar com o "parsing" de JSON.
Também ensinei minha namorada como rodar e mudar a aparência da aplicação e ela não é uma programadora Java. Isso mostra como aplicações JavaFX são acessíveis a todos os programadores.
Também ensinei minha namorada como rodar e mudar a aparência da aplicação e ela não é uma programadora Java. Isso mostra como aplicações JavaFX são acessíveis a todos os programadores.