2013-05-29 5 views
0

Я пытался добиться следующего.JSpinner с пользовательским рендером

Я хочу использовать JSpinner, который использует числа для своих данных, но я хочу, чтобы он отображался следующим образом: «6/10», то есть «значение/максимум», где JSpinner по умолчанию показывает только «значение ». Конечно, я использую SpinnerNumberModel, чтобы работать с целыми числами как данные.

Я искал аналоговое поведение по сравнению с рендерером JCombobox и/или редактором, рендерером JTable и/или редактором, но, похоже, JSpinner не работает таким образом. Я нашел this example, для пользовательского контента внутри счетчика. В примере используется JLabel для отображения значков, но в моем случае я должен иметь возможность редактировать содержимое и проверять ввод после нажатия . Введите.

Так это то, что я в основном сделал:

JSpinner spinner = new JSpinner(new SpinnerNumberModel(1,1,10,1)); 
spinner.setEditor(new CustomSpinnerEditor(spinner)); 

где CustomSpinnerEditor расширяет JTextField. Я набросил на него некоторые свойства inputmap/actionmap (для введите), добавив ChangeListener к счетчику, чтобы иметь возможность обновлять содержимое текстового поля, когда пользователь нажимает на стрелки, добавил FocusListener, чтобы выбрать весь текст на focusGained и проверить вход на focusLost, ... и он работает.

Таким образом, проблема заключается не в том, чтобы заставить его работать. Проблема в том, что это слишком много работает для чего-то очень простого и что я могу потерять некоторые функции, не используя редактор по умолчанию. Если компонент просто предоставил возможность установить на него средство визуализации, у меня был бы крючок, где я просто возвращал JLabel с правильной строкой, как если бы я делал это с JComboBox.

Есть ли более простой способ достичь того, что я хочу?

ответ

2

Вы можете избежать создания специализированного редактора, но я не знаю, если это становится проще: получить текстовое поле редактора по умолчанию, используя

JFormattedTextField f = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField(); 

, а затем установить форматтер завод на этом текстовом поле:

f.setFormatterFactory(new AbstractFormatterFactory() { 
    @Override 
    public AbstractFormatter getFormatter(JFormattedTextField tf) { 
     // return your formatter 
    } 
}); 

форматировщик возвращаемого метод getFormatter затем должно преобразовать между значением и строковым представлением этого, используя свою stringToValue и valueToString методы.

1

Я не знаю, подумаете ли вы об этом проще.

JSpinner Test

Я создал Renderer класс, который содержит значение и строку значения, которые вы хотите отобразить.

В пользовательской модели spinner я создал список из Renderer экземпляров и перешел в список.

Я собрал классы, чтобы я мог скопировать и вставить один блок кода. Эти 3 класса должны храниться в разных исходных модулях.

Вот один из способов сделать то, что вы хотите.

import java.awt.Color; 
import java.awt.Dimension; 
import java.util.ArrayList; 
import java.util.List; 

import javax.swing.AbstractSpinnerModel; 
import javax.swing.BorderFactory; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JSpinner; 
import javax.swing.SwingUtilities; 
import javax.swing.border.Border; 
import javax.swing.event.ChangeEvent; 
import javax.swing.event.ChangeListener; 

public class SpinnerTesting implements Runnable { 

    @Override 
    public void run() { 
     JFrame frame = new JFrame("JSpinner Test"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     JPanel mainPanel = new JPanel(); 
     final JSpinner spinner = new JSpinner(new CustomSpinnerModel(4, 1, 10, 
       1)); 
     setBorder(spinner); 
     spinner.addChangeListener(new ChangeListener() { 
      @Override 
      public void stateChanged(ChangeEvent event) { 
       Renderer renderer = (Renderer) spinner.getValue(); 
       System.out.println(renderer.getValue()); 
      } 
     }); 
     mainPanel.add(spinner); 

     frame.add(mainPanel); 
     frame.setSize(new Dimension(300, 100)); 
     frame.setVisible(true); 
    } 

    private void setBorder(JSpinner spinner) { 
     Border lineBorder = BorderFactory.createLineBorder(Color.BLACK); 
     Border insetBorder = BorderFactory.createEmptyBorder(0, 10, 0, 10); 
     spinner.setBorder(BorderFactory.createCompoundBorder(lineBorder, 
       insetBorder)); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new SpinnerTesting()); 
    } 

    public class CustomSpinnerModel extends AbstractSpinnerModel { 

     private int    value; 
     private int    minimum; 
     private int    maximum; 
     private int    stepSize; 

     private int    listIndex; 

     private List<Renderer> spinnerList; 

     public CustomSpinnerModel(int value, int minimum, int maximum, 
       int stepSize) throws IllegalArgumentException { 
      if (!((minimum <= value) && (value <= maximum))) { 
       throw new IllegalArgumentException(
         "(minimum <= value <= maximum) is false"); 
      } 

      this.value = value; 
      this.minimum = minimum; 
      this.maximum = maximum; 
      this.stepSize = stepSize; 

      this.spinnerList = new ArrayList<Renderer>(); 
      setSpinnerList(); 
     } 

     private void setSpinnerList() { 
      int index = 0; 
      for (int i = minimum; i <= maximum; i += stepSize) { 
       Renderer renderer = new Renderer(i, maximum); 
       if (i == value) { 
        listIndex = index; 
       } 
       spinnerList.add(renderer); 
       index++; 
      } 
     } 

     @Override 
     public Object getNextValue() { 
      listIndex = Math.min(++listIndex, (spinnerList.size() - 1)); 
      fireStateChanged(); 
      return spinnerList.get(listIndex); 
     } 

     @Override 
     public Object getPreviousValue() { 
      listIndex = Math.max(--listIndex, 0); 
      fireStateChanged(); 
      return spinnerList.get(listIndex); 
     } 

     @Override 
     public Object getValue() { 
      return spinnerList.get(listIndex); 
     } 

     @Override 
     public void setValue(Object object) { 

     } 

    } 

    public class Renderer { 

     private int  value; 

     private String valueString; 

     public Renderer(int value, int maximum) { 
      this.value = value; 
      this.valueString = value + "/" + maximum; 
     } 

     public int getValue() { 
      return value; 
     } 

     public String getValueString() { 
      return valueString; 
     } 

     @Override 
     public String toString() { 
      return valueString + " "; 
     } 

    } 

} 
Смежные вопросы