我正在使用JSlider来显示我的应用程序的进度,以便它在某个进程计算时更新。
我希望用户能够将拇指向后拖动到过去的刻度线,但是当发生这种情况时,我希望进度指示器保持在当前位置。
// A dismal attempt at drawing the situation
Progress: ---------
Thumb: |
// User drags backwards. What I have:
Progress: ---
Thumb: |
// What I want instead:
Progress: ---------
Thumb: |
任何人都可以提供有关如何最好地完成此任务的任何指导吗? 提前感谢您的帮助!
答案 0 :(得分:1)
“蓝色条形”并不是JSlider的一部分,它是Metal LAF的一部分。这就是Metal LAF选择绘制组件的方式。如果你和另一个LAF一起尝试,你就不会得到它所有
你需要结合一个JProgressBar和一个JSlider来获得你想要的东西。
这是一个起点。您需要调整它以使其看起来正确
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.event.ChangeListener;
public class ProgressSlider extends javax.swing.JPanel
{
private JProgressBar progressBar;
private JSlider slider;
public ProgressSlider() {
this(0, 100);
}
public ProgressSlider(int min, int max) {
setLayout(new GridBagLayout());
GridBagConstraints gbc;
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
slider = new JSlider(min, max, min);
progressBar = new JProgressBar(min, max);
add(slider, gbc);
add(progressBar, gbc);
}
public void setValue(int n) {
boolean adjSlider = slider.getValue() == progressBar.getValue();
progressBar.setValue(n);
if (adjSlider)
slider.setValue(n);
}
public int getValue() {
return progressBar.getValue();
}
public int getSliderValue() {
return slider.getValue();
}
public void syncSlider() {
slider.setValue(progressBar.getValue());
}
public void addChangeListener(ChangeListener l) {
slider.addChangeListener(l);
}
public void removeChangeListener(ChangeListener l) {
slider.removeChangeListener(l);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final ProgressSlider ps = new ProgressSlider();
final JTextField tf = new JTextField(4);
final JButton jb = new JButton(new AbstractAction("Set") {
public void actionPerformed(ActionEvent e) {
ps.setValue(Integer.parseInt(tf.getText()));
}});
JPanel jp = new JPanel();
jp.add(tf);
jp.add(jb);
jf.getContentPane().add(ps, BorderLayout.CENTER);
jf.getContentPane().add(jp, BorderLayout.SOUTH);
jf.pack();
jf.setVisible(true);
}
});
}
}
答案 1 :(得分:1)
我明白了。我扩展了JSlider以跟踪当前拇指位置的进度,然后覆盖MetalSliderUI的paintTrack方法以将轨道填充到我想要的位置。
这是解决方案,剥离了重要部分。
新的进度条:
public class ProgressSlider extends JSlider {
protected int progress;
private static final String uiClassID = "ProgressSliderUI";
public ProgressSlider() {
progress = 0;
putClientProperty("JSlider.isFilled", Boolean.TRUE);
updateUI();
}
public void updateUI() {
setUI(new ProgressSliderUI(this));
}
public String getUIClassID() {
return uiClassID;
}
public int getProgress() {
return this.progress;
}
public void setProgress(int value) {
if (value < this.getMinimum()) {
this.progress = this.getMinimum();
}
else if (value > this.getMaximum()) {
this.progress = this.getMaximum();
}
else {
this.progress = value;
}
}
}
新用户界面: 注意,UI类中的paintTrack()方法只添加了2行,紧跟注释后这样说。
public class ProgressSliderUI extends MetalSliderUI {
public ProgressSliderUI() {
super();
}
public ProgressSliderUI(JSlider b) {
}
@Override
public void paintTrack(Graphics g) {
Color trackColor = !slider.isEnabled() ? MetalLookAndFeel
.getControlShadow() : Color.blue;
boolean leftToRight = true;
g.translate(trackRect.x, trackRect.y);
int trackLeft = 0;
int trackTop = 0;
int trackRight = 0;
int trackBottom = 0;
// Draw the track
if (slider.getOrientation() == JSlider.HORIZONTAL) {
trackBottom = (trackRect.height - 1) - getThumbOverhang();
trackTop = trackBottom - (getTrackWidth() - 1);
trackRight = trackRect.width - 1;
}
else {
if (leftToRight) {
trackLeft = (trackRect.width - getThumbOverhang())
- getTrackWidth();
trackRight = (trackRect.width - getThumbOverhang()) - 1;
}
else {
trackLeft = getThumbOverhang();
trackRight = getThumbOverhang() + getTrackWidth() - 1;
}
trackBottom = trackRect.height - 1;
}
if (slider.isEnabled()) {
g.setColor(MetalLookAndFeel.getControlDarkShadow());
g.drawRect(trackLeft, trackTop, (trackRight - trackLeft) - 1,
(trackBottom - trackTop) - 1);
g.setColor(MetalLookAndFeel.getControlHighlight());
g.drawLine(trackLeft + 1, trackBottom, trackRight, trackBottom);
g.drawLine(trackRight, trackTop + 1, trackRight, trackBottom);
g.setColor(MetalLookAndFeel.getControlShadow());
g.drawLine(trackLeft + 1, trackTop + 1,
trackRight - 2, trackTop + 1);
g.drawLine(trackLeft + 1, trackTop + 1, trackLeft + 1,
trackBottom - 2);
}
else {
g.setColor(MetalLookAndFeel.getControlShadow());
g.drawRect(trackLeft, trackTop, (trackRight - trackLeft) - 1,
(trackBottom - trackTop) - 1);
}
// Draw the fill
if (filledSlider) {
int middleOfThumb = 0;
int fillTop = 0;
int fillLeft = 0;
int fillBottom = 0;
int fillRight = 0;
if (slider.getOrientation() == JSlider.HORIZONTAL) {
middleOfThumb = thumbRect.x + (thumbRect.width / 2);
middleOfThumb -= trackRect.x; // To compensate for the
// g.translate()
fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
fillBottom = !slider.isEnabled() ? trackBottom - 1
: trackBottom - 2;
if (!drawInverted()) {
fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
// THIS IS THE CHANGE OF NOTE:
// Fills the progress with the value from the custom slider
fillRight = xPositionForValue(((ProgressSlider) slider)
.getProgress());
fillRight -= trackRect.x;
}
else {
fillLeft = middleOfThumb;
fillRight = !slider.isEnabled() ? trackRight - 1
: trackRight - 2;
}
}
else {
middleOfThumb = thumbRect.y + (thumbRect.height / 2);
middleOfThumb -= trackRect.y; // To compensate for the
// g.translate()
fillLeft = !slider.isEnabled() ? trackLeft : trackLeft + 1;
fillRight = !slider.isEnabled() ? trackRight - 1
: trackRight - 2;
if (!drawInverted()) {
fillTop = middleOfThumb;
fillBottom = !slider.isEnabled() ? trackBottom - 1
: trackBottom - 2;
}
else {
fillTop = !slider.isEnabled() ? trackTop : trackTop + 1;
fillBottom = middleOfThumb;
}
}
if (slider.isEnabled()) {
g.setColor(slider.getBackground());
g.drawLine(fillLeft, fillTop, fillRight, fillTop);
g.drawLine(fillLeft, fillTop, fillLeft, fillBottom);
g.setColor(trackColor);
g.fillRect(fillLeft + 1, fillTop + 1, fillRight
- fillLeft, fillBottom - fillTop);
}
else {
g.setColor(MetalLookAndFeel.getControlShadow());
g.fillRect(fillLeft, fillTop, fillRight - fillLeft,
trackBottom - trackTop);
}
}
g.translate(-trackRect.x, -trackRect.y);
}
}
让司机测试它:
public class ProgressExample extends JFrame
{
public ProgressExample()
{
super("Progress Example");
ProgressSlider mSlider = new ProgressSlider();
mSlider.setMinimum(0);
mSlider.setMaximum(100);
mSlider.setValue(20);
mSlider.setProgress(50);
getContentPane().setLayout(new FlowLayout());
getContentPane().add(slider);
getContentPane().add(mSlider);
}
public static void main(String args[])
{
ProgressExample f = new ProgressExample();
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
f.setSize(300, 80);
f.show();
}
}
答案 2 :(得分:0)
好吧,我看不出这是怎么回事。您需要自定义滑块UI并更改模型,因为您无需存储两条模型信息,即“轨道值”和“拇指值”。
如果你想要一个大黑客,那么你将两个滑块相互重叠。底部滑块用于轨道值,顶部用于拇指值。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.metal.*;
public class SliderTest extends JFrame
{
public SliderTest()
{
JSlider bottom = createSlider();
bottom.setUI( new MySliderUI1() );
JSlider top = createSlider();
top.setUI( new MySliderUI2() );
top.setOpaque(false);
JPanel panel = new JPanel();
panel.setLayout( new OverlapLayout() );
panel.add( bottom );
panel.add( top );
getContentPane().add( panel );
}
private JSlider createSlider()
{
JSlider slider = new JSlider(2, 50);
slider.createStandardLabels(10, 5);
slider.setMajorTickSpacing(10);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setValue(20);
return slider;
}
class MySliderUI1 extends MetalSliderUI
{
public void paintThumb(Graphics g) {}
}
class MySliderUI2 extends MetalSliderUI
{
public void paintTrack(Graphics g) {}
}
public static void main(String[] args)
{
JFrame frame = new SliderTest();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
您还需要Overlap Layout。如何让它们保持同步取决于你。该解决方案有许多缺陷,但它可能会给你一些想法。