Создание простого пользовательского JComponent в Java?

Я хочу начать создавать свои собственные настраиваемые компоненты JComponent для проекта на работе. У меня есть простой пример ниже, который должен просто создать мяч на экране. (Большую часть я нашел в Интернете), но это действительно достойная отправная точка. Мой вопрос: почему этот код не показывает мяч в моей форме? Что я сделал не так?

Также каковы все основные методы, которые ДОЛЖНЫ быть предоставлены для пользовательского JComponent?

Код:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class testBall {
    public static void main(String[] args) {
        new testBall();
    }

    public testBall() {
        JPanel testPane = new JPanel();
        testPane.setBackground(Color.white);
        testPane.setLayout(new GridBagLayout());
        testPane.add(new MyBall(30,30,10));

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(testPane);
        frame.pack();
        frame.setSize(500, 300); 
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

class MyBall extends JComponent
{
    private static final long serialVersionUID = 1L;
    public MyBall() { }
    public MyBall(int x, int y, int diameter)
    {
        super();
        this.setLocation(x, y);
        this.setSize(diameter, diameter);
    }

    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.setColor(Color.red);
        g.fillOval(0, 0, 100, 100);
    }
}

Где я могу найти список всех методов, которые следует переопределить в классе JComponent? (Я знаю, что есть те, которые всегда должны быть включены в JComponent.)

Если я создам экземпляр этого компонента в классе и мне нужно изменить цвет круга, могу ли я просто вызвать метод repaint() из этого класса?


person Community    schedule 05.05.2014    source источник


Ответы (3)


Вы добавляете MyBall к testPane (у которого есть GridBagLayout) без указания каких-либо ограничений. (То есть ваш вызов add() не имеет второго параметра.) Ограничения по умолчанию, скорее всего, не то, что вам нужно. Попробуйте использовать BorderLayout для своей тестовой панели, так как по умолчанию используется BorderLayout.CENTER, что, вероятно, разумно в вашем случае:

testPane.setLayout(new BorderLayout());

Это заставляет мяч появиться для меня.

Что касается вашего второго вопроса, я думаю, что ваш класс в порядке, как определено. Основной метод, который вы хотите реализовать, это paintComponent(), как у вас есть. Иногда возникает необходимость переопределить методы get min/max/preferred size, но на самом деле это просто зависит от того, что вы хотите, чтобы компонент делал. JComponent не является абстрактным, поэтому вам не нужно ничего переопределять, если вы этого не хотите. Он предоставляет множество готовых функций, таких как фокус клавиатуры, сменный внешний вид, специальные возможности и т. д. Если вы не хотите ничего менять, просто оставьте это как есть. Реализуйте paintComponent() и различные методы get*Size() и покончите с этим. (Вам просто нужно выбрать методы в JavaDoc, чтобы увидеть, что подходит для переопределения.)

Другой вариант — расширить подкласс JComponent, если есть класс, который делает что-то похожее на то, что вы хотите сделать. Например, JPanel часто является хорошей отправной точкой для реализации собственного контейнера.

Вы, вероятно, искали что-то более конкретное, чем «это зависит», но прямо сейчас, если все, что вам нужно, это нарисовать мяч, просто переопределите методы, которые имеют дело с рендерингом методов JComponent (paintCompentent() и get*Size()).

В качестве примечания: вам действительно следует использовать SwingUtilities.invokeLater() для создания компонентов Swing в потоке Swing. См. раздел «Политика потоков Swing» по адресу http://docs.oracle.com/javase/6/docs/api/javax/swing/package-summary.html.

person matt forsythe    schedule 05.05.2014
comment
Если вам нужен список всех методов, которые могут или должны быть включены в JComponent, могу ли я найти его? - person ; 05.05.2014
comment
В моем тестовом методе, что, если я хочу перекрасить Jcomponent в зеленый цвет, когда я нажимаю на форму. Могу ли я просто вызвать repaint(), и это должно правильно перекрасить каждый компонент в форме. - person ; 05.05.2014
comment
@ user3376708 Обновлен ответ на ваш первый вопрос. Что касается вашего второго вопроса, да, я думаю, все, что вам нужно сделать, это вызвать repaint() для MyBall. - person matt forsythe; 05.05.2014
comment
Sometimes it becomes necessary to override the get min/max/preferred size methods, - это нужно делать постоянно при создании пользовательского компонента. Вам необходимо реализовать эти методы, чтобы менеджеры компоновки могли работать правильно. Решение с использованием BorderLayout не устраняет проблему. Попробуйте добавить компонент к NORTH от BorderLayout, и у вас все равно будет та же проблема. - person camickr; 06.05.2014
comment
Почему это не будет работать в разных макетах? Если бы я делал настоящий компонент, разве я не хотел бы сделать компонент таким, чтобы его можно было использовать в каждой форме макета. - person ; 06.05.2014
comment
@camickr Я говорю, что иногда это становится необходимым, потому что это зависит только от того, как вы собираетесь его использовать. Некоторые макеты навязывают размер вашему компоненту и полностью игнорируют информацию о размере. Для некоторых компонентов это может иметь смысл (например, если ваш компонент просто пытается нарисовать что-то красивое, чтобы занять пустое место). Переключение на BorderLayout действительно решает проблему, поскольку по умолчанию для BorderLayout используется значение CENTER, что позволяет отображать компонент. - person matt forsythe; 06.05.2014
comment
Camickr говорит, что вам действительно следует реализовать методы get*Size(), чтобы менеджеры компоновки могли выполнять свою работу. Многие менеджеры компоновки используют максимальный, минимальный и предпочтительный размер, чтобы определить, как выделить пространство в контейнере для различных компонентов внутри этого контейнера. Если вы не реализуете эти методы, ваш компонент не будет хорошо работать с другими (или даже сам по себе) при использовании с этими макетами. Это может работать, если вы не используете свой компонент в этих контейнерах, но это не совсем правильно. Вот почему ваш компонент не работает с GridBagLayout. - person matt forsythe; 06.05.2014
comment
Еще один комментарий о том, следует ли переопределять методы get*Size(): часто нет необходимости переопределять методы, потому что вы можете просто вызвать соответствующие методы установки в конструкторе (если вы заранее знаете свой минимальный/максимальный/предпочтительный размер). Тогда компонент будет корректно работать с менеджерами компоновки. Я говорю это потому, что геттеры для этих свойств на самом деле нетривиальны, поэтому их переопределение может быть немного трудоемким, если вам это не нужно. Тем не менее, переопределение все же может понадобиться, если ваши размеры должны рассчитываться динамически. - person matt forsythe; 06.05.2014

Сделал некоторые настройки вашего класса java, единственное изменение, которое я сделал, это добавить ваш новый MyBall непосредственно на панель содержимого JFrame, попробуйте запустить это, и вы увидите красный круг на вашем jframe

public class TestBall {
    public static void main(String[] args) {
        new TestBall();
    }

    public TestBall() {

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.getContentPane().add(new MyBall(30,30,10));
        frame.pack();
        frame.setSize(500, 300);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

class MyBall extends JComponent
{
    private static final long serialVersionUID = 1L;
    public MyBall() { }
    public MyBall(int x, int y, int diameter)
    {
        super();
        this.setLocation(x, y);
        this.setSize(diameter, diameter);
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.setColor(Color.red);
        g.fillOval(0, 0, 100, 100);
    }
}
person rahul pasricha    schedule 05.05.2014

Конструктор: мы просто задаем местоположение точкам, которые передаются через параметры конструктора. Это место, в котором будет находиться этот компонент. Точно так же мы устанавливаем размер (width x height).

paintComponent: здесь мы просто рисуем овал поверх переданного графического объекта.

Другая часть — это просто тест, который показывает, что компонент правильно создан и показан.

person Dmitry Ginzburg    schedule 05.05.2014