Icon wird auf JButton viel zu klein dargestellt


#1

Hallo zusammen,

ich habe gerade eben ein kleines Bild gezeichnet, die Größe des Bildes entspricht genau der größe des JButtons. Sobald ich das Icon dem JButton hinzufüge wird es auch angezeigt, leider nur viel zu klein.
Ich verstehe nicht warum?

Ich habe es schon mit button.setMargin( new Insets(0,0,0,0));
leider kein erfolg.

Woran liegt das, ich habe das Icon ganz normal mit der Methode setIcon(); auf den Button gesetzt.

Verstehe das nicht.

SG


#2

Verzicht auf Füll-Sätze wie ‘Verstehe das nicht.’, ‘Ich verstehe nicht warum?’, ‘leider kein erfolg.’, ‘Woran liegt das,’ wäre schön, bis auf maximal einen, wobei eh recht klar, dass du einfach nichts dazu weißt,

ist ja auch keine Schande, bei Swing eh nicht viel zu verstehen,
es gibt Standardregeln für die Images, für Verhalten von Images/ Icons in einem Button,
Regeln allgemein für das Layout der GUI um den Button herum welches auf den Button einwirkt, und das alles muss auch noch zusammenspielen,

schwer irgendwas vorherzusagen, außer für Experten die zu viel Zeit haben, solch verzichtbares genauer zu lernen :wink:

man kann sich die Situation genauer anschauen und typische Wege versuchen,
mag sein dass Margin was dabei bewirken kann, oder zig andere Dinge


Quellcode posten ist bei so einer Frage, praktisch jeder von dir, ein Muss,
wäre auch günstig wenn du dir das gleich angewöhnen könntest statt erst auf Nachfrage

je vollständiger der Code, und vorerst nur eine einfache GUI mit einem Button, desto besser,
freilich: wenn es einfach geht, im eigentlichen komplizierten Programm nicht, dann Problem schwieriger, oder auch nur Unterschiede festzustellen, der normale Weg zur Erkenntnis nach und nach

und wie groß ist das Bild in Pixel Höhe + Breite, in welcher Größe ungefähr dargestellt?

schau evtl. mal das Programm hier an zum Vergleich
https://coderanch.com/t/338181/java/JButton-ImageIcon-Size
klappt das mit deinem Bild? irgendwelche erwähnenswerten Erkenntnisse?


#3

Das Bild hat die gleichen Maße wie der JButton, also länge = 60px, höhe = 10px.

Zum Testen habe ich einfach mal ein neues Programm mit einem JFrame fenster erstellt und darauf einen JButton mit setBounds platziert. Anschließend habe ich mit setIcon(); das Bild auf den Button gesetzt und es hat alles wunderbar funktioniert.

In meinem eigentlichen Programm habe ich aber ein JPanel das mit einem GridBagLayout versehen wurde und genau hier scheint das Problem zu sein.
Auf diesen JPanel gibt es mehrere JButtons.
Jeder JButton hat seine eigenen Constraints, wobei alles gleich ist bis auf gridy da sich jeder Button in einer eigenen Zeile befindet. Das heißt die Buttons sind untereinander angeordnet.

So sieht der Code für einen Button aus:

JButton button = new JButton();
ImageIcon icon = new ImageIcon(“Pfad des Bildes”);

button.setIcon(icon);

Constraints für den Button:

constraints.gridx = 0;
constraints.gridy = 0;
constraints.ipadx = 60;
constraints.ipady = 10;
constraints.weightx = 1;
constraints.insets = new Insets(0,0,5,0);

Die Constraints wurden auch dem GridBagLayout mittgeteilt mit: gridbag.setConstraints…

Die Constraints funktionieren auch wunderbar, nur das Bild am Button passt nicht.
Es füllt nicht den gesamten Button aus. Wenn ich ein größeres Bild verwende, dann wird auch der Button wieder größer.

Anbei eine kleine Zeichnung, wie das ganze aussieht.

SG


#4

Schau dir an, was du für Attribute im GridBagLayout setzt. Im Wiki findest du auch ein Tutorial zum GBL.

Die Werte von ipadx bzw. ipady sorgen für Abstand zur eingefügten Komponente in einer Zelle des GBL.
Folgender einfacher Code macht das, was du willst:

import java.awt.*;
import javax.swing.*;

public class Test {
   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

   private static void createAndShowGui() {
      JPanel p = new JPanel(new GridBagLayout());
      p.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));
      GridBagConstraints gbc = new GridBagConstraints();

      Icon icon1 = new ImageIcon(Test.class.getResource("Button1.png"));
      JButton b1 = createButton(icon1);

      Icon icon2 = new ImageIcon(Test.class.getResource("Button2.png"));
      JButton b2 = createButton(icon2);

      Icon icon3 = new ImageIcon(Test.class.getResource("Button3.png"));
      JButton b3 = createButton(icon3);

      gbc.gridy = 0;
      gbc.insets = new Insets(0, 0, 5, 0);
      p.add(b1, gbc);

      gbc.gridy++;
      p.add(b2, gbc);

      gbc.gridy++;
      gbc.insets = new Insets(0, 0, 0, 0);
      p.add(b3, gbc);

      JFrame f = new JFrame("GBL-Demo");
      f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      f.add(p);
      f.pack();
      f.setLocationRelativeTo(null);
      f.setVisible(true);

   }

   private static JButton createButton(Icon icon) {
      JButton b = new JButton(icon);
      b.setMargin(new Insets(0, 0, 0, 0));
      return b;
   }
}

Bild

Im Anhang das fertige ausführbare Beispiel samt Qellcode in der Jar-Datei.
Buttons mit Bild im GBL.zip (5,7 KB)


#5

Im Allgemeinen sollte man sich, wenn etwas im kleinen Testfall funktioniert und in der großen Anwendung nicht, stückweise von einer Seite der anderen annähern (natürlich in einer Kopie). Also entweder im komplizierten Fall solange Dinge weglassen, bis man den Übeltäter hat, oder das funktionierende Beispiel analog immer mehr verkomplizieren, bis man den Übeltäter findet.

Das GridbagLayout ist zwar sehr flexibel, aber auch manchmal ein wenig hintertückisch, daher versuche ich es in der Regel durch die Kombination anderer Layouts zu vermeiden.


#6

Danke vielmals für eure Hilfe!

Leider bleibt bei mir auch mit deinem Code eine Rand übrig.
Dieser Rand ist irgendwie immer da, nur wenn ich das GridbagLayout entferne, dann ist der Button vollständig abgedeckt.

Ich wede nochmal herumprobieren, mal sehen ob ich dahinter komme.

SG


#7

weniger Rand als im GBC-Beispiel hier geht doch wohl nicht, wobei ich nur b1 ausprobiert habe,
1-2 Pixel, mit setBorder(null); oder setBorder(BorderFactory.createLineBorder(Color.BLACK));
kann man da auch noch sparen, hat dann aber den Effekt-Border verloren

füge ich deine beiden
constraints.ipadx = 60;
constraints.ipady = 10;
hinzu, dann kommt ziemlich die Lage, die du zuerst beschrieben hast,
zusätzliche Breite und Höhe dem Button zugewiesen, ein dicker Rand ähnlich deinem Bild,

siehe Beispiel
http://www.java2s.com/Code/Java/Swing-JFC/GridBagLayoutwithconstraints.htm
die größere Höhe des ersten Buttons ‘Java’,
in Breite auch, aber schwer zu erkennen, besser wenn alle 4 Texte gleich auf ‘Java’ gesetzt


mit dem Inhalt des Buttons an sich sollten diese ipad-Angaben nichts zu tun haben,
wäre ja auch nervig wenn man da immer die Angaben eines Bildes setzen müsste,

sowieso ungewöhnliche Attribute, praktisch selber noch nie benutzt, wie kommt man/ du dazu?

nicht viel wissen ist eine Sache, aber dann planlos die kompliziertesten Sondersachen rauszukramen, unerwähnt zu einem Rätsel zu bauen, das ist mal ein Kunststück :wink: wenn auch leider nicht neu

lasse diese seltenen Dinge einfach weg, und es geht womöglich besser


edit:
ein 60x10-Bild in einem Button von
http://www.java2s.com/Code/Java/Swing-JFC/GridBagLayoutwithconstraints.htm
eingefügt gibt freilich auch etwas Abstand, auch ohne ipadx/y,

der Befehl
setMargin(new Insets(0, 0, 0, 0));
aus der createButton-Methode im Code von L-ectron-X hat eben auch seine Berechtigung, nicht übersehen

da warst du ja schon selber dran, das + kein ipadx/y mag die Summe sein


#8

Stellst du dir noch ein anderes LAF ein? Dann hast du wahrscheinlich auch wieder andere Abstände zwischen Bild und Button-Rand. Es scheint nicht vorgesehen zu sein, dass ein Button mit einem Icon ausgefüllt werden kann…
Alternativ kann man auch einen Button mit einem Label simulieren. Allerdings musst du dann einen MouseListener am Label registrieren und selbst die Button-Zustände wechseln (gedrückt/nicht gedrückt).


#9

Also auf das JPanel wo sich die Buttons befinden, wende ich nur das GBL an sonst nichts.

An das mit den labels habe ich auch schon gedacht, wäre auch eine Möglichkleit.

Aber ich bin gestern draufgekommen, das ich die Buttons auch absolut platzieren kann.
Muss dann halt etwas mit der Position herumspielen, is aber auch kein Problem.
Dann werde ich einfall das Layout am JPanel auf “null” setzen und es absolut platzieren.

Eine Frage noch, kann man bei einem JButton den Origin anders setzen?
Standarmäßig fängt die Position eines Buttons links oben an.
Es wäre oft einfacher, wenn der Mittelpunkt nicht links oben sondern genau in der Mitte des Buttons ist.

Hat jemand von euch erfahrund damit?

Vielen Dank!

SG


#10

Was mir jetzt gerade noch eingefallen ist, das sich das JPanel auf einem JLayerdPane befindet.

Kann das der Grund sein?

SG


#11

Was du natürlich auch noch machen kannst, ist die paintComponent überschreiben.


#12

das ist keine Frage des Buttons,
sondern damit hat jedes Layout seine Probleme, wo zu platzieren, ob Größe der Komponente vorher feststellbar oder später noch anders,
ob gar das Layout die Größe erst noch beeinflusst, etwa eine GBC-Zelle ausfüllt, oder wiederum das ominöse ipadx/y :wink:

der Button selber weiß nicht, wie groß er dargestellt wird, außer seine eigenen Angaben zu MinimumSize/ PreferredSize usw.,
die kann man in einfachen Maßstäben nutzen um in einer Hilfsmethode für Positionen zu rechnen a la setButtonCenterTo(b, 100, 70) -> daraus wird Setzen des Buttons an 70, 65 ausgerechnet aus Breite 60, Höhe 10


hast du mein letztes Posting gelesen? mit ipadx/y, evtl. fehlenden setMargin() auf Button gibt es genug direkt vorliegende Gründe, warum bei dir evtl. Button mit größeren Rand dargestellt,

theoretisch ist zwar immer möglich, dass dein Java anders reagiert als (nahezu) alle Testprogramme der Welt auf (nahezu) allen anderen Computern der Welt,
aber wahrscheinlich ist das ja doch nicht,
je nachdem was du aktuell unter ‘leider bleibt bei mir auch mit deinem Code eine Rand übrig’ verstehst,

kannst du das Programm von L-Ectron-X bei dir ausführen und einen Screenshot schicken?
kannst du ein einfaches Testprogramm bereitstellen mit Button in GBL + einen Button in anderen Layout ‘ohne Rand’, zum Vergleich, evtl. wieder mit Screenshot, wenn bei anderen ja vermutlich kein Unterschied?

muss aber natürlich nicht sein, schon bemerkt dass mein letztes Posting nicht groß erwähnt :wink:
nur falls du in der Hinsicht doch weiter interessiert sein solltest


#13

NullLayout ist fast immer eine schlechte Idee. Man kann nie genau voraussagen, wie das Programm auf anderen Systemen aussehen wird. Außerdem hast du deutlich mehr Aufwand bei Umgestaltung oder Wartung deines Programms.
Der Rahmen, der nun noch um den Button verbleibt, liegt nicht an einem Layout, sondern daran, wie die UI des Buttons programmiert wurde. Damit der Button einen Rahmen haben kann, braucht es einen gewissen Abstand.
Hier mal noch mal ein Beispiel mit einem GridLayout. Wenn du das ausführst, wirst du sehen, dass das ganz genau so aussieht, wie das Beispiel mit dem GridBagLayout.

import java.awt.*;
import javax.swing.*;

public class Test {
   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

   private static void createAndShowGui() {
      JPanel p = new JPanel(new GridLayout(0, 1, 0, 5));
      p.setBorder(BorderFactory.createEmptyBorder(50, 50, 50, 50));

      Icon icon1 = new ImageIcon(Test.class.getResource("Button1.png"));
      JButton b1 = createButton(icon1);

      Icon icon2 = new ImageIcon(Test.class.getResource("Button2.png"));
      JButton b2 = createButton(icon2);

      Icon icon3 = new ImageIcon(Test.class.getResource("Button3.png"));
      JButton b3 = createButton(icon3);

      p.add(b1);
      p.add(b2);
      p.add(b3);

      JFrame f = new JFrame("GridLayout-Demo");
      f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      f.add(p);
      f.pack();
      f.setLocationRelativeTo(null);
      f.setVisible(true);

   }

   private static JButton createButton(Icon icon) {
      JButton b = new JButton(icon);
      b.setMargin(new Insets(0, 0, 0, 0));
      return b;
   }
}

Optional kann man vielleicht noch folgende Zeile in die createButton()-Methode vor der Rückgabe einsetzen:

b.setPreferredSize(new Dimension(b.getPreferredSize().width-4, b.getPreferredSize().height-4));

GridLayout-Demo

Wenn du beim MetalLookAndFeel bleibst funktioniert das.
Damit erhält der Button eine etwas kleinere bevorzugte Standardgröße, die das Icon flächendeckend darstellt.
Aber hier besteht weiterhin das Problem der ButtonUI, die mit anderen LookAndFeels andere Ergebnisse erzeugen wird. Und wer sagt, dass das MetalLookAndFeel mal nicht einer Anpassung unterzogen wird?


#14

Das mit dem manuellen setPreferredSize ist ein bißchen heikel.

Unabhängig davon hab’ ich das Problem vielleicht nicht ganz verstanden. Hier ist mal ein (direkt und ohne externen PNGs) compilierbares Beispiel:

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ButtonSizeTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel(new FlowLayout());
        panel.setOpaque(true);
        panel.setBackground(Color.GREEN);
        f.getContentPane().add(panel);

        JButton button0 = new JButton(createIcon());
        panel.add(button0);

        JButton button1 = new JButton(createIcon());
        button1.setMargin(new Insets(0, 0, 0, 0));
        panel.add(button1);

        JButton button2 = new JButton(createIcon());
        button2.setMargin(new Insets(0, 0, 0, 0));
        button2.setBorder(null);
        panel.add(button2);

        f.setSize(500, 500);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static Icon createIcon()
    {
        return new ImageIcon(createImage());
    }

    private static BufferedImage createImage()
    {
        int w = 80;
        int h = 40;
        BufferedImage image =
            new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.RED);
        g.fillRect(0, 0, w, h);
        g.setColor(Color.BLACK);
        g.drawString(w + "x" + h, 10, 20);
        g.dispose();
        return image;
    }
}

Das ganze sieht so aus, wenn man es startet:

ButtonSizes

  • Links ist das, was “normalerweise” angezeigt wird.
  • In der Mitte ist der Rand mit setInsets(...) weggemacht
  • Rechts ist der Rand und der Rahmen weggenommen, so das der Button und das Bild gleich groß sind.

Ob die wirklich gleich groß sind, hängt dann nur noch vom Layout Manager ab. Hier ist das FlowLayout, das versucht, seine Components in der Größe anzuzueigen, die sie bei getPreferredSize zurückgeben (und bei einem Button wie dem rechts ist das nur noch die Bildgröße).


#15

Gute Lösung, allerdings fällt auf, dass der rechte Button sich nicht mehr wie ein Button verhält. Eher wie ein Label. Man hat durch den fehlenden Rahmen keine sichtbare Statusänderung beim Klicken mehr.


#16

Ja, das ist nicht schön: Mouse-Over, Fokus und “Pressed”-Status sind nicht mehr erkennbar. Ich hatte kurz überlegt, das noch zu erwähnen, aber … dachte dann: “So ist das eben, wenn man NUR ein Bild sieht”.

Die Abhilfe ist da aber recht einfach: Man kann mit AbstractButton#setRolloverIcon und AbstractButton#setPressedIcon die Icons setzen, die angezeigt werden sollen, wenn man mit der Mouse drüberhovert oder der Button gerade runtergedrückt ist. (Es gibt noch andere, z.B. AbstractButton#setDisabledIcon für den Fall das der button disabled ist).

Hier ist das mal als Beispiel: Die Icons sind einfach Bilder, in denen “R” für “Rollover” und “P” für “Pressed” reingeschrieben wird:

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ButtonSizeTest2
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel(new FlowLayout());
        panel.setOpaque(true);
        panel.setBackground(Color.GREEN);
        f.getContentPane().add(panel);

        JButton button0 = new JButton(createIcon(""));
        button0.setRolloverIcon(createIcon("R"));
        button0.setPressedIcon(createIcon("P"));
        panel.add(button0);

        JButton button1 = new JButton(createIcon(""));
        button1.setRolloverIcon(createIcon("R"));
        button1.setPressedIcon(createIcon("P"));
        button1.setMargin(new Insets(0, 0, 0, 0));
        panel.add(button1);

        JButton button2 = new JButton(createIcon(""));
        button2.setRolloverIcon(createIcon("R"));
        button2.setPressedIcon(createIcon("P"));
        button2.setMargin(new Insets(0, 0, 0, 0));
        button2.setBorder(null);
        panel.add(button2);

        f.setSize(500, 500);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static Icon createIcon(String label)
    {
        return new ImageIcon(createImage(label));
    }

    private static BufferedImage createImage(String label)
    {
        int w = 80;
        int h = 40;
        BufferedImage image =
            new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.RED);
        g.fillRect(0, 0, w, h);
        g.setColor(Color.BLACK);
        g.drawString(w + "x" + h + ", " + label, 10, 20);
        g.dispose();
        return image;
    }
}

Für “schöne” Icons könnte man …

  • bei “Rollover” einen Rahmen (in den Bildbereich?!) zeichnen
  • bei “Pressed” das Bild ein bißchen verschieben und dunkler machen
  • bei “Disabled” das Bild in Graustufen umwandeln

aber das geht dann weit über die (ursprüngliche) Frage hinaus, warum das Bild zu klein war…


#17

Also mit dem Null-Layout dürfte es da eignetlich bei anderen PCs auch kein Problem geben, da das JPanel auf dem die Buttons platziert wurden immer gleich groß ist. Somit bleibt auch die Position und die größe immer die selbe.

Wenn es bei einem anderen System damit probleme geben sollte, hat man aber beim Layout-Manager auch nicht immer die Garantie, das der das dann richtig darstellt.

Oder liege ich da Falsch??


#18

Da hast
du Recht
manchmal
hat man
einfach
nicht genug
Platz um
Sachen
richtig zu
formatieren.

Sent from my Smartphone

Aber mal im Ernst: Die Argumentation zieht sich natürlich durch: WENN das Panel immer die gleiche Größe hat, könnte das OK sein, aber warum hat das Panel immer die gleiche Größe? Ja, weil das Fenster immer die gleiche Größe hat. Und warum das? Weil der Monitor immer die gleiche Größe hat… … … ??? Es hängt schon davon ab, WAS genau man machen will, und wie viel “Flexibilität” man braucht. Aber in den ALLERmeisten Fällen sind feste Größenangaben mit setPreferredSize oder null-Layout etwas, wo man zumindest mal skeptisch die Augenbraue heben sollte…


#19

genau, wenn es brennt, hat man auch mit Feuerwehr nicht die Garantie, dass alles gut geht…,

wenn sich die Größe ändert, dann hat man mit NullLayout meist verloren,
LayoutManager sind neben der komfortablen Hauptkonfiguration auch dafür da, im abweichenden Fall zumindest zu versuchen, noch halbwegs alternativ anständig anzuzeigen,

etwa mit Zeilenumbruch, Schrumpfen der Komponenten,
das kann blöde aussehen, aber kann genauso gut auch gerade so noch den OK-Button einblenden, während bei NullLayout im Pech-Fall vielleicht außerhalb des sichtbaren Bereichs und Programm tot


#20

Da habt Ihr natürlich recht, das ein Null-Layout nicht der feinen englischen Art entspricht.
Wegen dem JPanel, dieses ist wie gesagt immer gleich groß es wird nie skaliert.
Nur das Hintergrundbild wird skaliert, dies ist aber unabhängig von JPanel.
Wenn du eine größere Auflösung hast, wirkt das JPanel etwas kleiner, bei geringer Auflösung wirkt es natürlich größer.

Habe das aber auf unterschiedlichen PC’s ausprobiert und man kann alles sauber lesen, von daher passt das für mich.

Ändern kann ich es immer noch, ist aber mit dem GBL echt aufwändig. Es GBL kann sehr viel, das ist auch echt super! Nur leider oft nicht so einfach zu programmieren.

SG