Java 摆动不确定的 JProgressBar 在到达终点时从左侧开始而不是弹跳
Java swing indeterminate JProgressBar starting from the left when reaching end instead of bouncing
我在 GUI 应用程序中创建了一个 JProgressBar,并将其设置为 "indeterminate",但我不喜欢它 反弹 而不是 每次到达 都重新启动结束。 如何修复此图形设置?
更改 JProgressBar 的 UI。
UI就是画进度条的class。 BasicProgressBarUI,默认情况下使框反弹。您只需要编写自己的 class 扩展 MetalProgressBarUI(如果您使用的是 Metal)并覆盖 getBox(Rectangle),这是在给定矩形中存储框位置的方法.
使用 JProgressBar.setUI 将 UI 应用于您的进度条。您还可以使用 UIManager.put("ProgressBarUI", "fullyQualifiedClassName") 更改默认值,以更改进度条的默认值 UI。
正如 Snowy_1803 已经说过的,您需要重写 BasicProgressBarUI#getBox(...)
:
import java.awt.*;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.swing.*;
import javax.swing.plaf.basic.BasicProgressBarUI;
public final class MainPanel extends JPanel implements HierarchyListener {
private transient SwingWorker<String, Void> worker;
private MainPanel() {
super(new BorderLayout());
BoundedRangeModel model = new DefaultBoundedRangeModel();
JProgressBar progressBar = new JProgressBar(model) {
@Override public void updateUI() {
super.updateUI();
setUI(new OneDirectionProgressBarUI());
}
};
List<JProgressBar> list = Arrays.asList(new JProgressBar(model), progressBar);
JPanel p = new JPanel(new GridLayout(5, 1));
list.forEach(bar -> p.add(makePanel(bar)));
JButton button = new JButton("Test start");
button.addActionListener(e -> {
if (Objects.nonNull(worker) && !worker.isDone()) {
worker.cancel(true);
}
worker = new BackgroundTask();
list.forEach(bar -> {
bar.setIndeterminate(true);
worker.addPropertyChangeListener(new ProgressListener(bar));
});
worker.execute();
});
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
box.add(button);
box.add(Box.createHorizontalStrut(5));
addHierarchyListener(this);
add(p);
add(box, BorderLayout.SOUTH);
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
}
@Override public void hierarchyChanged(HierarchyEvent e) {
boolean isDisplayableChanged = (e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0;
if (isDisplayableChanged && !e.getComponent().isDisplayable() && Objects.nonNull(worker)) {
worker.cancel(true);
worker = null;
}
}
private static Component makePanel(Component cmp) {
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(5, 5, 5, 5);
c.weightx = 1d;
JPanel p = new JPanel(new GridBagLayout());
p.add(cmp, c);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new MainPanel());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class OneDirectionProgressBarUI extends BasicProgressBarUI {
@Override
protected Rectangle getBox(Rectangle r) {
Rectangle rect = super.getBox(r);
boolean vertical = progressBar.getOrientation() == JProgressBar.VERTICAL;
Insets ins = new Insets(0, 0, 0, 0); // progressBar.getInsets();
int currentFrame = getAnimationIndex();
int framecount = getFrameCount() / 2;
currentFrame = currentFrame % framecount;
// @see com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java
// this code adjusts the chunk size to properly account for the
// size and gap specified in the XP style. It also does it's own
// box placement for the chunk animation. This is required because
// the inherited algorithm from BasicProgressBarUI goes back and
// forth whereas XP only goes in one direction. XP also has ghosted
// trailing chunks to create the illusion of speed. This code
// adjusts the pixel length of the animation to account for the
// trails.
if (!vertical) {
rect.y = rect.y + ins.top;
rect.height = progressBar.getHeight() - ins.top - ins.bottom;
int len = progressBar.getWidth() - ins.left - ins.right;
len += rect.width * 2; // add 2x for the trails
double delta = (double) (len) / (double) framecount;
rect.x = (int) (delta * currentFrame) + ins.left;
} else {
rect.x = rect.x + ins.left;
rect.width = progressBar.getWidth() - ins.left - ins.right;
int len = progressBar.getHeight() - ins.top - ins.bottom;
len += rect.height * 2; // add 2x for the trails
double delta = (double) (len) / (double) framecount;
rect.y = (int) (delta * currentFrame) + ins.top;
}
return rect;
}
}
class BackgroundTask extends SwingWorker<String, Void> {
@Override public String doInBackground() {
try { // dummy task
Thread.sleep(5000);
} catch (InterruptedException ex) {
return "Interrupted";
}
int current = 0;
int lengthOfTask = 100;
while (current <= lengthOfTask && !isCancelled()) {
try { // dummy task
Thread.sleep(50);
} catch (InterruptedException ex) {
return "Interrupted";
}
setProgress(100 * current / lengthOfTask);
current++;
}
return "Done";
}
}
class ProgressListener implements PropertyChangeListener {
private final JProgressBar progressBar;
protected ProgressListener(JProgressBar progressBar) {
this.progressBar = progressBar;
this.progressBar.setValue(0);
}
@Override public void propertyChange(PropertyChangeEvent e) {
String strPropertyName = e.getPropertyName();
if ("progress".equals(strPropertyName)) {
progressBar.setIndeterminate(false);
int progress = (Integer) e.getNewValue();
progressBar.setValue(progress);
}
}
}
我在 GUI 应用程序中创建了一个 JProgressBar,并将其设置为 "indeterminate",但我不喜欢它 反弹 而不是 每次到达 都重新启动结束。 如何修复此图形设置?
更改 JProgressBar 的 UI。
UI就是画进度条的class。 BasicProgressBarUI,默认情况下使框反弹。您只需要编写自己的 class 扩展 MetalProgressBarUI(如果您使用的是 Metal)并覆盖 getBox(Rectangle),这是在给定矩形中存储框位置的方法.
使用 JProgressBar.setUI 将 UI 应用于您的进度条。您还可以使用 UIManager.put("ProgressBarUI", "fullyQualifiedClassName") 更改默认值,以更改进度条的默认值 UI。
正如 Snowy_1803 已经说过的,您需要重写 BasicProgressBarUI#getBox(...)
:
import java.awt.*;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.swing.*;
import javax.swing.plaf.basic.BasicProgressBarUI;
public final class MainPanel extends JPanel implements HierarchyListener {
private transient SwingWorker<String, Void> worker;
private MainPanel() {
super(new BorderLayout());
BoundedRangeModel model = new DefaultBoundedRangeModel();
JProgressBar progressBar = new JProgressBar(model) {
@Override public void updateUI() {
super.updateUI();
setUI(new OneDirectionProgressBarUI());
}
};
List<JProgressBar> list = Arrays.asList(new JProgressBar(model), progressBar);
JPanel p = new JPanel(new GridLayout(5, 1));
list.forEach(bar -> p.add(makePanel(bar)));
JButton button = new JButton("Test start");
button.addActionListener(e -> {
if (Objects.nonNull(worker) && !worker.isDone()) {
worker.cancel(true);
}
worker = new BackgroundTask();
list.forEach(bar -> {
bar.setIndeterminate(true);
worker.addPropertyChangeListener(new ProgressListener(bar));
});
worker.execute();
});
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
box.add(button);
box.add(Box.createHorizontalStrut(5));
addHierarchyListener(this);
add(p);
add(box, BorderLayout.SOUTH);
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
}
@Override public void hierarchyChanged(HierarchyEvent e) {
boolean isDisplayableChanged = (e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0;
if (isDisplayableChanged && !e.getComponent().isDisplayable() && Objects.nonNull(worker)) {
worker.cancel(true);
worker = null;
}
}
private static Component makePanel(Component cmp) {
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(5, 5, 5, 5);
c.weightx = 1d;
JPanel p = new JPanel(new GridBagLayout());
p.add(cmp, c);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new MainPanel());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class OneDirectionProgressBarUI extends BasicProgressBarUI {
@Override
protected Rectangle getBox(Rectangle r) {
Rectangle rect = super.getBox(r);
boolean vertical = progressBar.getOrientation() == JProgressBar.VERTICAL;
Insets ins = new Insets(0, 0, 0, 0); // progressBar.getInsets();
int currentFrame = getAnimationIndex();
int framecount = getFrameCount() / 2;
currentFrame = currentFrame % framecount;
// @see com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java
// this code adjusts the chunk size to properly account for the
// size and gap specified in the XP style. It also does it's own
// box placement for the chunk animation. This is required because
// the inherited algorithm from BasicProgressBarUI goes back and
// forth whereas XP only goes in one direction. XP also has ghosted
// trailing chunks to create the illusion of speed. This code
// adjusts the pixel length of the animation to account for the
// trails.
if (!vertical) {
rect.y = rect.y + ins.top;
rect.height = progressBar.getHeight() - ins.top - ins.bottom;
int len = progressBar.getWidth() - ins.left - ins.right;
len += rect.width * 2; // add 2x for the trails
double delta = (double) (len) / (double) framecount;
rect.x = (int) (delta * currentFrame) + ins.left;
} else {
rect.x = rect.x + ins.left;
rect.width = progressBar.getWidth() - ins.left - ins.right;
int len = progressBar.getHeight() - ins.top - ins.bottom;
len += rect.height * 2; // add 2x for the trails
double delta = (double) (len) / (double) framecount;
rect.y = (int) (delta * currentFrame) + ins.top;
}
return rect;
}
}
class BackgroundTask extends SwingWorker<String, Void> {
@Override public String doInBackground() {
try { // dummy task
Thread.sleep(5000);
} catch (InterruptedException ex) {
return "Interrupted";
}
int current = 0;
int lengthOfTask = 100;
while (current <= lengthOfTask && !isCancelled()) {
try { // dummy task
Thread.sleep(50);
} catch (InterruptedException ex) {
return "Interrupted";
}
setProgress(100 * current / lengthOfTask);
current++;
}
return "Done";
}
}
class ProgressListener implements PropertyChangeListener {
private final JProgressBar progressBar;
protected ProgressListener(JProgressBar progressBar) {
this.progressBar = progressBar;
this.progressBar.setValue(0);
}
@Override public void propertyChange(PropertyChangeEvent e) {
String strPropertyName = e.getPropertyName();
if ("progress".equals(strPropertyName)) {
progressBar.setIndeterminate(false);
int progress = (Integer) e.getNewValue();
progressBar.setValue(progress);
}
}
}