Ressentez le passage du temps même à Java

Je sens le passage du temps avec Emacs-Qiita J'ai senti que l'idée était très bonne, mais malheureusement je n'étais pas un utilisateur d'Emacs, alors quand Je ne pouvais pas sentir le flux de.

Afin de ressentir moi-même le passage du temps, j'ai créé une application de type accessoire de bureau avec le Java familier. J'ai reçu la permission de l'auteur original, @zk_phi, de le publier, je voudrais donc le présenter ici.

littlesky.gif

Installation

Vous pouvez obtenir le fichier jar pré-construit (littlesky.jar) depuis ici.

Le code source est posté sur GitHub> https://github.com/opengl-8080/little-sky

Environnement d'exploitation

OS Nous avons confirmé que cela fonctionne sur Windows 10.

Je n'ai pas d'environnement Mac sous la main, donc je ne sais pas si cela fonctionnera sur un Mac. Je ne vais rien écrire qui dépend du système d'exploitation, mais si cela ne fonctionne pas sur votre Mac, veuillez faire une pull request.

JRE Il devrait fonctionner sur Java 8u40 et supérieur (car vous utilisez Dialog) .. J'ai vu que cela fonctionnait également sur Java 9.

Commencez

$ java -jar littlesky.jar

Le fichier de configuration est enregistré dans le répertoire courant lors de l'exécution (littlesky.xml).

Le deuxième démarrage et les suivants font référence au fichier de paramètres dans le répertoire en cours. Si vous souhaitez modifier l'emplacement de démarrage, déplacez également le fichier de configuration.

Configuration au premier démarrage

Au premier démarrage, une boîte de dialogue de saisie des informations de localisation s'affiche.

littlesky.jpg

Entrez la latitude («Latitude») et la longitude («Longitude»). (Utilisé pour obtenir les heures de lever / coucher du soleil et les informations météorologiques)

Description de l'écran

littlesky.jpg

Comme pour l'original, l'icône du ciel (icône vide), l'heure et les informations de température s'affichent. L'icône du ciel montre la phase lunaire lorsqu'il fait beau et le temps lorsqu'il pleut ou qu'il neige.

La couleur de fond change en fonction du temps.

Menu contextuel

littlesky.jpg

Faites un clic droit sur l'heure pour afficher le menu contextuel. Vous pouvez fermer l'application et modifier les paramètres.

Utilisez la fonction météo

Comme pour l'original, vous pouvez utiliser la fonction météo en utilisant OpenWeatherMap.

Obtenez la clé d'API OpenWeatherMap

Obtenez la clé API d'OpenWeatherMap.

La clé API peut être obtenue en enregistrant un compte avec OpenWeatherMap. Si vous recherchez "Clé API Open Weather Map", vous trouverez diverses informations.

Définir la clé API

Sélectionnez Options dans le menu contextuel pour afficher un formulaire de saisie des clés API.

littlesky.jpg

Entrez la clé API obtenue dans [Clé API] et cliquez sur le bouton [Enregistrer] pour enregistrer les paramètres.

Activer la fonction météo

littlesky.jpg

Après avoir entré la clé API, vous pourrez sélectionner Service météo> Démarrer dans le menu contextuel. Sélectionnez ceci pour activer la fonction météo.

Une fois toutes les 15 minutes, les informations sur la météo, le volume des nuages et la température seront acquises en fonction des informations de position définies et reflétées sur l'écran.

Une fois que vous avez défini la clé API, la fonction météo démarre automatiquement la prochaine fois que vous démarrez l'application.

Si vous souhaitez arrêter la fonction météo, sélectionnez Service météo> Arrêter dans le menu contextuel ou videz la clé API et enregistrez.

Paramètres du proxy

Dans un environnement où un proxy est requis pour accéder à Internet, il est nécessaire de définir le proxy.

Ouvrez la boîte de dialogue des paramètres avec Options dans le menu contextuel.

littlesky.jpg

Dans Proxy HTTP, entrez les paramètres du proxy.

Si aucun port n'est entré, «80» est utilisé par défaut. Saisissez le nom d'utilisateur et le mot de passe uniquement si le proxy requiert une authentification.

Paramètres d'affichage

Vous pouvez modifier les paramètres d'affichage depuis [Affichage] dans le menu contextuel.

littlesky.jpg

Amenez toujours au premier plan

Si vous cochez [Toujours en haut], la fenêtre sera toujours au premier plan du bureau. La valeur par défaut est désactivée.

Afficher / masquer les secondes

En commutant la vérification de [Afficher les secondes], vous pouvez afficher / masquer les secondes de temps. La valeur par défaut est activée.

Afficher / masquer la température

En commutant le contrôle [Afficher la température], vous pouvez afficher ou masquer la température. La valeur par défaut est activée.

Afficher / masquer l'icône vide

En commutant la vérification de [Afficher l'icône d'état du ciel], vous pouvez changer l'affichage / non-affichage de l'icône du ciel (phase mensuelle / icône météo). La valeur par défaut est activée.

Ce que j'ai appris

J'ai été signalé par Bukome, et j'ai trouvé que c'était un peu mauvais, donc je suis désolé de l'ajouter plus tard, mais j'aimerais résumer ce que j'ai appris en faisant cette application.

Créer une fenêtre sans cadre de fenêtre

package sample.javafx;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class NoFrameWindow extends Application {

    public static void main(String[] args) {
        launch(NoFrameWindow.class, args);
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        Pane pane = new Pane();
        pane.setPrefWidth(200);
        pane.setPrefHeight(100);
        pane.setStyle("-fx-background-radius: 50; -fx-background-color: yellow;");

        Scene scene = new Scene(pane);
        scene.setFill(Color.TRANSPARENT);
        
        primaryStage.initStyle(StageStyle.TRANSPARENT);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

** Résultat d'exécution **

javafx.jpg

La description

Définir l'arrière-plan par programmation


import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.paint.Color;

...

    BackgroundFill backgroundFill = new BackgroundFill(Color.YELLOW, new CornerRadii(50), null);
    Background background = new Background(backgroundFill);
    pane.setBackground(background);

Autoriser le glissement des fenêtres

Si vous supprimez le cadre de la fenêtre, vous ne pouvez pas faire glisser la fenêtre telle quelle. Cette mise en œuvre vous oblige à faire de votre mieux par vous-même.

Il y a des questions similaires lors de la recherche de Stackoverflow, et la méthode d'implémentation est également introduite, je vais donc l'utiliser telle quelle.

Moving an undecorated stage in javafx 2 - Stack Overflow

package sample.javafx;

...

public class NoFrameWindow extends Application {

    ...
    
    private double mouseX;
    private double mouseY;
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        Pane pane = new Pane();
        ...
        
        pane.setOnMousePressed(e -> {
            this.mouseX = primaryStage.getX() - e.getScreenX();
            this.mouseY = primaryStage.getY() - e.getScreenY();
        });
        pane.setOnMouseDragged(e -> {
            primaryStage.setX(e.getScreenX() + this.mouseX);
            primaryStage.setY(e.getScreenY() + this.mouseY);
        });

        ...
    }
}

** Résultat d'exécution **

javafx.gif

Interpolation de couleur entre deux couleurs

Quant à la couleur de fond du ciel, la couleur de fond n'est décidée que pour certains moments clés (en référence à la méthode de mise en œuvre d'origine). La couleur entre un certain temps clé A et un temps clé B interpole la couleur entre les deux couleurs A et B.

L'interpolation de couleur entre deux couleurs est directement appliquée à la classe Color, qui est la classe de couleurs JavaFX, car c'est [interpolate (Color, double)](https://docs.oracle.com/javase/jp/8/javafx/ Il y avait une méthode appelée api / javafx / scene / paint / Color.html # interpolate-javafx.scene.paint.Color-double-), et je l'ai utilisée.

ʻInterpolate () calcule la couleur à la position du rapport (valeur de" 0.0 "à" 1.0 ") spécifié par le deuxième argument de la couleur qui devient le récepteur à la couleur spécifiée par le premier argument. revenir. Par exemple, color1.interporate (color2, 0.5)calculera et retournera une valeur qui est exactement entrecolor1 et color2 ( 0.5`).

Le rapport est compris entre l'heure définie pour chaque clé et l'heure actuelle [Durée] de l'API Date & Time (https://docs.oracle.com/javase/jp/8/docs/api/java/time/Duration.html) Il a été calculé en utilisant. L'implémentation spécifique se trouve dans la classe SkyColorGradation.

Implémentation utilisant les propriétés JavaFX

Récemment [j'ai étudié JavaFX dur](https://qiita.com/opengl-8080/items/51bef25aa05ecd929a3d#javafx-%E3%83%97%E3%83%AD%E3%83%91%E3%83% 86% E3% 82% A3) Dans cette implémentation, j'ai activement utilisé les propriétés JavaFX que j'avais apprises à l'époque.

Profitant du fait que le modèle d'observateur peut être implémenté simplement en utilisant la propriété JavaFX, la configuration globale ressemble à ↓.

javafx.png

Par exemple, l'affichage de l'heure est le suivant.

model

ClockBase.java


package littlesky.model.clock;

import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;

import java.time.LocalDate;
import java.time.LocalTime;

public class ClockBase implements Clock {

    protected final ReadOnlyObjectWrapper<LocalDate> date = new ReadOnlyObjectWrapper<>();
    protected final ReadOnlyObjectWrapper<LocalTime> time = new ReadOnlyObjectWrapper<>();

    @Override
    public LocalDate getDate() {
        return this.date.get();
    }

    @Override
    public LocalTime getTime() {
        return this.time.get();
    }

    @Override
    public ReadOnlyObjectProperty<LocalDate> dateProperty() {
        return this.date.getReadOnlyProperty();
    }

    @Override
    public ReadOnlyObjectProperty<LocalTime> timeProperty() {
        return this.time.getReadOnlyProperty();
    }
}

ReadTimeClock.java


package littlesky.model.clock;

import javafx.application.Platform;

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class RealTimeClock extends ClockBase {
    
    public RealTimeClock() {
        this.updateDateTime();
    }
    
    public void start() {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor((runnable) -> {
            Thread thread = new Thread(runnable);
            thread.setDaemon(true);
            return thread;
        });
        
        executor.scheduleAtFixedRate(() -> {
            Platform.runLater(this::updateDateTime);
        }, 0, 500, TimeUnit.MILLISECONDS);
    }
    
    private void updateDateTime() {
        LocalDateTime now = LocalDateTime.now();
        if (this.getDate() == null || !this.getDate().equals(now.toLocalDate())) {
            this.date.set(now.toLocalDate());
        }
        this.time.set(now.toLocalTime());
    }
}

Cette classe compte l'heure actuelle. Il génère un thread distinct du thread d'interface utilisateur et compte l'heure actuelle à des intervalles de 500 millisecondes.

Pour l'heure actuelle, la date («date») et l'heure («heure») sont divulguées à l'extérieur en utilisant respectivement «ReadOnlyObjectProperty».

ClockBase.java


    protected final ReadOnlyObjectWrapper<LocalDate> date = new ReadOnlyObjectWrapper<>();
    protected final ReadOnlyObjectWrapper<LocalTime> time = new ReadOnlyObjectWrapper<>();

    ...

    @Override
    public ReadOnlyObjectProperty<LocalDate> dateProperty() {
        return this.date.getReadOnlyProperty();
    }

    @Override
    public ReadOnlyObjectProperty<LocalTime> timeProperty() {
        return this.time.getReadOnlyProperty();
    }

ReadOnlyObjectWrapper est une classe qui peut être utilisée pour exposer des propriétés en lecture seule au monde extérieur. Vous pouvez l'utiliser pour empêcher la réécriture de la valeur depuis l'extérieur du modèle.

Une chose à noter est que la valeur est mise à jour dans un thread distinct du thread de l'interface utilisateur, vous devez donc toujours utiliser Platform.runLater () pour mettre à jour la valeur de la propriété à partir du thread de l'interface utilisateur. .. Si vous mettez à jour la valeur en utilisant runLater () dans le modèle, le côté vue peut utiliser la propriété en toute confiance.

view

TimeLabelViewModel.java


package littlesky.view.main;

import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
...
import littlesky.model.clock.Clock;
import littlesky.model.option.ViewOptions;
...

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

import static littlesky.util.BindingBuilder.*;

public class TimeLabelViewModel {
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
    private static final DateTimeFormatter formatterWithoutSeconds = DateTimeFormatter.ofPattern("HH:mm");
    private final ReadOnlyStringWrapper text = new ReadOnlyStringWrapper("00:00:00");
    ...
    
    public void bind(Clock clock, SkyColor skyColor, ViewOptions viewOptions) {
        this.text.bind(
            binding(clock.timeProperty(), viewOptions.showSecondsProperty())
            .computeValue(() -> this.formatClockTime(clock, viewOptions))
        );
        ...
    }
    
    private String formatClockTime(Clock clock, ViewOptions viewOptions) {
        LocalTime time = clock.getTime();
        return time.format(viewOptions.isShowSeconds() ? formatter : formatterWithoutSeconds);
    }
    
    ...
    
    public ReadOnlyStringProperty textProperty() {
        return this.text.getReadOnlyProperty();
    }

    ...
}

TimeLabelViewModel est une classe qui contrôle l'affichage de l'heure.

Dans la méthode bind (), nous définissons la valeur du texte (propriété text) à afficher sur l'étiquette d'heure en fonction de Clock et ViewOptions.

    public void bind(Clock clock, SkyColor skyColor, ViewOptions viewOptions) {
        this.text.bind(
            binding(clock.timeProperty(), viewOptions.showSecondsProperty())
            .computeValue(() -> this.formatClockTime(clock, viewOptions))
        );
        ...
    }

--binding (...) .computeValue (...) est un constructeur qui vous permet d'écrire des instances de Binding avec moins de description, et tout ce que vous faites est de créer une classe anonyme pour ʻObjectBinding` est.

    private String formatClockTime(Clock clock, ViewOptions viewOptions) {
        LocalTime time = clock.getTime();
        return time.format(viewOptions.isShowSeconds() ? formatter : formatterWithoutSeconds);
    }

--formatClockTime () se réfère à l'heure mise à jour, showSeconds pour générer le texte de l'heure à afficher sur l'écran. --Enfin, la propriété text est exposée au monde extérieur en tant que propriété en lecture seule, tout comme le modèle.

    private final ReadOnlyStringWrapper text = new ReadOnlyStringWrapper("00:00:00");

    ...

    public ReadOnlyStringProperty textProperty() {
        return this.text.getReadOnlyProperty();
    }

controller

MainController.java


package littlesky.controller.main;

...

public class MainController implements Initializable {
    ...

    @FXML
    private Label timeLabel;

    ...

    @Override
    public void initialize(URL url, ResourceBundle resources) {
        ...
        this.replaceClockAndWeather(this.realTimeClock, this.openWeatherMap);
    }
    
    ...
    
    private void replaceClockAndWeather(Clock newClock, Weather weather) {
        ...

        TimeLabelViewModel timeLabelViewModel = new TimeLabelViewModel();
        timeLabelViewModel.bind(newClock, skyColor, this.options.getViewOptions());
        this.timeLabel.textProperty().bind(timeLabelViewModel.textProperty());

        ...
    }
    
    ...
}

--MainController est injecté avec timeLabel, qui affiche l'heure à l'écran.

  1. L'heure est mise à jour avec RealTimeClock
  2. TimeLabelViewModel le détecte et met à jour le texte affiché, en tenant compte des paramètres d'affichage à ce moment-là.
  3. La propriété text de timeLabel, qui était liée à la propriété text de TimeLavelViewModel, est mise à jour et l'affichage de l'écran change. La mise à jour du modèle est reflétée dans la vue, par exemple ---

Ce n'est pas la seule bonne réponse, car c'est une méthode à laquelle nous sommes arrivés par essais et erreurs lors de sa mise en œuvre. Cependant, je pense que cette méthode d'utilisation du mécanisme de surveillance des propriétés JavaFX comme moyen de propager les modifications du modèle à la vue n'est pas mauvaise dans la direction.

** Ce que je pensais était bon **

«Je sens que les rôles de chaque classe sont clairement séparés. --Modèle met à jour la logique principale et les valeurs --View détermine ce qu'il faut afficher en fonction des valeurs du modèle --Control est un pont entre la vue et le modèle «Je pense que la coopération de ces classes est simplifiée par le mécanisme de surveillance des propriétés JavaFX.

** Points que je souhaite améliorer **

Calcul des heures de lever et de coucher du soleil

En ce qui concerne le calcul des heures de lever et de coucher du soleil, j'ai cherché une bibliothèque en Java et j'ai trouvé les deux suivantes.

Le premier calcule l'heure du lever et du coucher du soleil, et le second calcule également la position du soleil, la position de la lune et la phase lunaire.

La première semble ne pas avoir été entretenue depuis plusieurs années, et la seconde bibliothèque semble être meilleure compte tenu de cette utilisation.

Cependant, lorsque je l'ai réellement utilisé, les heures de lever et de coucher du soleil étaient toutes deux calculées avec une précision raisonnable (quelques minutes d'erreur), mais le calcul de la phase de lune par cette dernière bibliothèque était considérablement différent de celui réel. C'était.

Donc, au final, j'ai décidé d'utiliser l'ancienne bibliothèque pour calculer le lever et le coucher du soleil.

SunriseSunsetTime


package littlesky.model.sun;

import com.luckycatlabs.sunrisesunset.SunriseSunsetCalculator;
import com.luckycatlabs.sunrisesunset.dto.Location;
import littlesky.model.location.UserLocation;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class SunriseSunsetTime {
    private final LocalDate localDate;
    private final SunriseSunsetCalculator calculator;
    private final TimeZone timeZone = TimeZone.getDefault();
    
    public SunriseSunsetTime(UserLocation userLocation, LocalDate localDate) {
        this.localDate = localDate;
        Location location = new Location(userLocation.getLatitude(), userLocation.getLongitude());
        this.calculator = new SunriseSunsetCalculator(location, this.timeZone);
    }
    
    public LocalTime sunriseTime() {
        Calendar today = this.toCalendar(this.localDate);
        Calendar sunriseTime = this.calculator.getOfficialSunriseCalendarForDate(today);

        return this.toLocalTime(sunriseTime);
    }
    
    public LocalTime sunsetTime() {
        Calendar today = this.toCalendar(this.localDate);
        Calendar sunsetTime = this.calculator.getOfficialSunsetCalendarForDate(today);

        return this.toLocalTime(sunsetTime);
    }
    
    private Calendar toCalendar(LocalDate localDate) {
        Date date = Date.from(localDate.atStartOfDay(this.timeZone.toZoneId()).toInstant());
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }
    
    private LocalTime toLocalTime(Calendar calendar) {
        return calendar.getTime().toInstant().atZone(this.timeZone.toZoneId()).toLocalTime();
    }
}

L'objet de date requis par la bibliothèque est de type «Calendrier», qui peut ne pas avoir été maintenu pendant plusieurs années. Puisqu'il s'agit de Java 8, il était nécessaire de le convertir et de l'utiliser avec l'API Date & Time.

Calcul de la phase mensuelle

Ensuite, qu'est-il arrivé au calcul de la phase lunaire? Je n'ai pas trouvé la bibliothèque, j'ai donc décidé de la calculer moi-même.

Cependant, il n'est pas possible de calculer la trajectoire, donc

En référence à ce domaine, j'ai écrit un programme pour trouver l'âge de la lune avec une précision raisonnable.

Plus précisément, à partir de l'âge de la lune le 1er janvier 1999 (13,17), le nombre de jours jusqu'à la date actuelle est calculé, et l'âge de la lune progressant pendant cette période est calculé en tenant compte du cycle méton.

L'implémentation est en MoonPhase.

Recommended Posts

Ressentez le passage du temps même à Java
Obtenez le résultat de POST en Java
L'histoire de l'écriture de Java dans Emacs
L'histoire de la comparaison de chaînes de bas niveau en Java
[Java] Gestion des Java Beans dans la chaîne de méthodes
L'histoire de la fabrication d'un Othello ordinaire à Java
À propos de l'idée des classes anonymes en Java
L'histoire de l'apprentissage de Java dans la première programmation
Mesurer la taille d'un dossier avec Java
[Communication Socket (Java)] Impressions de la mise en œuvre de la communication Socket dans la pratique pour la première fois
La date et l'heure de java8 ont été mises à jour
Importer des fichiers de la même hiérarchie en Java
Obtenez l'URL de la destination de la redirection HTTP en Java
[Java] Récupère le fichier dans le fichier jar quel que soit l'environnement
Modifier la qualité de stockage des images JPEG en Java
Récapitulez les éléments supplémentaires de la classe Optional dans Java 9
Implémentation Java de tri-tree
A été effectué dans l'année de base de la semaine calendaire Java
Définir le fuseau horaire de la JVM de votre application Azure
Comptez le nombre de chiffres après la virgule décimale en Java
Comment dériver le dernier jour du mois en Java
Accéder à l'interface réseau avec Java
[Java] Supprimer les éléments de la liste
Devinez le code de caractère en Java
[Édition Java] Histoire de la sérialisation
Décompressez le fichier zip en Java
Gestion des fuseaux horaires avec Java
Même avec Swift, horodatage en nanosecondes!
Liste des membres ajoutés dans Java 9
Analyser l'analyse syntaxique de l'API COTOHA en Java
Liste des types ajoutés dans Java 9
Ordre de traitement dans le programme
L'origine des expressions Java lambda
Implémentation d'une fonction similaire en Java
Appelez la super méthode en Java
Examiner les informations système de l'environnement d'exploitation AWS Lambda en Java
[Java] Obtenez les dates des derniers lundi et dimanche dans l'ordre
Sortie de la différence entre chaque champ de deux objets en Java
Examiner la liste des polices disponibles dans AWS Lambda + Java
La milliseconde définie dans /lib/calendars.properties de Java jre est UTC
Examinez la liste des ID de fuseau horaire disponibles dans la classe Java ZoneId
Obtenez l'URL publique du fichier privé de Flickr en Java
Créons une application TODO en Java 5 Changer l'affichage de TODO
Comment obtenir la longueur d'un fichier audio avec Java
Comment incrémenter la valeur de Map sur une ligne en Java
Implémentation de DBlayer en Java (RDB, MySQL)
Augmenter dynamiquement le nombre d'éléments dans un tableau bidimensionnel Java (tableau multidimensionnel)
Vérifiez le contenu du magasin de certificats Java
Référence Java à comprendre dans la figure
Examiner l'utilisation de la mémoire des éléments Java
Introduction à Java pour la première fois # 2
L'histoire de l'oubli de fermer un fichier en Java et de l'échec
[Java] Obtenez le jour d'un jour spécifique
Essayez d'utiliser l'API Stream en Java
Appelez l'API de notification Windows en Java
L'identité des paramètres de rails [: id]
Mémo: [Java] Vérifiez le contenu du répertoire
J'ai essayé le nouveau yuan à Java