[JAVA] Créer un client GUI hebdomadaire [5] Première application de bureau

Ce n'est pas mensuel, donc c'est sûr. Fondamentalement, l'histoire de Swing.

Résumé jusqu'au numéro précédent

[\ 1 ] connaissances de base de stage / unstage

5 choses à faire pour la scène / désétape ligne par ligne

1. Sortie diff comme le patch original 2. Sélectionnez la ligne que vous souhaitez mettre en scène / désinstaller 3. Modifier le corps du morceau`` 4. Recalculez l'en-tête du morceau`` «5. Appliquer le patch final»

[\ 2 ] étape / capture incomplète de l'étape

Je ne pouvais pas utiliser JGit, alors j'ai parlé d'utiliser git.exe.

[\ 3 ] édition du patch stage / unstage

3. Modifier le corps du morceau`` 4. Recalculez l'en-tête du morceau``

[4] diff et al.

1. Sortie diff comme le patch original

Ce problème

2. Sélectionnez la ligne que vous souhaitez mettre en scène / désinstaller

進捗.gif

Cependant, il doit être affiché pour être sélectionné. Cette fois, c'est une question d'affichage.

Difficile de penser à l'interface utilisateur

En tant qu'application GUI

Il semble qu'il soit nécessaire de refléter de telles choses dans la conception, Je n'y pense pas fondamentalement ici. Parce que je veux faire "mon connard sur mes pensées"

Pensez-y et soyez heureux.

Par exemple UITableView

La première chose que j'ai imaginée était C'est une vue de type collection avec une section comme UITableView d'iOS. Mais vous et Java, c'est JVM.

En tant qu'interface utilisateur pour une sélection partielle de diff avec Swing rapidement J'ai choisi JList car cela semble être le plus simple. Le diff qui est la source du patch est affiché dans la liste ligne par ligne et sélectionné.

Je voulais mettre à jour ligne par ligne, donc je l'ai fait JTable (décrit plus tard), Puisqu'il s'agit d'un JTable avec une seule colonne, l'utilisation de base est la même.

Sensation de Mo

D'ailleurs, même si vous l'affichez dans une liste, vous n'avez pas à afficher le diff ligne par ligne. Tout ce que vous avez à faire est d'afficher le texte à l'intérieur de chaque ligne de la liste et de le laisser y être sélectionné. J'ai naturellement pensé que l'unité de sélection était une ligne dans la liste.

À cette époque, j'ai l'impression d'être coincé dans le développement d'applications mobiles. (Je travaille essentiellement sur le développement d'applications natives pour iOS et Android)

Déclenchement de scène / non-scène après sélection

Après avoir sélectionné, vous avez besoin d'une action qui déclenche l'étape / désétape. Si vous regardez SourceTree, vous verrez un bouton et un menu contextuel. Le clic droit est un problème à utiliser régulièrement. Il est également difficile de penser à la position et à la forme du bouton.

C'est pourquoi j'ai décidé de cliquer sur la partie en-tête du diff.

Affichage de Diff

hunk

Il est plus facile d'afficher les données obtenues à partir de git diff telles quelles, alors affichons-les en unités de morceau.

L'en-tête diff mentionné ici a plusieurs lignes pour chaque fichier, De la ligne commençant par diff --git juste avant la ligne d'en-tête du morceau. L'en-tête du morceau ici est une ligne commençant par @@.

<diff-per-file>
	<diff-header />
	<hunk>
		<hunk-header />
		<hunk-body />
	</hunk>
	<hunk>
		<hunk-header />
		<hunk-body />
	</hunk>
</diff-per-file>

<!--Il peut y avoir aucun morceau comme des images-->
<diff-per-file>
	<diff-header />
</diff-per-file>

Le git diff a la structure ci-dessus,

  1. Ce serait bien de savoir quel fichier est différent en unités de morceau
  2. J'essaie de mettre en scène / désenregistrer en cliquant sur la partie d'en-tête, donc je suis heureux si elle est plus large

Pour cette raison, affichez l'en-tête de diff avec chaque morceau.

<diff-per-file>
	<diff-header />
	<hunk>
		<hunk-header />
		<hunk-body />
	</hunk>
	<diff-header />
	<hunk>
		<hunk-header />
	    <hunk-body />
	</hunk>
</diff-per-file>

<diff-per-file>
	<diff-header />
	<hunk>
		<hunk-header dummy="new file" />
		<hunk-body img="icon.png " />
	</hunk>
</diff-per-file>

Si vous combinez plusieurs morceaux du même diff, l'en-tête de diff sera dupliqué, Il ne semble y avoir aucun problème même si elles sont envoyées au patch en une seule fois. (Cependant, je fais essentiellement git apply uniquement sur une base de morceau, donc il peut y avoir un problème caché)

tête de morceau

Par conséquent, plusieurs lignes de texte sont affichées sur une ligne de la liste. JLabel etc. peut mettre du html (comme?) Avec <html> </ html>.

Collector<CharSequence, ?, String> toHtml = Collectors.joining(
            "</nobr><br><nobr>",
            "<html><nobr>",
            "</nobr></html>");
Arrays.stream(Chaîne de caractères dans la partie d'en-tête.split("\n")).collect(toHtml)

Je me demande si <nobr> </ nobr> peut être utilisé dans n'importe quel environnement maintenant ou dans le futur, mais c'est utile.

beau corps

La sortie de git diff est telle qu'elle est. Il est facile de comprendre si les couleurs sont différentes pour la ligne d'ajout, la ligne de suppression et la ligne de contexte.

Acquisition asynchrone d'images

Lors de l'ajout ou de la suppression d'images (comme des fichiers binaires), il est géré en unités de fichier, donc Le morceau d'unité n'existe pas et n'apparaît pas dans la sortie de git diff.

Cependant, je veux afficher l'image car c'est un gros problème. Dans le numéro précédent, nous avons mentionné la méthode et les précautions.

Lors de l'affichage dans une liste

Dans cet esprit, il existe deux méthodes d'affichage.

Je ne suis pas content d'attendre, alors montrons ce qui peut être affiché en premier.

(Je ne pensais pas que ce serait un problème à mettre en œuvre, alors j'ai choisi ce dernier comme une évidence, mais je pense que c'est totalement ant d'attendre car cela dépend des performances de la machine locale, pas via le réseau.)

Mettre à jour l'affichage de la liste ligne par ligne

Cependant, JList ne semble pas pouvoir se mettre à jour ligne par ligne. Nous utilisons donc JTable avec une seule colonne.

À la suite de divers essais, je l'ai divisé en une ligne avec uniquement du texte et une ligne avec uniquement des images. La mise à jour dans l'ordre suivant a donné les performances souhaitées.

  1. Déterminez d'abord la hauteur de la ligne de texte
  2. Démarrez l'acquisition asynchrone des images dans les lignes d'images
  3. Une fois que vous avez l'image, déterminez la hauteur de la ligne à partir de l'image

Ce qui suit inclut les hacks qui dépendent de l'implémentation interne de Swing, donc Selon la version, la même idée et le même code peuvent ne pas fonctionner. Je vérifie javac 1.8.0_144. (Comprenez-vous que c'est la même chose que la version javac?) (Au fait, existe-t-il des plans pour améliorer considérablement les composants existants de Swing à l'avenir?)

Déterminez d'abord la hauteur de la ligne de texte

Lorsqu'il est nécessaire d'afficher / de mettre à jour chaque ligne dans JTable, le TableCellRenderer défini pour chaque colonne est appelé. TableCellRenderer est une interface.

public interface TableCellRenderer {

	Component getTableCellRendererComponent(JTable table, 
	                                        Object value,
	                                        boolean isSelected, 
	                                        boolean hasFocus,
	                                        int row, 
	                                        int column);
}

Il devrait y avoir plusieurs façons de mettre à jour chaque ligne, Ici, nous utilisons JTable # setRowHeight (int row, int rowHeight). Il est essentiel de changer la hauteur d'une ligne à l'autre.

Vous pouvez trouver la hauteur du texte en mettant le texte dans JLabel. Créez un JLabel et définissez la hauteur avant l'appel de TableCellRenderer.

La hauteur de chaque ligne est mise à jour lorsque les données associées à la table changent. Ceci est un exemple de code.

//Je vais d'abord assembler le setRowHeight de la ligne de texte
preRender = () -> {
    //Si vous n'effectuez pas l'une des opérations suivantes, le nombre maximal de lignes dans le tableau dépendra du nombre de lignes initialement affichées.
    //Probablement rowModel dans JTable= null;Je pense que tu as besoin
    this.table.setRowHeight(1);
//    setRowSorter(null);
//    tableChanged(new TableModelEvent(getModel()));
    final int size = dataModel.getSize();
    for (int i = 0; i < size; i++) {
        final ListItem item = dataModel.getElementAt(i);
        final ImageHolder imageHolder = item.imageHolder();
        //Si ce n'est pas une ligne d'image
        if (imageHolder == null || !imageHolder.hasLoader()) {
            //Ne pas encaisser car cela ne devrait pas coûter cher
            final JLabel label = new JLabel();
            this.renderer.render(label, item, false, null);
            final int height = label.getPreferredSize().height;
            table.setRowHeight(i, height);
        }
    }
};

Je l'ai écrit dans l'exemple de code, mais en guise de mise en garde,

Si vous n'appelez pas, etc., le nombre maximum de lignes dans le tableau dépendra du nombre de lignes initialement affichées. Si vous affichez d'abord 3 lignes, le tableau ne peut afficher que 3 lignes. Je ne sais pas pourquoi cela arrive, mais L'initialisation du private SizeSequence rowModel; du JTable semble se comporter comme prévu.

N'appelez pas JTable # setRowHeight (int row, int rowHeight) plus d'une fois pour la même ligne, Il se peut que.

Lancer l'acquisition asynchrone des images dans les lignes d'images

Comme pour le texte, vous pouvez commencer à l'avance, Je n'ai ressenti aucun problème de performance, donc Utilisez TableCellRenderer # getTableCellRendererComponent.

Dans tout les cas, Au moins lors de la mise à jour des lignes environnantes, TableCellRenderer # getTableCellRendererComponent sera appelé, donc Il doit être mis en œuvre pour que l'acquisition d'image et l'appel de rappel après acquisition ne soient effectués qu'une seule fois.

Une fois que vous obtenez l'image, déterminez la hauteur de la ligne à partir de l'image

Étant donné que l'affichage ne peut pas être modifié en dehors de TableCellRenderer # getTableCellRendererComponent, Après avoir obtenu l'image, donnez l'image à l'élément associé Appelez JTable # setRowHeight (int row, int rowHeight). Ensuite, TableCellRenderer # getTableCellRendererComponent sera à nouveau appelé.

final TableColumn tableColumn = this.table.getColumnModel().getColumn(0);
tableColumn.setCellRenderer((table, value, isSelected, hasFocus, row, column) -> {
        final ListItem item = (ListItem) value;
        final JLabel label = new JLabel();
        //Démarrez l'acquisition d'image ici ou définissez une image
        this.renderer.render(label, item, isSelected, imageIcon -> {
                //Après l'acquisition d'image
                final int iconHeight = imageIcon.getIconHeight();
                table.setRowHeight(row, iconHeight);
        });
        return label;
});

le progrès

C'est un gif de progression qui est familier à chaque fois et qui n'a rien à voir avec la publication.

進捗_3.gif

Il y a encore des bugs et des problèmes, mais vous pouvez vous engager. Je veux pouvoir afficher l'historique bientôt.

Épilogue

Aperçu de la prochaine fois

Une fois que vous commencez à écrire

J'étais fatigué pour une raison quelconque, alors j'ai décidé de passer au numéro suivant.

Situation récente

J'ai commencé à écrire cette série et ces applications C'est parce que j'ai pris ma retraite et que j'aimais être au chômage.

Je n'avais pas encore l'intention de travailler pendant un moment, Le travail est descendu du ciel, alors je suis devenu indépendant. C'était un flux que je suis devenu programmeur, donc je pense que ça va continuer comme ça.

Le dernier en date est la rénovation de l'application iOS. Je n'ai pas encore écrit de code, mais vous devriez git commit dans votre propre application. (Je ne l'ai pas encore exécuté sur mac, mais je crois en JVM)

Si vous avez du travail sous la main, vous serez plus motivé pour fabriquer des outils en dehors du travail.

Swing

Il n'y a pas de raison particulière d'écrire en Swing, Il n'y avait que "je ne veux pas écrire de CSS" J'avais l'intention de l'implémenter temporairement pour la conception, Il n'y a pas de problème particulier, je prévois donc de continuer tel quel.

Je me demande quel type de forme utiliser pour la distribution.

Recommended Posts

Créer un client GUI hebdomadaire [5] Première application de bureau
Créer un client GUI hebdomadaire git [4] diff et al.
Faire une capture incomplète de l'étape / unstage du client GUI hebdomadaire [2]
Créer une nouvelle application avec Rails
Essayez de créer une application client serveur
[Rails6] Créer une nouvelle application avec Rails [Débutant]
Créer une application TODO dans Java 7 Créer un en-tête
[Rails 5] Créer une nouvelle application avec Rails [Débutant]
Créez une application de recherche simple avec Spring Boot
J'ai essayé de créer une application de clonage LINE
Maintenant, le client git GUI self-made est très populaire!