【问题标题】:How to Change the Clockface of an Analog Clock Using BufferedImage in Java如何在 Java 中使用 BufferedImage 更改模拟时钟的钟面
【发布时间】:2014-11-03 20:09:24
【问题描述】:

我正在尝试修改使用 BufferedImage 实现模拟时钟的 java 代码。我希望当用户单击一个滚动浏览预加载图像位置的数组的按钮时动态更改时钟的表面。到目前为止,我的尝试都失败了。我有两节课;第一个类创建并显示模拟,第二个类尝试使用一个修改器 (setAnalogClockFace()) 和一个访问器 (getAnalogClockFace()) 更改模拟时钟的面。以下是here 上的第一个类的代码,稍作修改:

package panels;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.util.*;

public class Clock extends JPanel{
/**
 * 
 */
private static final long serialVersionUID = 1L;
private int hours = 0;
private int minutes = 0;
private int seconds = 0;
private int millis = 0;
private static final int spacing = 10;
private static final float threePi = (float)(3.0 * Math.PI);
private static final float radPerSecMin = (float)(Math.PI/30.0);
private int size; //height and width of clockface
private int centerX;//X coord of middle of clock
private int centerY;//Y coord of middle of clock
private BufferedImage clockImage;
private BufferedImage imageToUse;
private javax.swing.Timer t;

public Clock(){
    this.setPreferredSize(new Dimension(203,203));
    this.setBackground(getBackground());
    this.setForeground(Color.darkGray);

    t = new javax.swing.Timer(1000, new ActionListener(){
        public void actionPerformed(ActionEvent ae){
            update();
        }
    });
}

public void update(){
    this.repaint();
}

public void start(){
    t.start();
}

public void stop(){
    t.stop();
}

public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D)g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                                RenderingHints.VALUE_ANTIALIAS_ON);

    int w = getWidth();
    int h = getHeight();
    size = ((w < h) ? w : h) - 2 * spacing;

    centerX = size/2 + spacing;
    centerY = size/2 + spacing;

    setClockFace(new AnalogClockTimer().getAnalogClockFace());
    clockImage = getClockFace();

    if (clockImage == null | clockImage.getWidth() != w | clockImage.getHeight() != h){
        clockImage = (BufferedImage)(this.createImage(w,h));
        Graphics2D gc = clockImage.createGraphics();
        gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                                RenderingHints.VALUE_ANTIALIAS_ON);

        drawClockFace(gc);
    }


    Calendar now = Calendar.getInstance();
    hours = now.get(Calendar.HOUR);
    minutes = now.get(Calendar.MINUTE);
    seconds = now.get(Calendar.SECOND);
    millis = now.get(Calendar.MILLISECOND);

    g2d.drawImage(clockImage, null, 0, 0);
    drawClockHands(g);
}

private void drawClockHands(Graphics g){
    int secondRadius = size/2;
    int minuteRadius = secondRadius * 3/4;
    int hourRadius = secondRadius/2;

    float fseconds = seconds + (float)millis/1000;
    float secondAngle = threePi - (radPerSecMin * fseconds);

    drawRadius(g, centerX, centerY, secondAngle, 0, secondRadius);

    float fminutes = (float)(minutes + fseconds/60.0);
    float minuteAngle = threePi - (radPerSecMin * fminutes);

    drawRadius(g, centerX, centerY, minuteAngle, 0 , minuteRadius);

    float fhours = (float)(hours + fminutes/60.0);
    float hourAngle = threePi - (5 * radPerSecMin * fhours);

    drawRadius(g, centerX, centerY, hourAngle, 0 ,hourRadius);
}

private void drawClockFace(Graphics g){
    //g.setColor(Color.lightGray);
    g.fillOval(spacing, spacing, size, size);
    //g.setColor(new Color(168,168,168));
    g.drawOval(spacing, spacing, size, size);

    for(int sec = 0; sec < 60; sec++){
        int tickStart;

        if(sec%5 == 0){
            tickStart = size/2-10;
        }else{
            tickStart = size/2-5;
        }

        drawRadius(g, centerX, centerY, radPerSecMin * sec, tickStart, size/2);
    }
}

private void drawRadius(Graphics g, int x, int y, double angle,
        int minRadius, int maxRadius){
    float sine = (float)Math.sin(angle);
    float cosine = (float)Math.cos(angle);

    int dxmin = (int)(minRadius * sine);
    int dymin = (int)(minRadius * cosine);

    int dxmax = (int)(maxRadius * sine);
    int dymax = (int)(maxRadius * cosine);

    g.drawLine(x+dxmin, y+dymin, x+dxmax,y+dymax);
}

public void setClockFace(BufferedImage img){
    if(img != null){
        imageToUse = img;
    }else{
        try{
            imageToUse =  
ImageIO.read(getClass().getClassLoader().getResource("http://imgur.com/T8x0I29"));
        }catch(IOException io){
            io.printStackTrace();
        }
    }

}

public BufferedImage getClockFace(){
    return imageToUse;
}

}

尝试设置钟面的第二个类是:

package panels;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import panels.Clock;


public class AnalogClockTimer extends javax.swing.JPanel implements ActionListener,  
MouseListener {
private Clock clock;
/**
 * 
 */
private static final long serialVersionUID = 1L;
private JButton prevBtn;
private JTextField clockFaceCount;
private JButton nextBtn;
private JPanel buttonedPanel,leftPanel,rightPanel;
private BufferedImage image;

String[] clockFace;
int arrayPointer = 1;


/**
* Auto-generated main method to display this 
* JPanel inside a new JFrame.
*/
public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.getContentPane().add(new AnalogClockTimer());
    frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
}

public AnalogClockTimer() {
    super();
    initGUI();
}

private void initGUI() {
    try {
        BorderLayout thisLayout = new BorderLayout();
        this.setLayout(thisLayout);
        this.setPreferredSize(new java.awt.Dimension(283, 233));
        this.setBackground(getBackground());

        {
            clock = new Clock();
            add(clock,BorderLayout.CENTER);
            clock.start();
            clock.setSize(203, 203);
        }
        {
            buttonedPanel = new JPanel();
            FlowLayout buttonedPanelLayout = new FlowLayout();
            buttonedPanel.setLayout(buttonedPanelLayout);
            this.add(buttonedPanel, BorderLayout.SOUTH);
            buttonedPanel.setPreferredSize(new java.awt.Dimension(283, 30));
            {
                prevBtn = new JButton(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_rest.png")));
                prevBtn.setPreferredSize(new java.awt.Dimension(20, 19));
                prevBtn.setBackground(getBackground());
                prevBtn.setBorderPainted(false);
                prevBtn.setEnabled(false);
                prevBtn.addMouseListener(this);
                prevBtn.addActionListener(this);
                buttonedPanel.add(prevBtn);
            }
            {
                nextBtn = new JButton(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_rest.png")));
                nextBtn.setPreferredSize(new java.awt.Dimension(20, 19));
                nextBtn.setBackground(getBackground());
                nextBtn.setBorderPainted(false);
                nextBtn.addMouseListener(this);
                nextBtn.addActionListener(this);
            }
            {
                clockFaceCount = new JTextField();
                clockFaceCount.setPreferredSize(new java.awt.Dimension(50, 19));
                clockFaceCount.setBackground(getBackground());
                clockFaceCount.setBorder(null);
                clockFaceCount.setHorizontalAlignment(SwingConstants.CENTER);
                clockFaceCount.setEditable(false);
                buttonedPanel.add(clockFaceCount);

                buttonedPanel.add(nextBtn);
            }
            {
                clockFace = new String[]{"http://imgur.com/079QSdJ","http://imgur.com/vQ7tZjI",
                        "http://imgur.com/T8x0I29"};

                for(int i = 1; i <= clockFace.length; i++){

                    if(i == 1){
                        nextBtn.setEnabled(true);
                        prevBtn.setEnabled(false);
                    }
                    clockFaceCount.setText(arrayPointer+" of "+clockFace.length);
                }
            }
            {
                leftPanel = new JPanel();
                leftPanel.setPreferredSize(new Dimension(40, getHeight()));
                add(leftPanel,BorderLayout.WEST);
            }
            {
                rightPanel = new JPanel();
                rightPanel.setPreferredSize(new Dimension(40,getHeight()));
                add(rightPanel,BorderLayout.EAST);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public void mouseClicked(MouseEvent me) {
    if(me.getSource() == prevBtn)
        prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_pressed.png")));
    else
        nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_pressed.png")));
}

@Override
public void mouseEntered(MouseEvent me) {
    if(me.getSource()==prevBtn)
        prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_hover.png")));
    else
        nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_hover.png")));
}

@Override
public void mouseExited(MouseEvent me) {
    if(me.getSource() == prevBtn)
        prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_rest.png")));
    else
        nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_rest.png")));

}

@Override
public void mousePressed(MouseEvent me) {
    if(me.getSource() == prevBtn){
        prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_pressed.png")));
    }else{
        nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_pressed.png")));
    }
}

@Override
public void mouseReleased(MouseEvent me) {
    if(me.getSource() == prevBtn)
        prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_hover.png")));
    else
        nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_hover.png")));

}

@Override
public void actionPerformed(ActionEvent ae) {
    if(ae.getSource() == nextBtn){
        {
            if(arrayPointer < clockFace.length){
                ++arrayPointer;
                prevBtn.setEnabled(true);

                if(arrayPointer == clockFace.length)
                    nextBtn.setEnabled(false);
            }

            clockFaceCount.setText(arrayPointer + " of " + clockFace.length);
            setAnalogClockFace(arrayPointer);
        }
    }else if(ae.getSource() == prevBtn){
        {
            if(arrayPointer > 1){
                --arrayPointer;
                nextBtn.setEnabled(true);

                if(arrayPointer == 1){
                    clockFaceCount.setText(arrayPointer+" of "+clockFace.length);
                    prevBtn.setEnabled(false);
                }
            }
            clockFaceCount.setText(arrayPointer + " of "+clockFace.length);
            setAnalogClockFace(arrayPointer);
        }
    }

}

private void setAnalogClockFace(int pointerPosition) {
    try{
        image = ImageIO.read(getClass().getClassLoader().getResource(clockFace[pointerPosition]));
    }catch(IOException io){
        io.printStackTrace();
    }
}

public BufferedImage getAnalogClockFace(){
    return image;
}

}

我已经为此工作了好几天,有人可以帮助我了解哪里做错了吗?任何帮助将不胜感激。

这是一个使用 JLabel 实现相同结果的 MCVE:

package panels;

import java.awt.Dimension;

import javax.swing.ImageIcon;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JTextField;

import java.awt.event.*;
import java.awt.BorderLayout;

@SuppressWarnings({"serial"})
public class ClockPanel extends javax.swing.JPanel implements ActionListener {
private JLabel clockImageLabel;
private JPanel buttonsPanel;
private JButton next,prev;
private JTextField displayCount;

String[] imageFaces;
int pointer = 1;
/**
* Auto-generated main method to display this 
* JPanel inside a new JFrame.
*/
public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.getContentPane().add(new ClockPanel());
    frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
}

public ClockPanel() {
    super();
    initGUI();
}

private void initGUI() {
    try {
        setPreferredSize(new Dimension(203, 237));
        BorderLayout panelLayout = new BorderLayout();
        setLayout(panelLayout);

        {
            clockImageLabel = new JLabel();
            clockImageLabel.setPreferredSize(new Dimension(203,203));
            clockImageLabel.setHorizontalAlignment(SwingConstants.CENTER);
            clockImageLabel.setVerticalAlignment(SwingConstants.CENTER);
            add(clockImageLabel,BorderLayout.NORTH);
        }
        {
            buttonsPanel = new JPanel();

            {
                next = new JButton(">");
                next.addActionListener(this);
            }
            {
                prev = new JButton("<");
                prev.addActionListener(this);

                displayCount = new JTextField();
                displayCount.setBorder(null);
                displayCount.setEditable(false);
                displayCount.setBackground(getBackground());
                displayCount.setHorizontalAlignment(SwingConstants.CENTER);
                buttonsPanel.add(prev);
                buttonsPanel.add(displayCount);
                displayCount.setPreferredSize(new java.awt.Dimension(45, 23));
                buttonsPanel.add(next);

                add(buttonsPanel,BorderLayout.SOUTH);
            }
            {
                imageFaces = new String[]{"http://imgur.com/T8x0I29",
                        "http://imgur.com/079QSdJ","http://imgur.com/vQ7tZjI"
                };

                for(int i = 1; i < imageFaces.length; i++){
                    if(i == 1){
                        next.setEnabled(true);
                        prev.setEnabled(false);
                    }
                    displayCount.setText(pointer+" of "+imageFaces.length);
                    setLabelImage(pointer);
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

private void setLabelImage(int pointerPosition){
    clockImageLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource(imageFaces[pointerPosition])));
    this.repaint();
}

@Override
public void actionPerformed(ActionEvent ae) {
    if(ae.getSource() == next){
            if(pointer < imageFaces.length){
                ++pointer;
                prev.setEnabled(true);

                if(pointer == imageFaces.length)next.setEnabled(false);

                displayCount.setText(pointer+" of "+imageFaces.length);
                setLabelImage(pointer);
            }
    }else if(ae.getSource() == prev){
        if(pointer > 1){
            --pointer;
            next.setEnabled(true);

            if(pointer == 1)prev.setEnabled(false);

            displayCount.setText(pointer+" of "+imageFaces.length);
            setLabelImage(pointer);
        }
    }
}

}

这里是代码的 1/2 图片: HandlessAnalogClock 1 HandlessAnalogClock 2 HandlessAnalogClock 3

【问题讨论】:

  • 1) 为了尽快获得更好的帮助,请发布MCVE(最小完整可验证示例)。 2) 例如,获取图像的一种方法是热链接到在this Q&A 中看到的图像。
  • 请注意,您的 MCVE 应该明显少于这 2 个类的 380 多行代码...
  • duplicate 中可以看到一个完整的工作示例。
  • @AndrewThompson 对我迟到评论你之前的帖子表示歉意;我以 JLabel 为例复制了上述问题,我发现如前所述更改模拟时钟的外观要容易得多。我不明白为什么我似乎无法在 JPanel 上做同样的事情
  • @AndrewThompson 我已经编辑了我的帖子,以在 JLabel 上使用相同的图像包含一个 MCVE。

标签: java arrays swing bufferedimage clock


【解决方案1】:

此 MCVE 将在三个图像中的两个之间成功翻转,但 ArrayIndexOutOfBoundsException 仍然存在问题。

最后一部分是“不包括电池”(留作练习供用户纠正)。

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.net.URL;

@SuppressWarnings({"serial"})
public class ClockPanel extends JPanel implements ActionListener {

    private JLabel clockImageLabel;
    private JPanel buttonsPanel;
    private JButton next, prev;
    private JTextField displayCount;

    BufferedImage[] images;
    int pointer = 1;

    /**
     * Auto-generated main method to display this JPanel inside a new JFrame.
     */
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.getContentPane().add(new ClockPanel());
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.pack();
        frame.setVisible(true);
    }

    public ClockPanel() {
        super();
        initGUI();
    }

    private void initGUI() {
        try {
            setPreferredSize(new Dimension(203, 237));
            BorderLayout panelLayout = new BorderLayout();
            setLayout(panelLayout);

            clockImageLabel = new JLabel();
            clockImageLabel.setPreferredSize(new Dimension(203, 203));
            clockImageLabel.setHorizontalAlignment(SwingConstants.CENTER);
            clockImageLabel.setVerticalAlignment(SwingConstants.CENTER);
            add(clockImageLabel, BorderLayout.NORTH);

            buttonsPanel = new JPanel();

            next = new JButton(">");
            next.addActionListener(this);

            prev = new JButton("<");
            prev.addActionListener(this);

            displayCount = new JTextField();
            displayCount.setBorder(null);
            displayCount.setEditable(false);
            displayCount.setBackground(getBackground());
            displayCount.setHorizontalAlignment(SwingConstants.CENTER);
            buttonsPanel.add(prev);
            buttonsPanel.add(displayCount);
            displayCount.setPreferredSize(new java.awt.Dimension(45, 23));
            buttonsPanel.add(next);

            add(buttonsPanel, BorderLayout.SOUTH);

            String[] imageFaces = new String[]{
                "http://i.imgur.com/T8x0I29.png",
                "http://i.imgur.com/079QSdJ.png",
                "http://i.imgur.com/vQ7tZjI.png"
            };
            images = new BufferedImage[imageFaces.length];
            for (int ii=0; ii<imageFaces.length; ii++) {
                System.out.println("loading image: " + ii);
                images[ii] = ImageIO.read(new URL(imageFaces[ii]));
                System.out.println("loaded image: " + images[ii]);
            }
            setLabelImage(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setLabelImage(int pointerPosition) {
        System.out.println("setLabelImage: " + pointerPosition);
        clockImageLabel.setIcon(new ImageIcon(images[pointerPosition]));
        this.repaint();
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        if (ae.getSource() == next) {
            if (pointer < images.length) {
                ++pointer;
                prev.setEnabled(true);

                if (pointer == images.length) {
                    next.setEnabled(false);
                }

                displayCount.setText(pointer + " of " + images.length);
                setLabelImage(pointer);
            }
        } else if (ae.getSource() == prev) {
            if (pointer > 1) {
                --pointer;
                next.setEnabled(true);

                if (pointer == 1) {
                    prev.setEnabled(false);
                }

                displayCount.setText(pointer + " of " + images.length);
                setLabelImage(pointer);
            }
        }
    }
}

【讨论】:

  • 感谢您的帖子,但我认为在 JLabel 上显示图像时翻阅图像比在 JPanel 上绘制图像时容易得多。我想我在之前的帖子中发布了一个 MCVE!
  • “我想我在之前的帖子中发布了一个 MCVE!” 这很奇怪。你写的好像你希望我在试图提供帮助之前先浏览你过去的帖子。每个问答都应该是完全独立的。
  • +1 用于 MCVE,但需要更少的 setPreferredSize()。 :-) @aknessy:还要考虑环绕索引,在引用的示例中可以看到 here
  • @trashgod Aw.. 只有 ..6 个调用 来设置首选尺寸?!?我完全错过了那些。 OP 请参阅Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?(是的。)
  • @AndrewThompson:我也这样做。 :-) 更多 here.
【解决方案2】:

扩展@Andrewexample,考虑将当前图像的索引安排为wrap-around,如引用的示例here所示。

private int pointer = 0;
...
private void initGUI() {
    ...
    setLabelImage(pointer);
    ...
}

private void setLabelImage(int pointerPosition) {
    System.out.println("setLabelImage: " + pointerPosition);
    clockImageLabel.setIcon(new ImageIcon(images[pointerPosition]));
    displayCount.setText((pointerPosition + 1) + " of " + images.length);
    this.repaint();
}

@Override
public void actionPerformed(ActionEvent ae) {
    if (ae.getSource() == next) {
        pointer++;
        if (pointer >= images.length) {
            pointer = 0;
        }
    } else if (ae.getSource() == prev) {
        pointer--;
        if (pointer < 0) {
            pointer = images.length - 1;
        }
    }
    setLabelImage(pointer);
}

作为练习,尝试通过在initGUI() 中创建一个List&lt;ImageIcon&gt; 来排除ImageIconsetLabelImage() 中的重复实例化。

【讨论】:

    猜你喜欢
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    • 2015-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多