2011-12-07 32 views
5

在此自定義邊框類中,我定義了RoundRectangle2D形狀。該對象用於繪製邊框。不幸的是,由於JComponentpaint方法在paintBorder之前調用paintComponent,因此將Graphics剪輯設置爲RoundRectangle2D形狀不起作用;即使我發出repaint。因此,組件將在其邊界外繪畫,這是不可取的。自定義邊框外的組件繪畫

所以,我想知道:如何讓組件在自定義邊框內獨家繪製

我考慮的一種方法是在paintComponent方法中獲取組件的Border對象。然後將這個對象轉換爲合適的類,其中我定義了影響剪輯的參數。但是這看起來不像是一個「健全」的設計。


編輯 -

import java.awt.BasicStroke; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.FlowLayout; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Insets; 
import java.awt.RenderingHints; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 
import javax.swing.border.AbstractBorder; 

class JRoundedCornerBorder extends AbstractBorder 
{ 
    private static final long serialVersionUID = 7644739936531926341L; 
    private static final int THICKNESS = 2; 

    JRoundedCornerBorder() 
    { 
     super(); 
    } 

    @Override 
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) 
    { 
     Graphics2D g2 = (Graphics2D)g.create(); 

     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     if(c.hasFocus()) 
     { 
      g2.setColor(Color.BLUE); 
     } 
     else 
     { 
      g2.setColor(Color.BLACK); 
     } 
     g2.setStroke(new BasicStroke(THICKNESS, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 
     g2.drawRoundRect(THICKNESS, THICKNESS, width - THICKNESS - 2, height - THICKNESS - 2, 20, 20); 

     g2.dispose(); 
    } 

    @Override 
    public Insets getBorderInsets(Component c) 
    { 
     return new Insets(THICKNESS, THICKNESS, THICKNESS, THICKNESS); 
    } 

    @Override 
    public Insets getBorderInsets(Component c, Insets insets) 
    { 
     insets.left = insets.top = insets.right = insets.bottom = THICKNESS; 
     return insets; 
    } 

    public boolean isBorderOpaque() { 
     return false; 
    } 

    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       final JFrame frame = new JFrame(); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new FlowLayout()); 

       // Add button with custom border 
       final JButton button = new JButton("Hello"); 
       button.setBorder(new JRoundedCornerBorder()); 
       button.setBackground(Color.YELLOW); 
       button.setPreferredSize(new Dimension(200, 200)); 
       frame.add(button); 

       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 

enter image description here

紅圈突出,其中部分超出它的邊界。

+0

應該自動發生,只要你的邊界正確地報告其插圖(又名:空間,它要繪製成)最佳展現你的代碼:-) – kleopatra

+0

只記得(說沒有看到可能相關或不相關,硬代碼):AbstractBorder用於getBorderInsets中的錯誤 - 確保正確實現。 – kleopatra

+0

@kleopatra,我想我正在報告我的插頁。請參閱我的編輯! – mre

回答

6

啊......最後我得到了它(無緣roundBorder出於某種原因,我的錯:-)這個按鈕被簡單地遵循其合同:它說不透明,所以必須填滿它的整個區域,包括邊界的區域。所以,設置一個夾子(你可以在的paintComponent做)將使違反合同:

// DO NOT - a opaque component violates its contract, as it will not fill 
// its complete area 
     @Override 
     protected void paintComponent(Graphics g) { 
      if (getBorder() instanceof JRoundedCornerBorder) { 
       Shape borderShape = ((JRoundedCornerBorder) getBorder()). 
        getBorderShape(getWidth(), getHeight()); 
       g.setClip(borderShape); 
      } 
      super.paintComponent(g); 
     } 

醜陋,但安全將自身報告爲透明並接管背景畫自己:

 @Override 
     protected void paintComponent(Graphics g) { 
      if (getBorder() instanceof JRoundedCornerBorder) { 
       g.setColor(getBackground()); 
       Shape borderShape = ((JRoundedCornerBorder) getBorder()) 
        .getBorderShape(getWidth(), getHeight()); 
       ((Graphics2D) g).fill(borderShape); 
      } 
      super.paintComponent(g); 
     } 

     @Override 
     public boolean isContentAreaFilled() { 
      if (getBorder() instanceof JRoundedCornerBorder) return false; 
      return super.isContentAreaFilled(); 
     } 

     // using 
     button.setOpaque(false); 
3

你如何繪製邊框?你有沒有實現Border接口? 有3種方法來放置那裏所有的邊境邏輯

void paintBorder(Component c, Graphics g, int x, int y, int width, int height); 
    Insets getBorderInsets(Component c); 
    boolean isBorderOpaque(); 
+0

我分類爲'AbstractBorder'。當我在'paintBorder'中實現邏輯時,組件的某些部分被繪製在邊界之外,這是我不想要的。所以,我認爲通過較早地設置剪輯來防止這種情況發生,我不知道如何去實現這些。 OMG。 – mre

5

不回答你的問題,只是一個想法如何做到這一點

enter image description here

從很長的代碼
import java.awt.*; 
    import java.awt.geom.*; 
    import javax.swing.*; 
    import javax.swing.event.*; 

    public class Panel2Test { 

     public static void main(String[] args) { 
      SwingUtilities.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        ShadeOptionsPanel shadeOptions = new ShadeOptionsPanel(); 
        ShadowSelector shadowSelector = new ShadowSelector(shadeOptions); 
        //ComponentSource componentSource = new ComponentSource(shadeOptions); 
        JFrame f = new JFrame("Rounded Concept Demo with Shadows"); 
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        f.add(shadowSelector, "North"); 
        f.add(shadeOptions); 
        //f.add(componentSource, "South"); 
        f.setSize(300, 200); 
        f.setLocation(150, 150); 
        f.setVisible(true); 
       } 
      }); 
     } 

     private Panel2Test() { 
     } 
    } 

    class ShadeOptionsPanel extends JPanel { 

     private static final long serialVersionUID = 1L; 
     private final int PAD, DIA, BORDER; 
     private Color colorIn, colorOut; 
     private int xc, yc; 
     private Ellipse2D eIn, eOut; 
     private GradientPaint gradient; 
     private CustomPaint customPaint; 
     private Area arcBorder; 
     private int width, height; 
     private Point2D neOrigin, nwOrigin, swOrigin, seOrigin, neDiag, nwDiag, swDiag, seDiag; 
     private final static int NORTHEAST = 0, NORTHWEST = 1, SOUTHWEST = 2, SOUTHEAST = 3; 
     public int shadowVertex = 3; 

     public ShadeOptionsPanel() { 
      PAD = 25; 
      DIA = 75; 
      BORDER = 10; 
      colorIn = Color.black; 
      colorOut = getBackground(); 
     } 

     @Override 
     public void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2 = (Graphics2D) g; 
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
      width = getWidth(); 
      height = getHeight(); 
      g2.drawRoundRect(PAD, PAD, width - 2 * PAD, height - 2 * PAD, DIA, DIA); 
      calculateArcOrigins(); 
      calculateCardinalDiagonals(); 
      drawVertexArc(g2, shadowVertex); 
      switch (shadowVertex) { 
       case NORTHEAST: 
        drawNorthSide(g2); 
        drawEastSide(g2); 
        xc = PAD + DIA/2; // draw northwest arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(nwOrigin, nwDiag); 
        eOut = getOuterEllipse(nwOrigin, nwDiag); 
        arcBorder = getArcArea(eIn, eOut, 90.0); 
        g2.fill(arcBorder); 
        xc = width - PAD - DIA/2; // draw southeast arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(seOrigin, seDiag); 
        eOut = getOuterEllipse(seOrigin, seDiag); 
        arcBorder = getArcArea(eIn, eOut, 270.0); 
        g2.fill(arcBorder); 
        break; 
       case NORTHWEST: 
        drawNorthSide(g2); 
        drawWestSide(g2); 
        xc = width - PAD - DIA/2;// draw northeast arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(neOrigin, neDiag); 
        eOut = getOuterEllipse(neOrigin, neDiag); 
        arcBorder = getArcArea(eIn, eOut, 0.0); 
        g2.fill(arcBorder); 
        xc = PAD + DIA/2;// draw southwest arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(swOrigin, swDiag); 
        eOut = getOuterEllipse(swOrigin, swDiag); 
        arcBorder = getArcArea(eIn, eOut, 180.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHWEST: 
        drawWestSide(g2); 
        drawSouthSide(g2); 
        xc = PAD + DIA/2; // draw northwest arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(nwOrigin, nwDiag); 
        eOut = getOuterEllipse(nwOrigin, nwDiag); 
        arcBorder = getArcArea(eIn, eOut, 90.0); 
        g2.fill(arcBorder); 
        xc = width - PAD - DIA/2; // draw the southeast arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(seOrigin, seDiag); 
        eOut = getOuterEllipse(seOrigin, seDiag); 
        arcBorder = getArcArea(eIn, eOut, 270.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHEAST: 
        drawEastSide(g2); 
        drawSouthSide(g2); 
        xc = width - PAD - DIA/2; // draw northeast arc 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(neOrigin, neDiag); 
        eOut = getOuterEllipse(neOrigin, neDiag); 
        arcBorder = getArcArea(eIn, eOut, 0.0); 
        g2.fill(arcBorder); 
        xc = PAD + DIA/2; // draw southwest arc 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = getInnerEllipse(swOrigin, swDiag); 
        eOut = getOuterEllipse(swOrigin, swDiag); 
        arcBorder = getArcArea(eIn, eOut, 180.0); 
        g2.fill(arcBorder); 
      } 
     } 

     private Ellipse2D getInnerEllipse(Point2D center, Point2D corner) { 
      return new Ellipse2D.Double(center.getX() - DIA/2, 
        center.getY() - DIA/2, DIA, DIA); 
     } 

     private Ellipse2D getOuterEllipse(Point2D center, Point2D corner) { 
      int w = DIA, h = DIA; 
      if (shadowVertex < 2) { 
       if (center.getY() > corner.getY()) { 
        h += 2 * BORDER; 
       } else { 
        w += 2 * BORDER; 
       } 
      } else if (center.getY() > corner.getY()) { 
       w += 2 * BORDER; 
      } else { 
       h += 2 * BORDER; 
      } 
      return new Ellipse2D.Double(center.getX() - w/2, center.getY() - h/2, w, h); 
     } 

     private Area getArcArea(Ellipse2D e1, Ellipse2D e2, double start) { 
      Arc2D arc1 = new Arc2D.Double(e1.getBounds2D(), start, 90.0, Arc2D.PIE); 
      Arc2D arc2 = new Arc2D.Double(e2.getBounds2D(), start, 90.0, Arc2D.PIE); 
      Area arc = new Area(arc2); 
      arc.subtract(new Area(arc1)); 
      return arc; 
     } 

     private void drawNorthSide(Graphics2D g2) { 
      gradient = new GradientPaint(width/2, PAD - BORDER, colorOut, 
        width/2, PAD, colorIn); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(PAD + DIA/2, PAD - BORDER, 
        width - 2 * (PAD + DIA/2) + 1, BORDER)); 
     } 

     private void drawWestSide(Graphics2D g2) { 
      gradient = new GradientPaint(PAD - BORDER, height/2, colorOut, 
        PAD, height/2, colorIn); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(PAD - BORDER, PAD + DIA/2, 
        BORDER, height - 2 * (PAD + DIA/2) + 1)); 
     } 

     private void drawSouthSide(Graphics2D g2) { 
      gradient = new GradientPaint(width/2, height - PAD, colorIn, 
        width/2, height - PAD + BORDER, colorOut); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(PAD + DIA/2, height - PAD, 
        width - 2 * (PAD + DIA/2) + 1, BORDER)); 
     } 

     private void drawEastSide(Graphics2D g2) { 
      gradient = new GradientPaint(width - PAD, height/2, colorIn, 
        width - PAD + BORDER, height/2, colorOut); 
      g2.setPaint(gradient); 
      g2.fill(new Rectangle2D.Double(width - PAD, PAD + DIA/2, 
        BORDER, height - 2 * (PAD + DIA/2) + 1)); 
     } 

     /** 
     * Draws the central, full-shaded arc (opposite of the unshaded arc). 
     */ 
     private void drawVertexArc(Graphics2D g2, int index) { 
      switch (index) { 
       case NORTHEAST: 
        xc = width - PAD - DIA/2; 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(width - PAD - DIA, PAD, DIA, DIA); 
        eOut = new Ellipse2D.Double(width - PAD - DIA - BORDER, PAD - BORDER, 
          DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 0.0); 
        g2.fill(arcBorder); 
        break; 
       case NORTHWEST: 
        xc = PAD + DIA/2; 
        yc = PAD + DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(PAD, PAD, DIA, DIA); 
        eOut = new Ellipse2D.Double(PAD - BORDER, PAD - BORDER, 
          DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 90.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHWEST: 
        xc = PAD + DIA/2; 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(PAD, height - PAD - DIA, DIA, DIA); 
        eOut = new Ellipse2D.Double(PAD - BORDER, height - PAD - DIA - BORDER, 
          DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 180.0); 
        g2.fill(arcBorder); 
        break; 
       case SOUTHEAST: 
        xc = width - PAD - DIA/2; 
        yc = height - PAD - DIA/2; 
        customPaint = new CustomPaint(xc, yc, new Point2D.Double(0, DIA/2), 
          DIA/2, BORDER, colorIn, colorOut); 
        g2.setPaint(customPaint); 
        eIn = new Ellipse2D.Double(width - PAD - DIA, height - PAD - DIA, DIA, DIA); 
        eOut = new Ellipse2D.Double(width - PAD - DIA - BORDER, 
          height - PAD - DIA - BORDER, DIA + 2 * BORDER, DIA + 2 * BORDER); 
        arcBorder = getArcArea(eIn, eOut, 270.0); 
        g2.fill(arcBorder); 
      } 
     } 

     private void calculateArcOrigins() { 
      neOrigin = new Point2D.Double(width - PAD - DIA/2, PAD + DIA/2); 
      nwOrigin = new Point2D.Double(PAD + DIA/2, PAD + DIA/2); 
      swOrigin = new Point2D.Double(PAD + DIA/2, height - PAD - DIA/2); 
      seOrigin = new Point2D.Double(width - PAD - DIA/2, height - PAD - DIA/2); 
     } 

     private void calculateCardinalDiagonals() { 
      neDiag = new Point2D.Double(neOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(45))/2, 
        neOrigin.getY() - DIA * Math.sin(Math.toRadians(45))/2); 
      nwDiag = new Point2D.Double(nwOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(135))/2, 
        nwOrigin.getY() - DIA * Math.sin(Math.toRadians(135))/2); 
      swDiag = new Point2D.Double(swOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(225))/2, 
        swOrigin.getY() - DIA * Math.sin(Math.toRadians(225))/2); 
      seDiag = new Point2D.Double(seOrigin.getX() 
        + DIA * Math.cos(Math.toRadians(315))/2, 
        seOrigin.getY() - DIA * Math.sin(Math.toRadians(315))/2); 
     } 

     public Dimension getInnerSize() { 
      return new Dimension((int) nwOrigin.distance(neOrigin), 
        (int) nwOrigin.distance(swOrigin)); 
     } 
    } 

    class ShadowSelector extends JPanel { 

     private static final long serialVersionUID = 1L; 
     private ShadeOptionsPanel soPanel; 
     private String[] directions = {"northeast", "northwest", "southwest", "southeast"}; 

     public ShadowSelector(ShadeOptionsPanel sop) { 
      soPanel = sop; 

      final SpinnerListModel model = new SpinnerListModel(directions); 
      model.setValue(directions[3]); 
      JSpinner spinner = new JSpinner(model); 
      spinner.setPreferredSize(new Dimension(90, spinner.getPreferredSize().height)); 
      spinner.addChangeListener(new ChangeListener() { 

       @Override 
       public void stateChanged(ChangeEvent e) { 
        String value = (String) model.getValue(); 
        soPanel.shadowVertex = model.getList().indexOf(value); 
        soPanel.repaint(); 
       } 
      }); 
      add(new JLabel("shadow vertex", JLabel.RIGHT)); 
      add(spinner); 
     } 
    } 

class CustomPaint implements Paint { 

    Point2D originP, radiusP; 
    int radius, border; 
    Color colorIn, colorOut; 

    public CustomPaint(int x, int y, Point2D radiusP, 
      int radius, int border, 
      Color colorIn, Color colorOut) { 
     originP = new Point2D.Double(x, y); 
     this.radiusP = radiusP; 
     this.radius = radius; 
     this.border = border; 
     this.colorIn = colorIn; 
     this.colorOut = colorOut; 
    } 

    @Override 
    public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) { 
     Point2D xformOrigin = xform.transform(originP, null), xformRadius = xform.deltaTransform(radiusP, null); 
     return new CustomPaintContext(xformOrigin, xformRadius, radius, border, colorIn, colorOut); 
    } 

    @Override 
    public int getTransparency() { 
     int alphaIn = colorIn.getAlpha(); 
     int alphaOut = colorOut.getAlpha(); 
     return (((alphaIn & alphaOut) == 0xff) ? OPAQUE : TRANSLUCENT); 
    } 
} 

class CustomPaintContext implements PaintContext { 

    Point2D originP, radiusP; 
    Color colorIn, colorOut; 
    int radius, border; 

    public CustomPaintContext(Point2D originP, Point2D radiusP, int radius, int border, Color colorIn, Color colorOut) { 
     this.originP = originP; 
     this.radiusP = radiusP; 
     this.radius = radius; 
     this.border = border; 
     this.colorIn = colorIn; 
     this.colorOut = colorOut; 
    } 

    @Override 
    public void dispose() { 
    } 

    @Override 
    public ColorModel getColorModel() { 
     return ColorModel.getRGBdefault(); 
    } 

    @Override 
    public Raster getRaster(int x, int y, int w, int h) { 
     WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h); 
     int[] data = new int[w * h * 4]; 
     for (int j = 0; j < h; j++) { 
      for (int i = 0; i < w; i++) { 
       double distance = originP.distance(x + i, y + j); 
       double r = radiusP.distance(radius, radius); 
       double ratio = distance - r < 0 ? 0.0 : (distance - r)/border; 
       if (ratio > 1.0) { 
        ratio = 1.0; 
       } 
       int base = (j * w + i) * 4; 
       data[base + 0] = (int) (colorIn.getRed() + ratio * (colorOut.getRed() - colorIn.getRed())); 
       data[base + 1] = (int) (colorIn.getGreen() + ratio * (colorOut.getGreen() - colorIn.getGreen())); 
       data[base + 2] = (int) (colorIn.getBlue() + ratio * (colorOut.getBlue() - colorIn.getBlue())); 
       data[base + 3] = (int) (colorIn.getAlpha() + ratio * (colorOut.getAlpha() - colorIn.getAlpha())); 
      } 
     } 
     raster.setPixels(0, 0, w, h, data); 
     return raster; 
    } 
} 
+0

OMG。 CustomPaint實現的任何代碼? – ThomasRS

+1

@Thomas基本上(不包括我的人)全部來自[Top10 in All Time](http:// stackoverflow。com/tags/swing/topusers)在代數中是非常好的:-) – mKorbel

+0

@Thomas,我發現代碼很有趣,所以谷歌一點,發現這個,http://www.pastemine.com/97q0。在那裏你可以找到CustomPaint的實現。無論如何,這是另一個非工作的例子,所以我會修復代碼 – albfan