как избавиться от разных локаций от краски и покрасить сразу - PullRequest
1 голос
/ 02 мая 2020

Я работаю над кнопками произвольной формы. Первый этап пройден успешно, так как кнопки выглядят как положено. Второй этап завершается неудачей, так как paintImmediate вызывает из doClick paintts в совершенно другое место. Я попробовал несколько способов использовать AffineTransforms, но не смог рассчитать правильное местоположение.

Любая подсказка приветствуется.

Я попробовал вот что - Buttonclass:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JButton;
import javax.swing.SwingUtilities;


public class MyCircleButton extends JButton implements MouseListener {
   public MyCircleButton(String text, double xCenter, double yCenter, double rOuter, double rInner,
         double start, double extend) {
      super(text);
      this.text     = text;
      this.xCenter  = xCenter;
      this.yCenter  = yCenter;
      this.xOffset  = xCenter - rOuter;
      this.yOffset  = yCenter - rOuter;
      this.rOuter   = rOuter;
      this.rInner   = rInner;
      this.angStart = start;
      this.angSize  = extend;
      int fontSize = (int) (rOuter * 0.15);

      this.font = new Font("Arial", Font.BOLD, fontSize);
      this.addMouseListener(this);
      calcShape();
      calcText();
   }


   @Override
   public boolean contains(int x, int y) {
      return shape.contains(x, y);
   }


   @Override
   public void doClick(int pressTime) {
      model.setPressed(true);
      Component c = SwingUtilities.getRoot(this);
      Graphics  g = c.getGraphics();
      Rectangle r = shape.getBounds();

      if (g != null) {
         Graphics2D      g2 = (Graphics2D) g;
         Point           p  = SwingUtilities.convertPoint(this, new Point(0, 0), getParent());
         AffineTransform t  = new AffineTransform();

         t.setToTranslation(p.getX(), p.getY());
         //         t.setToTranslation(xText, yText);
         //         t.setToTranslation(xCenter, yCenter);
         //         g2.getTransform().concatenate(t);
         g2.setTransform(t);
         paint(g);
      }
      try {
         Thread.currentThread().sleep(pressTime);
      } catch (InterruptedException ie) {
      }
      model.setPressed(false);
   }


   @Override
   public int getHeight() {
      return shape.getBounds().height;
   }


   @Override
   public int getWidth() {
      return shape.getBounds().width;
   }


   @Override
   public int getX() {
      return shape.getBounds().x;
   }


   @Override
   public int getY() {
      return shape.getBounds().y;
   }


   @Override
   public void mouseClicked(MouseEvent me) {
      System.out.println("mouse clicked CIRCLE-BUTTON<" + text + "> at " + me.getX() + "/" + me.getY());
      doClick(50);
   }


   @Override
   public void mouseEntered(MouseEvent me) {
   }


   @Override
   public void mouseExited(MouseEvent me) {
   }


   @Override
   public void mousePressed(MouseEvent me) {
   }


   @Override
   public void mouseReleased(MouseEvent me) {
   }


   @Override
   public void paint(Graphics g) {
      Graphics2D  g2        = (Graphics2D) g;
      Shape       oldClip   = g2.getClip();
      Paint       oldPaint  = g2.getPaint();
      Stroke      oldStroke = g2.getStroke();
      Font        oldFont   = g2.getFont();
      FontMetrics fm        = g2.getFontMetrics(font);
      Rectangle2D tb        = fm.getStringBounds(text, g2);

      g2.setClip(this.shape);
      if (getModel().isArmed() || getModel().isPressed())
         g2.setPaint(Color.RED);
      else
         g2.setPaint(Color.GREEN);
      g2.fill(shape);
      g2.setStroke(new BasicStroke(4));
      g2.setPaint(Color.WHITE);
      g2.draw(shape);

      g2.setPaint(Color.BLACK);
      g2.setFont(font);
      g2.drawString(text, (int) (xText + 2 - tb.getWidth() / 2), (int) (yText + 2 + fm.getAscent() * 0.35));
      g2.setPaint(Color.WHITE);
      g2.drawString(text, (int) (xText - tb.getWidth() / 2), (int) (yText + fm.getAscent() * 0.35));

      g2.setFont(oldFont);
      g2.setStroke(oldStroke);
      g2.setPaint(oldPaint);
      g2.setClip(oldClip);
   }


   protected void calcShape() {
      Arc2D     a        = new Arc2D.Double(xOffset, yOffset, 2 * rOuter, 2 * rOuter, angStart, angSize,
            Arc2D.PIE);
      Ellipse2D e        = new Ellipse2D.Double(xOffset + rOuter - rInner, yOffset + rOuter - rInner,
            2 * rInner, 2 * rInner);
      Area      resShape = new Area(a);

      resShape.subtract(new Area(e));
      this.shape = resShape;
   }


   protected void calcText() {
      double angle = angStart + angSize / 2;
      double r     = rInner + (rOuter - rInner) / 2;
      double xOff  = r * Math.sin(Math.toRadians(90 + angle));
      double yOff  = r * Math.cos(Math.toRadians(90 + angle));

      xText = xCenter + (Math.abs(xOff) > 0.1 ? xOff : 0);
      yText = yCenter + (Math.abs(yOff) > 0.1 ? yOff : 0);
   }


   private double xCenter;
   private double yCenter;
   private double xOffset;
   private double yOffset;
   private double rOuter;
   private double rInner;
   private double angStart;
   private double angSize;
   private double xText;
   private double yText;
   private String text;
   private Shape  shape;
   private Font   font;
}

и класс Pane:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class TestCirclePane extends JPanel {
   public TestCirclePane(int w, int h) {
      diameter = (int) (0.9 * (w < h ? w : h));
      Dimension s = new Dimension(w, h);

      xCenter = w / 2;
      yCenter = h / 2;
      setMinimumSize(s);
      setPreferredSize(s);
      setLayout(null);
      createComponents(this);
   }


   @Override
   public void paintChildren(Graphics g) {
      for (Component c : getComponents()) {
         if (c instanceof MyCircleButton)
            ((MyCircleButton) c).paint(g);
      }
   }


   protected void createComponents(JPanel p) {
      double r   = diameter / 2;
      double w   = r * 0.66;
      double r3o = r;
      double r3i = r - w * 0.5;

      add(new MyCircleButton("B0", xCenter, yCenter, r3o, r3i, 135, 90));
      add(new MyCircleButton("B1", xCenter, yCenter, r3o, r3i, 45, 90));
      add(new MyCircleButton("B2", xCenter, yCenter, r3o, r3i, -45, 90));
      add(new MyCircleButton("B3", xCenter, yCenter, r3o, r3i, 225, 90));
   }


   @Override
   protected void paintComponent(Graphics arg0) {
      super.paintComponent(arg0);
      Graphics2D g2       = (Graphics2D) arg0;
      Dimension  d        = getSize();
      Paint      oldPaint = g2.getPaint();

      g2.setPaint(Color.WHITE);
      g2.fill3DRect(0, 0, d.width, d.height, true);
      g2.setPaint(Color.LIGHT_GRAY);
      g2.drawLine(0, yCenter, d.width, yCenter);
      g2.drawLine(xCenter, 0, xCenter, d.height);
      g2.setPaint(oldPaint);
   }


   public static void createWindow() {
      JFrame frame  = new JFrame("Test - CircleButton");
      JPanel client = new JPanel();

      client.setLayout(new BorderLayout());
      client.add(placeHolder("top"), BorderLayout.NORTH);
      client.add(placeHolder("bottom"), BorderLayout.SOUTH);
      client.add(placeHolder("left"), BorderLayout.WEST);
      client.add(placeHolder("right"), BorderLayout.EAST);
      client.add(new TestCirclePane(800, 600), BorderLayout.CENTER);

      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.add(client);
      frame.pack();
      frame.setVisible(true);
   }


   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            createWindow();
         }
      });
   }


   public static JComponent placeHolder(String s) {
      JLabel l = new JLabel(s);

      l.setPreferredSize(new Dimension(300, 300));

      return l;
   }


   private int               xCenter;
   private int               yCenter;
   private int               diameter;
   private static final long serialVersionUID = 1L;
}

Ответы [ 3 ]

0 голосов
/ 04 мая 2020

Так что это вариант, который работает. Снова сначала класс Button:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JButton;
import javax.swing.SwingUtilities;


public class MyCircleButton extends JButton implements MouseListener {
   public MyCircleButton(String text, double xCenter, double yCenter, double rOuter, double rInner,
         double start, double extend) {
      super(text);
      this.xCenter  = xCenter;
      this.yCenter  = yCenter;
      this.xOffset  = xCenter - rOuter;
      this.yOffset  = yCenter - rOuter;
      this.rOuter   = rOuter;
      this.rInner   = rInner;
      this.angStart = start;
      this.angSize  = extend;
      int fontSize = (int) ((rOuter - rInner) * 0.5);

      setFont(new Font("Arial", Font.BOLD, fontSize));
      this.addMouseListener(this);
      setBorderPainted(false);
      setContentAreaFilled(false);
      calcShape();
      calcText();
   }


   @Override
   public boolean contains(int x, int y) {
      return shape.contains(x, y);
   }


   @Override
   public int getHeight() {
      return shape.getBounds().height;
   }


   @Override
   public int getWidth() {
      return shape.getBounds().width;
   }


   @Override
   public int getX() {
      return shape.getBounds().x;
   }


   @Override
   public int getY() {
      return shape.getBounds().y;
   }


   @Override
   public void mouseClicked(MouseEvent me) {
   }


   @Override
   public void mouseEntered(MouseEvent me) {
   }


   @Override
   public void mouseExited(MouseEvent me) {
   }


   @Override
   public void mousePressed(MouseEvent me) {
      model.setArmed(true);
      model.setPressed(true);
      paintImmediately();
   }


   @Override
   public void mouseReleased(MouseEvent me) {
      model.setArmed(false);
      model.setPressed(false);
      paintImmediately();
   }


   @Override
   public void paintComponent(Graphics g) {
      Graphics2D      g2        = (Graphics2D) g;
      Paint           oldPaint  = g2.getPaint();
      Stroke          oldStroke = g2.getStroke();
      Font            oldFont   = g2.getFont();
      AffineTransform oldTrans  = g2.getTransform();
      FontMetrics     fm        = g2.getFontMetrics(getFont());
      Rectangle2D     tb        = fm.getStringBounds(getText(), g2);
      AffineTransform t         = new AffineTransform();

      t.setToTranslation(-1 * shape.getBounds().getX(), -1 * shape.getBounds().getY());
      t.concatenate(oldTrans);
      g2.setTransform(t);
      if (getModel().isArmed() || getModel().isPressed())
         g2.setPaint(Color.RED);
      else
         g2.setPaint(Color.GREEN);
      g2.fill(shape);
      g2.setStroke(new BasicStroke(4));
      g2.setPaint(Color.WHITE);
      g2.draw(shape);

      g2.setPaint(Color.BLACK);
      g2.setFont(getFont());
      g2.drawString(getText(), (int) (xText + 2 - tb.getWidth() / 2),
            (int) (yText + 2 + fm.getAscent() * 0.35));
      g2.setPaint(Color.WHITE);
      g2.drawString(getText(), (int) (xText - tb.getWidth() / 2), (int) (yText + fm.getAscent() * 0.35));
      g2.setFont(oldFont);
      g2.setStroke(oldStroke);
      g2.setPaint(oldPaint);
   }


   protected void calcShape() {
      Arc2D     a        = new Arc2D.Double(xOffset, yOffset, 2 * rOuter, 2 * rOuter, angStart, angSize,
            Arc2D.PIE);
      Ellipse2D e        = new Ellipse2D.Double(xOffset + rOuter - rInner, yOffset + rOuter - rInner,
            2 * rInner, 2 * rInner);
      Area      resShape = new Area(a);

      resShape.subtract(new Area(e));
      this.shape = resShape;
   }


   protected void calcText() {
      double angle = angStart + angSize / 2;
      double r     = rInner + (rOuter - rInner) / 2;
      double xOff  = r * Math.sin(Math.toRadians(90 + angle));
      double yOff  = r * Math.cos(Math.toRadians(90 + angle));

      xText = xCenter + (Math.abs(xOff) > 0.1 ? xOff : 0);
      yText = yCenter + (Math.abs(yOff) > 0.1 ? yOff : 0);
   }


   protected void paintImmediately() {
      Component       c  = SwingUtilities.getRoot(this);
      Graphics        g  = c.getGraphics();
      Graphics2D      g2 = (Graphics2D) g;
      Point           p  = SwingUtilities.convertPoint(this, new Point(0, 0), c);
      AffineTransform oT = g2.getTransform();
      AffineTransform t  = new AffineTransform();

      t.setToTranslation(p.getX(), p.getY());
      g2.setTransform(t);
      paintComponent(g);
      g2.setTransform(oT);
   }


   private double xCenter;
   private double yCenter;
   private double xOffset;
   private double yOffset;
   private double rOuter;
   private double rInner;
   private double angStart;
   private double angSize;
   private double xText;
   private double yText;
   private Shape  shape;
}

, а теперь класс Pane:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class TestCirclePane extends JPanel {
   public TestCirclePane(int w, int h) {
      diameter = (int) (0.9 * (w < h ? w : h));
      Dimension s = new Dimension(w, h);

      xCenter = w / 2;
      yCenter = h / 2;
      setMinimumSize(s);
      setPreferredSize(s);
      setLayout(null);
      createComponents(this);
   }


   protected void createComponents(JPanel p) {
      double r   = diameter / 2;
      double w   = r * 0.66;
      double r3o = r;
      double r3i = r - w * 0.5;

      add(new MyCircleButton("B0", xCenter, yCenter, r3o, r3i, 135, 90));
      add(new MyCircleButton("B1", xCenter, yCenter, r3o, r3i, 45, 90));
      add(new MyCircleButton("B2", xCenter, yCenter, r3o, r3i, -45, 90));
      add(new MyCircleButton("B3", xCenter, yCenter, r3o, r3i, 225, 90));
   }


   @Override
   protected void paintComponent(Graphics arg0) {
      super.paintComponent(arg0);
      Graphics2D g2       = (Graphics2D) arg0;
      Dimension  d        = getSize();
      Paint      oldPaint = g2.getPaint();

      g2.setPaint(Color.WHITE);
      g2.fill3DRect(0, 0, d.width, d.height, true);
      g2.setPaint(Color.LIGHT_GRAY);
      g2.drawLine(0, yCenter, d.width, yCenter);
      g2.drawLine(xCenter, 0, xCenter, d.height);
      g2.setPaint(oldPaint);
   }


   public static void createWindow() {
      JFrame frame  = new JFrame("Test - CircleButton");
      JPanel client = new JPanel();

      client.setLayout(new BorderLayout());
      client.add(placeHolder("top"), BorderLayout.NORTH);
      client.add(placeHolder("bottom"), BorderLayout.SOUTH);
      client.add(placeHolder("left"), BorderLayout.WEST);
      client.add(placeHolder("right"), BorderLayout.EAST);
      client.add(new TestCirclePane(800, 600), BorderLayout.CENTER);

      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.add(client);
      frame.pack();
      frame.setVisible(true);
   }


   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            createWindow();
         }
      });
   }


   public static JComponent placeHolder(String s) {
      JLabel l = new JLabel(s);

      l.setPreferredSize(new Dimension(300, 300));

      return l;
   }


   private int               xCenter;
   private int               yCenter;
   private int               diameter;
   private static final long serialVersionUID = 1L;
}

Поскольку кнопка с этой формой не ведет себя как «обычная» кнопка, мне нужно было добавить слушателя мыши. Для визуальной обратной связи по клику я использовал mousePressed() и mouseReleased(). Но когда мой paintComponent() был вызван из-за пределов цепочки краски, я понял, что он не знает своего происхождения (кнопок).

Поэтому я подумал, что внутри моей краски сразу же находится за пределами концепции рисования кнопок , Поэтому я подготовил среду, как суперкласс будет делать при обычных операциях рисования. Ну, то, что я думал, суперкласс сделает. Не знаю - просто предположение.

Так что, если вы думаете, что мое кодирование неверно, пожалуйста, объясните мне, как сделать лучше.

0 голосов
/ 08 мая 2020

Я наконец-то понял трюк =: O

Я переместил создание фигур из панели в Layoutmanager, и он устанавливает размер и расположение кнопок, которые теперь работают, как и ожидалось. Без перегрузки позиции или размера получателей и причины без покраски Немедленно:)

Спасибо за помощь.

0 голосов
/ 02 мая 2020

Не уверен, поможет ли это, но вот мои два цента:

  1. Не переопределяйте paintChildren(…). JPanel автоматически перекрасит любой компонент, добавленный на панель.

  2. Не перекрывать краску (…). Пользовательское рисование выполняется путем переопределения paintComponent(…), и вам нужно вызвать super.paintComponent(…), иначе вы можете иметь артефакты рисования.

  3. Не переопределяйте getX( и getY(), эти методы используется для управления расположением компонента на панели.

  4. Я также думаю, что вам не следует переопределять getWidth () и getHeight (). Я думаю, вы должны переопределить метод getPreferredSize() для управления размером компонента.

  5. Если вы собираетесь использовать нулевую разметку, то ваш код отвечает за установку размер / расположение каждого компонента на панели.

  6. Не переводить в вашем методе рисования. Окраска компонента всегда должна выполняться относительно (0, 0). Расположение определяет, где компонент находится на панели.

  7. Не вызывайте Thread.sleep () в методе doClick (). Thread.sleep () приведет к сну EDT, то есть GUI не сможет перерисоваться. Не уверен, почему вам даже нужно переопределить метод.

...