25 de jun. de 2014

[Especial] Outra app da Copa do Mundo... Usando JavaFX, FXML, Javascript e CSS

Olá pessoal, essa é uma postagem especial da copa do mundo onde faço uma tradução da minha postagem no blog fxapps.blogspot.com

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)

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;
}

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.

A nossa aplicação final:



Um comentário: