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

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.

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);
    }

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.

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.

É 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.

1 curtida

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

Agradeço à todos.