重绘 Swing 组件会产生糟糕的结果

Posted

技术标签:

【中文标题】重绘 Swing 组件会产生糟糕的结果【英文标题】:Repainting a Swing component produce an awful result 【发布时间】:2015-05-16 14:02:37 【问题描述】:

我“绘制”了一个组件来制作一种旋钮,因为我没有找到一种方法来拥有一个“圆形 JSlider”。我使用抗锯齿来获得良好的效果。当我用鼠标按下组件时,我会修改光标位置并要求组件重新绘制。然后,问题出现了:我repain的结果很糟糕!

如果我只在释放鼠标时要求重新绘制,结果似乎保持正确。但是,如果我在设置时不“连续”重新绘制组件,那么设置旋钮值会更加复杂。

导致问题的“重绘”方法位于类末尾的 run 方法中。

有人已经遇到这个问题了吗?可以使用什么样的解决方案来解决它? 这是我正在使用的代码:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class Knob extends JPanel implements MouseListener 
int length, originX, originY, centerX, centerY, width, height, diameter, squareLength;
int minorTick, majorTick, xTick, yTick, xCursor, yCursor;
int xMouse, yMouse, xMouseOrigin, yMouseOrigin;
float yDeltaMouse;
double angleOrigin, angleRange, angle;
double angleCursorOrigin;
double angleCursorInitial;
Color backgroundColor, knobColor;
boolean mousePressed;
Thread t;

Knob () 
    System.out.println("Knob");
    angleCursorOrigin=0.5;
    angleCursorInitial=0.5;
    knobColor =new Color(0,255,0,255);      
    minorTick=9;
    majorTick=3;

    this.addMouseListener(this);


public void paintComponent (Graphics g) 
    width=this.getWidth()/10*10;
    height=this.getHeight()/10*10;

    Graphics2D g2D = (Graphics2D)g;
    System.setProperty("awt.useSystemAAFontSettings", "on");
    System.setProperty("swing.aatext", "true");
    g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    if (width>height) 
        length=height;
     else 
        length=width;
    
    centerX=width/2;
    centerY=height/2;

    g.setColor(Color.BLACK);
    squareLength = (int )(length*0.9);
    originX=(width-squareLength)/2;
    originY=(height-squareLength)/2;

    /*
    //-45 = (-(Math.PI)/4)
    angleOrigin= -45;
    // 270 = (3*(Math.PI)/2)
    angleRange=270;
    //g.drawRect(originX, originY, squareLength, squareLength);
    //g.fillArc(originX, originY, squareLength, squareLength, (int)angleOrigin, (int)angleRange);
    for (int i=0; i<minorTick ; i++) 
        angle=((i*angleRange/(minorTick-1))+angleOrigin)-7;
        //System.out.println(angle*360/(2*Math.PI));
        xTick= (int) (centerX+Math.cos(angle)*squareLength/2);
        yTick= (int) (centerY-Math.sin(angle)*squareLength/2);
        //g.drawLine(centerX, centerY, xTick, yTick);
        //g.fillArc(originX, originY, squareLength, squareLength, (int)angle, (int)14);
    
    */

    angleOrigin=-(Math.PI)/4;
    angleRange=3*(Math.PI)/2;       
    g2D.setStroke(new BasicStroke(length/50+1));        
    for (int i=0; i<minorTick ; i++) 
        angle=i*angleRange/(minorTick-1)+angleOrigin;
        //System.out.println(angle*360/(2*Math.PI));
        xTick= (int) (centerX+Math.cos(angle)*squareLength/2);
        yTick= (int) (centerY-Math.sin(angle)*squareLength/2);
        //g.drawLine(centerX, centerY, xTick, yTick);
        g2D.draw (new Line2D.Float(centerX, centerY, xTick, yTick));
    

    backgroundColor = this.getBackground();
    g.setColor(backgroundColor);
    diameter=(int)(length*0.8);
    originX=(width-diameter)/2;
    originY=(height-diameter)/2;
    g.fillOval(originX, originY, diameter, diameter);


    /*
    RadialGradientPaint gp;
    Point2D center= new Point2D.Float(width/2, height/2);
    diameter=(int)(length*0.75);
    originX=(width-diameter)/2;
    originY=(height-diameter)/2;
    float radius=diameter/2;        
    float[] dist = 0.7f, 1f;
    Color[] colors = Color.BLUE, Color.GRAY;
    gp=new RadialGradientPaint(center, radius, dist, colors);
    g2D.setPaint(gp);       
    g2D.fillOval(originX,originY,diameter,diameter);
    */
    diameter=(int)(length*0.75);
    originX=(width-diameter)/2;
    originY=(height-diameter)/2;
    g.setColor(Color.GRAY);
    g.fillOval(originX,originY,diameter,diameter);

    diameter=(int)(length*0.6);
    originX=(width-diameter)/2;
    originY=(height-diameter)/2;
    g.setColor(knobColor);
    g.fillOval(originX,originY,diameter,diameter);

    g2D.setStroke(new BasicStroke(length/50+3));
    angle=(2*Math.PI)*(0.75-angleCursorOrigin*0.75)+angleOrigin;
    xCursor= (int) (centerX+Math.cos(angle)*length*0.35);
    yCursor= (int) (centerY-Math.sin(angle)*length*0.35);
    g.setColor(Color.GRAY);
    g2D.draw (new Line2D.Float(centerX, centerY, xCursor, yCursor));



@Override
public void mouseClicked(MouseEvent arg0) 
    // TODO Auto-generated method stub
    //System.out.println("Bouton : "+arg0.getButton());



@Override
public void mouseEntered(MouseEvent arg0) 
    // TODO Auto-generated method stub



@Override
public void mouseExited(MouseEvent arg0) 
    // TODO Auto-generated method stub



@Override
public void mousePressed(MouseEvent arg0) 
    // TODO Auto-generated method stub
    //System.out.println("Bouton : "+arg0.getButton());
    PointerInfo pointer = MouseInfo.getPointerInfo();
    Point mouseLocation = pointer.getLocation();
    xMouseOrigin = (int) mouseLocation.getX();
    yMouseOrigin = (int) mouseLocation.getY();
    if (arg0.getButton()==MouseEvent.BUTTON1) 
        mousePressed=true;
        t= new Thread(new TrackPosition());
        t.start();
     else if (arg0.getButton()==MouseEvent.BUTTON3) 
        angleCursorOrigin=angleCursorInitial;
        repaint();
           


@Override
public void mouseReleased(MouseEvent arg0) 
    // TODO Auto-generated method stub
    mousePressed=false;
    //System.out.println("Mouse released");

    repaint();



class TrackPosition implements Runnable 

    @Override
    public void run() 
        // TODO Auto-generated method stub
        while (mousePressed==true) 
            PointerInfo pointer = MouseInfo.getPointerInfo();
            Point mouseLocation = pointer.getLocation();
            yMouse = (int) mouseLocation.getY();
            yDeltaMouse=(float)(yMouse-yMouseOrigin)/100;
            angleCursorOrigin=angleCursorOrigin+yDeltaMouse;
            yMouseOrigin=yMouse;
            if (angleCursorOrigin >=1) 
                angleCursorOrigin=1;
             else if (angleCursorOrigin <= 0) 
                angleCursorOrigin=0;
            
            //This repaint is a problem if I "uncomment" it
            //repaint();
        

    



/**
 * @param args
 */
public static void main(String[] args) 
    // TODO Auto-generated method stub
    JFrame frame = new JFrame();
    frame.setSize(300,90);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(new Knob());
    frame.setVisible(true);




【问题讨论】:

让我们从您没有调用 super.paintComponent 的事实开始,然后再谈一下您的“跟踪线程”已经过时且不需要的事实。您可以改用 MouseMotionListener 【参考方案1】:

让我们从在您的paintComponent 方法中执行自定义绘画之前不调用super.paintComponent 的事实开始。

见Painting in AWT and Swing 和 Performing Custom Painting了解更多详情

然后转移到这个事实,即您的“鼠标跟踪线程”是多余的,实际上并不是必需的。相反,您应该使用MouseMotionListener,有关详细信息,请参阅How to Write a Mouse Listener。

这将使您无需在本地和屏幕上下文之间进行转换。

【讨论】:

以上是关于重绘 Swing 组件会产生糟糕的结果的主要内容,如果未能解决你的问题,请参考以下文章

swing更改组件(删除后添加)得到心得:起码得刷新一下啊,可能还得再考虑重绘

Java Swing:重绘()与无效[重复]

Swing JLabel:强制重绘()

WM_PAINT消息在窗口重绘的时候产生,那什么时候窗口会重绘(异步工作方式,效率更高,灵活性更强)

visibillity和display:none产生的回流和重绘

TabView 在重绘时是不是会错过导航点击?