Dúvida sobre a construção de componentes no Java Swing

5 respostas Resolvido
javaswing
R

Durante meus estudos sobre programação com Java Swing percebi que muitos programadores desenvolvem seus próprios componentes. Isto é, ao invés de criarem diretamente um novo componente Swing pela IDE e editá-lo na ferramenta padrão de design, criam classes Java que estendem a classe do componente, sobrescrevendo seus métodos. Assim, quase toda a programação dos componentes visuais acontece através de código e nem tanto com o drag and drop.

Gostaria de entender se há alguma vantagem nisso. É mais aconselhável criar seus próprios componentes Java Swing ou apenas arrastá-los para o editor da IDE, modificando as propriedades que ali estão?

A título de exemplo, deixo aqui um código que encontrei que amplia a classe JTextField do Java Swing. Ela requer para seu funcionamento que esteja instalada no projeto a biblioteca TimingFramework: TimingFramework-0.55.jar (100,8,KB)

package main;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.TimingTarget;
import org.jdesktop.animation.timing.TimingTargetAdapter;

public class TextField extends JTextField {

    public String getHelperText() {
        return helperText;
    }

    public void setHelperText(String helperText) {
        this.helperText = helperText;
        repaint();
    }

    public String getLabelText() {
        return labelText;
    }

    public void setLabelText(String labelText) {
        this.labelText = labelText;
    }

    public Color getLineColor() {
        return lineColor;
    }

    public void setLineColor(Color lineColor) {
        this.lineColor = lineColor;
    }

    private final Animator animator;
    private boolean animateHinText = true;
    private float location;
    private boolean show;
    private boolean mouseOver = false;
    private String labelText = "Label";
    private String helperText = "";
    private int spaceHelperText = 15;
    private Color lineColor = new Color(3, 155, 216);

    public TextField() {
        setBorder(new EmptyBorder(20, 3, 23, 3));
        setSelectionColor(new Color(76, 204, 255));
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent me) {
                mouseOver = true;
                repaint();
            }

            @Override
            public void mouseExited(MouseEvent me) {
                mouseOver = false;
                repaint();
            }
        });
        addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent fe) {
                showing(false);
            }

            @Override
            public void focusLost(FocusEvent fe) {
                showing(true);
            }
        });
        TimingTarget target = new TimingTargetAdapter() {
            @Override
            public void begin() {
                animateHinText = getText().equals("");
            }

            @Override
            public void timingEvent(float fraction) {
                location = fraction;
                repaint();
            }

        };
        animator = new Animator(300, target);
        animator.setResolution(0);
        animator.setAcceleration(0.5f);
        animator.setDeceleration(0.5f);
    }

    private void showing(boolean action) {
        if (animator.isRunning()) {
            animator.stop();
        } else {
            location = 1;
        }
        animator.setStartFraction(1f - location);
        show = action;
        location = 1f - location;
        animator.start();
    }

    @Override
    public void paint(Graphics grphcs) {
        super.paint(grphcs);
        Graphics2D g2 = (Graphics2D) grphcs;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
        int width = getWidth();
        int height = getHeight();
        if (mouseOver) {
            g2.setColor(lineColor);
        } else {
            g2.setColor(new Color(150, 150, 150));
        }
        g2.fillRect(2, height - spaceHelperText - 1, width - 4, 1);
        createHintText(g2);
        createLineStyle(g2);
        createHelperText(g2);
        g2.dispose();
    }

    private void createHintText(Graphics2D g2) {
        Insets in = getInsets();
        g2.setColor(new Color(150, 150, 150));
        FontMetrics ft = g2.getFontMetrics();
        Rectangle2D r2 = ft.getStringBounds(labelText, g2);
        double height = getHeight() - in.top - in.bottom;
        double textY = (height - r2.getHeight()) / 2;
        double size;
        if (animateHinText) {
            if (show) {
                size = 18 * (1 - location);
            } else {
                size = 18 * location;
            }
        } else {
            size = 18;
        }
        g2.drawString(labelText, in.right, (int) (in.top + textY + ft.getAscent() - size));
    }

    private void createLineStyle(Graphics2D g2) {
        if (isFocusOwner()) {
            double width = getWidth() - 4;
            int height = getHeight() - spaceHelperText;
            g2.setColor(lineColor);
            double size;
            if (show) {
                size = width * (1 - location);
            } else {
                size = width * location;
            }
            double x = (width - size) / 2;
            g2.fillRect((int) (x + 2), height - 2, (int) size, 2);
        }
    }

    private void createHelperText(Graphics2D g2) {
        if (helperText != null && !helperText.equals("")) {
            Insets in = getInsets();
            int height = getHeight() - 15;
            g2.setColor(new Color(255, 76, 76));
            Font font = getFont();
            g2.setFont(font.deriveFont(0, font.getSize() - 1));
            FontMetrics ft = g2.getFontMetrics();
            Rectangle2D r2 = ft.getStringBounds(labelText, g2);
            double textY = (15 - r2.getHeight()) / 2f;
            g2.drawString(helperText, in.right, (int) (height + ft.getAscent() - textY));
        }
    }

    @Override
    public void setText(String string) {
        if (!getText().equals(string)) {
            showing(string.equals(""));
        }
        super.setText(string);
    }
}

Desde já, agradeço e peço perdão se a pergunta for muito iniciante.

5 Respostas

J

Rodrigo existe várias porém vou falar apenas uma que é onde eu vi mais motivo a cor.
Vc tem o paint component que é um método não alterarvel que vc só pode alterar na construção do seu componente.
Segue um exemplo de um botão (entra sai e clica);

@Override
    protected void paintComponent(Graphics g) {

        Graphics2D g2 = (Graphics2D) g.create();
        if (mouseEntered && !isClicked) {
            float[] fractions = {0.0f, 0.5f, 1.0f};
            Color[] colors = {rgb1, rgb2, rgb1};
            g2.setPaint(new LinearGradientPaint(0, 0, 0, getHeight(), fractions, colors));
        } else if (mouseClicked && isClicked) {
            float[] fractions = {0.0f, 0.5f, 1.0f};
            Color[] colors = {rgb1, rgb2, rgb1};
            g2.setPaint(new LinearGradientPaint(0, 0, 0, getHeight(), fractions, colors));
        } else {
            float[] fractions = {0.0f, 0.5f, 1.0f};
            Color[] colors = {rgb1, rgb2, rgb1};
            g2.setPaint(new LinearGradientPaint(0, 0, 0, getHeight(), fractions, colors));
        }
        setFocusable(false);
        g2.fillRect(0, 0, getWidth(), getHeight());
        g2.dispose();

        super.paintComponent(g);
    }
T
Solucao aceita

Como você mesmo notou, cada abordagem tem vantagens e desvantagens. É mais o caso do que você quer obter, do que uma ser melhor que a outra.

Se você só precisa criar uma tela para um programa simples, criar visualmente pelo netbeans normalmente é suficiente. Mas você vai notar que o código da tela gerado é bem complexo, mais complexo que fazê-lo “na mão”. Muitos programadores preferem ter mais controle dessa parte.

Ademais, criando seus próprios componentes e telas “na mão”, você ganha mais flexibilidade. É comum estender certos componentes (como no exemplo que você mostrou) para adicionar comportamentos a eles, e reusar esses componentes extras, em vez de programar esses comportamentos em componentes genéricos.

Por exemplo, se seu sistema tem um JTextField para entrada de documentos (ex: cnpj e cpf), você estender um jtextfield para embutir a validação de cnpj diretamente nesse componente, e reusá-lo em todas as telas que precisem de um campo desse tipo, em vez de programar a validação em cada uma dessas telas.

Abraço.

J

Ja comentando o seu código com o exemplo mostrado vc pode ver o “Graphics g” que é uma variavel que só pode ser acessada na construção do botão. Até onde eu saiba.

S

É bem mais vantajoso criar seus próprios componentes ou na maioria das vezes apenas implementando o seu próprio Model do componente.

O Swing utiliza MVC ao extremo, quase toda classe “NomeDoComponente” tem uma interface “NomeDoComponenteModel” correspondente para dar a flexibilidade e facilidade de alimentar o componente.

Infelizmente os editores visuais não costumam estar preparados para facilitar que o programador especialize o seu Model dos componentes. Aí a maioria programa utilizando os “DefaultNomeDoComponenteModel”.

Aí a galera usa que geralmente são apenas facilitadores para adicionar conteúdo estático nos componentes.

Então a galera usa esses “DefaultNomeDoComponenteModel” pois é o que o editor visual geralmente usa, mas querem um sistema dinâmico e aí começa a gambiarra:

  • implementam loops desnecessários para preencher o componente;
  • implementam loops desnecessários para excluir itens do componente;
  • implementam loops desnecessários para atualizar itens do componente.

Sem contar que começam a fazer Ctrl+C, Ctrl+V de códigos existentes relacionados a algum componente e adaptam para fazer parecido em outro componente do mesmo tipo, mas com informações diferentes.

A consequência disso é um sistema com telas lentas e que apresentam “piscadas”.

Sempre procure estudar como funciona a Interface Model do componente que você está utilizando.

Assim você consegue implementar seus próprios modelos, muitas vezes até modelos genéricos, que podem ser reaproveitados.

R

Bem pessoal, acredito que entendi basicamente o porquê disso com todas as postagens.

Agradeço à todos.

Criado 24 de maio de 2023
Ultima resposta 24 de mai. de 2023
Respostas 5
Participantes 4