С большой помощью Гарри Радости о пряча ручку, наконец, я был в состоянии решить эту проблему.
Если вы отметите ответ Гарри, вы можете прочитать об исключении метода BasicSliderUI.paintThumb(Graphics)
для скрытия ручки. Он отлично работает на большинстве L & F не основано на синтезаторе (и это означает, что Nimbus), где такой подход будет отличаться друг от друга, но выполнимо с помощью настроек на L & F.
Установка это немного сложнее: имея PropertyChangeListener
на UIManager
, который проверяет любые изменения на L & F и устанавливает подходящий делегат пользовательского интерфейса на компоненте, делает магию (в этом решении я просто показываю тот, который основан на BasicSliderUI
, остальные - это копии). Кроме того, я немного изменил его, чтобы установить значение в позицию, где сначала щелкнут. Для того, чтобы делегат знал, должен ли он рисовать ручку или нет, я решил иметь свойство клиента на JSlider
под названием "ready"
, которое должно быть boolean
. Таким образом, делегат может быть установлен на любом JSlider
, а не только на расширенном.
Теперь, как управлять неизвестными значениями? Добавив еще одно новое свойство "selectedValue"
, он связан как с "value"
, так и с "ready"
и Integer
. "ready"
является false
или true
в зависимости от того, если это null
или нет, и наоборот, и как "value"
и "selectedValue"
держать то же значение (один из которых int
и другой Integer
), если не готов, который установил бы "selectedValue"
к null
. Это может показаться сложным, но это не так. Также есть небольшая настройка для очистки значения с помощью [Esc] и свойств распространения в реализованном компоненте.
Вот код:
BasicSliderUIExt
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.plaf.basic.BasicSliderUI;
public class BasicSliderUIExt extends BasicSliderUI {
public BasicSliderUIExt(JSlider slider) {
super(slider);
}
@Override
public void paintThumb(Graphics g) {
if (isReady(super.slider)) {
super.paintThumb(g);
}
}
@Override
protected TrackListener createTrackListener(final JSlider slider) {
return new TrackListener() {
@Override
public void mousePressed(MouseEvent event) {
if (isReady(slider)) {
super.mousePressed(event);
} else {
JSlider slider = (JSlider) event.getSource();
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
slider.setValue(valueForYPosition(event.getY()));
break;
case SwingConstants.HORIZONTAL:
slider.setValue(valueForXPosition(event.getX()));
break;
}
super.mousePressed(event);
super.mouseDragged(event);
}
}
@Override
public boolean shouldScroll(int direction) {
if (isReady(slider)) {
return super.shouldScroll(direction);
}
return false;
}};
}
public static boolean isReady(JSlider slider) {
Object ready = slider.getClientProperty(JSliderExt.READY_PROPERTY);
return (ready == null) || (!(ready instanceof Boolean)) || (((Boolean) ready).booleanValue());
}
}
JSliderExt
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Constructor;
import javax.swing.BoundedRangeModel;
import javax.swing.JSlider;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.SliderUI;
public class JSliderExt extends JSlider {
private static final long serialVersionUID = 1L;
public static final String EXTENT_PROPERTY = "extent";
public static final String MAXIMUM_PROPERTY = "maximum";
public static final String MINIMUM_PROPERTY = "minimum";
public static final String READY_PROPERTY = "ready";
public static final String SELECTED_VALUE_PROPERTY = "selectedValue";
public static final String VALUE_PROPERTY = "value";
public static final boolean READY_DEFAULT_VALUE = false;
protected SliderUI uix = new BasicSliderUIExt(this);
public JSliderExt(BoundedRangeModel model, boolean ready) {
super(model);
init(ready);
}
public JSliderExt(BoundedRangeModel model) {
super(model);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int orientation, int minimmum, int maximum, int value, boolean ready) {
super(orientation, minimmum, maximum, value);
init(ready);
}
public JSliderExt(int orientation, int minimmum, int maximum, int value) {
super(orientation, minimmum, maximum, value);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int minimmum, int maximum, int value, boolean ready) {
super(minimmum, maximum, value);
init(ready);
}
public JSliderExt(int minimmum, int maximum, int value) {
super(minimmum, maximum, value);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int minimmum, int maximum, boolean ready) {
super(minimmum, maximum);
init(ready);
}
public JSliderExt(int minimmum, int maximum) {
super(minimmum, maximum);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int orientation, boolean ready) {
super(orientation);
init(ready);
}
public JSliderExt(int orientation) {
super(orientation);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(boolean ready) {
super();
init(ready);
}
public JSliderExt() {
super();
init(READY_DEFAULT_VALUE);
}
private void init(boolean ready) {
UIManager.addPropertyChangeListener(new PropertyChangeListener() { // Changes the UI delegate in L&F change
@Override
public void propertyChange(PropertyChangeEvent event) {
if ("lookAndFeel".equals(event.getPropertyName())) {
Object newValue = event.getNewValue();
if ((newValue != null) && (newValue instanceof LookAndFeel)) {
LookAndFeel lookAndFeel = (LookAndFeel) newValue;
try {
if (lookAndFeel instanceof MetalLookAndFeel) {
JSliderExt.this.uix = new MetalSliderUIExt();
} else if (lookAndFeel instanceof com.sun.java.swing.plaf.motif.MotifLookAndFeel) {
JSliderExt.this.uix = new MotifSliderUIExt(JSliderExt.this);
} else if (lookAndFeel instanceof com.sun.java.swing.plaf.windows.WindowsLookAndFeel) {
JSliderExt.this.uix = new WindowsSliderUIExt(JSliderExt.this);
} else {
throw new NullPointerException("Default Look & Feel not matched");
}
} catch (Exception e) {
try {
Package sliderPackage = JSliderExt.this.getClass().getPackage();
String lookAndFeelName = lookAndFeel.getName();
if (lookAndFeelName.equals("CDE/Motif")) {
lookAndFeelName = "Motif";
} else if (lookAndFeelName.equals("Windows Classic")) {
lookAndFeelName = "Windows";
} else if (lookAndFeelName.startsWith("JGoodies")) {
lookAndFeelName = "Basic";
}
Class<?> sliderUiType = Class.forName(sliderPackage.getName() + "." + lookAndFeelName
+ "SliderUIExt");
Constructor<?> constructor1 = null;
try {
constructor1 = sliderUiType.getConstructor(JSlider.class);
} catch (Exception e3) { // Nothing to do here
}
Constructor<?> constructor0 = null;
try {
constructor0 = sliderUiType.getConstructor();
} catch (Exception e3) { // Nothing to do here
}
Object sliderUi = null;
if (constructor1 != null) {
sliderUi = constructor1.newInstance(JSliderExt.this);
} else if (constructor0 != null) {
sliderUi = constructor0.newInstance();
}
if ((sliderUi != null) && (sliderUi instanceof SliderUI)) {
JSliderExt.this.setUI((SliderUI) sliderUi);
}
} catch (Exception e2) {
JSliderExt.this.uix = new BasicSliderUIExt(JSliderExt.this);
}
}
} else {
JSliderExt.this.uix = new BasicSliderUIExt(JSliderExt.this);
}
updateUI();
}
}});
addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
String propertyName = event.getPropertyName();
if (READY_PROPERTY.equals(propertyName)) {
Object newValue = event.getNewValue();
if ((newValue == null) || (!(newValue instanceof Boolean)) || (((Boolean) newValue).booleanValue())) {
setSelectedValue(Integer.valueOf(getValue()));
} else {
setSelectedValue(null);
}
} else if (SELECTED_VALUE_PROPERTY.equals(propertyName)) {
Object newValue = event.getNewValue();
System.out.println(newValue);
if ((newValue != null) && (newValue instanceof Integer)) {
int value = getValue();
int newSelectedValue = ((Integer) newValue).intValue();
if (value != newSelectedValue) {
setValue(newSelectedValue);
}
setReady(true);
} else {
setReady(false);
}
repaint();
} else if (VALUE_PROPERTY.equals(propertyName)) {
setReady(true);
Object newValue = event.getNewValue();
if ((newValue != null) && (newValue instanceof Integer)) {
setSelectedValue((Integer) newValue);
}
}
}});
addKeyListener(new KeyAdapter() { // Enables escape key for clearing value
@Override
public void keyPressed(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
JSliderExt.this.setReady(false);
}
}});
setReady(ready);
}
@Override
public void setValue(int value) {
int oldValue = getValue();
super.setValue(value);
firePropertyChange(VALUE_PROPERTY, Integer.valueOf(oldValue), Integer.valueOf(value));
}
@Override
public void setExtent(int extent) {
int oldExtent = getExtent();
super.setExtent(extent);
firePropertyChange(EXTENT_PROPERTY, Integer.valueOf(oldExtent), Integer.valueOf(extent));
}
@Override
public void setMinimum(int minimum) {
int oldMinimum = getMinimum();
super.setMinimum(minimum);
firePropertyChange(MINIMUM_PROPERTY, Integer.valueOf(oldMinimum), Integer.valueOf(minimum));
}
@Override
public void setMaximum(int maximum) {
int oldMaximum = getMaximum();
super.setMaximum(maximum);
firePropertyChange(MAXIMUM_PROPERTY, Integer.valueOf(oldMaximum), Integer.valueOf(maximum));
}
public Integer getSelectedValue() {
return (Integer) getClientProperty(SELECTED_VALUE_PROPERTY);
}
public void setSelectedValue(Integer selectedValue) {
putClientProperty(SELECTED_VALUE_PROPERTY, selectedValue);
}
public boolean isReady() {
Object ready = getClientProperty(READY_PROPERTY);
return ((ready != null) && (ready instanceof Boolean)) ? ((Boolean) ready).booleanValue()
: READY_DEFAULT_VALUE;
}
public void setReady(boolean waiting) {
putClientProperty(READY_PROPERTY, Boolean.valueOf(waiting));
}
@Override
public void updateUI() {
setUI(this.uix);
updateLabelUIs();
}
}
Пожалуйста, обратите внимание, что с помощью этого кода, может потребоваться некоторые изменения в выборе делегатов в зависимости от вашего приложения, так как это предназначен для системы Windows. Как сказано, Synth/Nimbus нужно работать по-другому, но также и любой пользовательский L & F или для OSX необходимо, чтобы делегат был расширен и добавлен в слушателя.
Ну, это очень помогает, но это не полностью решает проблему. В одной руке наше приложение поддерживает 2 взгляда и чувств, один из которых - Windows, и, похоже, он не работает под Windows LAF. С другой стороны, я все еще не знаю, как установить неизвестное значение, так как параметр «BoundedRangeModel» корректирует значения, устанавливаемые между минимумом и максимумом, и связывает их с этим поведением. –
Он работает под Windows LAF, я просто установил его неправильно. В любом случае вторая проблема все еще не решена. –
@ YagoMéndezVidal, что еще не сделано? спрятать ручку? –