Tworzenie niestandardowego JButton w Javie

97

Czy istnieje sposób na utworzenie JButtonwłasnej grafiki przycisku, a nie tylko z obrazem wewnątrz przycisku?

Jeśli nie, czy istnieje inny sposób tworzenia niestandardowego JButtonw java?

Anton
źródło

Odpowiedzi:

91

Kiedy po raz pierwszy uczyłem się Javy, musieliśmy stworzyć Yahtzee i pomyślałem, że fajnie byłoby tworzyć własne komponenty i kontenery Swinga zamiast rysować wszystko na jednym JPanel. Zaletą rozszerzenia Swingkomponentów jest oczywiście możliwość dodania obsługi skrótów klawiaturowych i innych funkcji ułatwień dostępu, których nie można zrobić po prostu posiadającpaint() wydrukowanie ładnego obrazka metody. Może nie być to jednak najlepsze rozwiązanie, ale może to być dla Ciebie dobry punkt wyjścia.

Edytuj 8/6 - Jeśli nie widać tego na zdjęciach, każda kostka jest przyciskiem, na który możesz kliknąć. Spowoduje to przeniesienie go DiceContainerponiżej. Patrząc na kod źródłowy, możesz zobaczyć, że każdy przycisk Die jest rysowany dynamicznie, na podstawie jego wartości.

tekst alternatywny
tekst alternatywny
tekst alternatywny

Oto podstawowe kroki:

  1. Utwórz klasę, która rozszerza JComponent
  2. Wywołaj konstruktora nadrzędnego super() w swoich konstruktorach
  3. Upewnij się, że klasujesz narzędzia MouseListener
  4. Umieść to w konstruktorze:

    enableInputMethods(true);   
    addMouseListener(this);
  5. Zastąp te metody:

    public Dimension getPreferredSize()  
    public Dimension getMinimumSize()  
    public Dimension getMaximumSize()
  6. Zastąp tę metodę:

    public void paintComponent(Graphics g)

Ilość miejsca, z którym musisz pracować podczas rysowania przycisku, jest definiowana przez getPreferredSize(), przyjmowanie getMinimumSize()i getMaximumSize()zwracanie tej samej wartości. Nie eksperymentowałem zbyt wiele z tym, ale w zależności od układu używanego w GUI, twój przycisk może wyglądać zupełnie inaczej.

I na koniec kod źródłowy . Na wypadek, gdybym coś przeoczył.

Kevin
źródło
1
Cześć, po pierwsze dzięki za kod! Sugerowałbym dodanie `` setActionComme (polecenie ciągu) '' do twojego kodu. jest to jeden ze sposobów filtrowania zdarzeń w Swing. (ale możesz argumentować, że jest 1001 rzeczy, które można dodać, aby trochę poprawić sytuację: P)
Jason Rogers,
1
Aby stworzyć kostkę, którą możesz nacisnąć, możesz użyć zwykłego starego JButton (zamiast tworzenia podklasy lub tworzenia niestandardowego JComponent), korzystając z setIcon / setPressedIcon, a także innych funkcji Icon w JButton
ControlAltDel
34

Tak, jest to możliwe. Jedną z głównych zalet używania Swinga jest łatwość, z jaką można tworzyć abstrakcyjne kontrolki i manipulować nimi.

Oto szybki i brudny sposób na rozszerzenie istniejącej klasy JButton, aby narysować okrąg po prawej stronie tekstu.

package test;

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;

public class MyButton extends JButton {

    private static final long serialVersionUID = 1L;

    private Color circleColor = Color.BLACK;

    public MyButton(String label) {
        super(label);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Dimension originalSize = super.getPreferredSize();
        int gap = (int) (originalSize.height * 0.2);
        int x = originalSize.width + gap;
        int y = gap;
        int diameter = originalSize.height - (gap * 2);

        g.setColor(circleColor);
        g.fillOval(x, y, diameter, diameter);
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        size.width += size.height;
        return size;
    }

    /*Test the button*/
    public static void main(String[] args) {
        MyButton button = new MyButton("Hello, World!");

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 400);

        Container contentPane = frame.getContentPane();
        contentPane.setLayout(new FlowLayout());
        contentPane.add(button);

        frame.setVisible(true);
    }

}

Zwróć uwagę, że nadpisując paintComponent można zmienić zawartość przycisku, ale obramowanie jest malowane metodą paintBorder . Należy również zarządzać metodą getPreferredSize , aby dynamicznie obsługiwać zmiany zawartości. Należy zachować ostrożność podczas pomiaru wskaźników czcionek i wymiarów obrazu.

Powyższy kod nie jest właściwym podejściem do tworzenia kontrolki, na której można polegać. Wymiary i kolory w Swing są dynamiczne i zależą od wyglądu i stylu użytkowania. Nawet domyślny wygląd Metal zmienił się w wersjach JRE. Byłoby lepiej zaimplementować AbstractButton i przestrzegać wytycznych określonych przez Swing API. Dobrym punktem wyjścia jest przyjrzenie się plikom javax.swing.LookAndFeel i javax.swing.UIManager klasom .

http://docs.oracle.com/javase/8/docs/api/javax/swing/LookAndFeel.html

http://docs.oracle.com/javase/8/docs/api/javax/swing/UIManager.html

Zrozumienie anatomii LookAndFeel jest przydatne do pisania kontrolek: Tworzenie niestandardowego wyglądu i stylu

McDowell
źródło
13

Zawsze możesz wypróbować wygląd i styl Syntezatora. Dostarczasz plik xml, który działa jak rodzaj arkusza stylów, wraz z dowolnymi obrazami, których chcesz użyć. Kod może wyglądać następująco:

try {
    SynthLookAndFeel synth = new SynthLookAndFeel();
    Class aClass = MainFrame.class;
    InputStream stream = aClass.getResourceAsStream("\\default.xml");

    if (stream == null) {
        System.err.println("Missing configuration file");
        System.exit(-1);                
    }

    synth.load(stream, aClass);

    UIManager.setLookAndFeel(synth);
} catch (ParseException pe) {
    System.err.println("Bad configuration file");
    pe.printStackTrace();
    System.exit(-2);
} catch (UnsupportedLookAndFeelException ulfe) {
    System.err.println("Old JRE in use. Get a new one");
    System.exit(-3);
}

Następnie kontynuuj i dodaj swój JButton tak, jak zwykle. Jedyną zmianą jest to, że używasz metody setName (string), aby określić, do czego powinien mapować przycisk w pliku xml.

Plik xml może wyglądać następująco:

<synth>
    <style id="button">
        <font name="DIALOG" size="12" style="BOLD"/>
        <state value="MOUSE_OVER">
            <imagePainter method="buttonBackground" path="dirt.png" sourceInsets="2 2 2 2"/>
            <insets top="2" botton="2" right="2" left="2"/>
        </state>
        <state value="ENABLED">
            <imagePainter method="buttonBackground" path="dirt.png" sourceInsets="2 2 2 2"/>
            <insets top="2" botton="2" right="2" left="2"/>
        </state>
    </style>
    <bind style="button" type="name" key="dirt"/>
</synth>

Znajdujący się tam element bind określa, na co należy odwzorować (w tym przykładzie zastosuje on ten styl do wszystkich przycisków, których właściwość name została ustawiona na „dirt”).

I kilka przydatnych linków:

http://javadesktop.org/articles/synth/

http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/synth.html

rjohnston
źródło
8

Pewnie jadę milion mil w złym kierunku (ale jestem dopiero młody: P). ale nie mógłbyś dodać grafiki do panelu, a następnie wskaźnika myszy do obiektu graficznego, tak aby gdy użytkownik znajdował się na grafice, Twoja akcja była wykonywana.

AngelOfCake
źródło
1
To by działało, ale wolałbym użyć standardowego JButton niż utworzyć przycisk mojego typu, jeśli to możliwe.
Anton,
7

Nie tworzyłem SWINGa od moich wczesnych zajęć z CS, ale gdyby nie był wbudowany, mógłbyś po prostu dziedziczyć javax.swing.AbstractButtoni tworzyć własne. Powinno być całkiem proste, aby połączyć coś razem z istniejącymi szkieletami.

John Downey
źródło