-2

Is that possible to distribute a string in JTextField like this: ("bruno" in blue text field).

The JTextField is divided automatically into many equally spaced positions as the maximal column value.
enter image description here

I write this code to explain why font tracking doesn't work for me:

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.font.TextAttribute;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

public class SpacedFieldTest extends JFrame{

    Map<TextAttribute, Object> fontAttributes = new HashMap<TextAttribute,     Object>();
    int maxColumn;
    String text;
    public SpacedFieldTest(String text, int maxColumn) {
        this.text = text;
        this.maxColumn = maxColumn;
        addContent();
        pack();
    }

    private void addContent() {
        setLayout(new FlowLayout());
        final SpacedField field = new SpacedField();
        configureField(field, "Arial", 20f);
        getContentPane().add(field);

        JSpinner spinner = createSpinner(field);
        getContentPane().add(spinner);

        JLabel tracking = new JLabel("Tracking");
        getContentPane().add(tracking);
    }

    private JSpinner createSpinner(final SpacedField field) {
        float min = 0.0f;
        float value = 0.1f;
        float max = 3.0f;
        float stepSize = 0.1f;
            SpinnerNumberModel model = new SpinnerNumberModel(value, min,     max, stepSize);
            JSpinner spinner = new JSpinner(model){
                @Override
                public Object getValue() {
                    fontAttributes.put(TextAttribute.TRACKING,     super.getValue());
                        field.setFont(field.getFont().deriveFont(fontAttributes));
                    return super.getValue();
                }

            };
        spinner.setPreferredSize(new Dimension(50,25));
        JSpinner.NumberEditor editor =     (JSpinner.NumberEditor)spinner.getEditor();
        DecimalFormat format = editor.getFormat();
        format.setMinimumFractionDigits(1);
        return spinner;
    }

    private void configureField(JTextField field, String family, float     fontSize) {

        fontAttributes.put(TextAttribute.SIZE, fontSize);
        fontAttributes.put(TextAttribute.FAMILY, family);
        Font font = Font.getFont(fontAttributes);
        field.setFont(font);
        field.setText(text);
    }

    class SpacedField extends JTextField{
        final int WIDTH = 300;
        final int HEIGHT = 25;
        int gridWidth;
        public SpacedField() {

            gridWidth = WIDTH/maxColumn;
            setPreferredSize(new Dimension(WIDTH, HEIGHT));
            setDocument(new SpacedFieldDocument());
        }
        public void paintComponent(Graphics g){
            super.paintComponent(g);
            paintGrid(g);
        }
        private void paintGrid(Graphics g) {
            for(int i=1; i<=maxColumn; i++){
                g.drawLine(i*gridWidth, HEIGHT/2, i*gridWidth, HEIGHT);
            }

        }
    }

    class SpacedFieldDocument extends PlainDocument{
        public void insertString(int offs, String str, AttributeSet a)
                throws BadLocationException {

            if(getLength()>maxColumn)
                return;
            super.insertString(offs, str, a);
        }
    }

    public static void main(String args[]){

        final SpacedFieldTest sf = new SpacedFieldTest("abcde", 5);
        sf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                sf.setVisible(true);
            }
        });
    }
}

1 Answers1

0

Possible-ish.

What you will want to do is set up a separate JTextField for each available character. This is best implemented as an independent class, one that extends JComponent, but that's up to you.

I recommend throwing these in your initializers:

private final LinkedList<JTextField> fields = new LinkedList<>();
private KeyListener fieldKeyListener = new KeyListener(){

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() != KeyEvent.VK_BACK_SPACE && e.getKeyCode() != KeyEvent.VK_DELETE) {
            System.out.println("Key code: " + e.getKeyCode());
            int index = fields.indexOf(e.getSource());
            int newIndex = index + 1;
            if(newIndex < fields.size()) fields.get(newIndex).requestFocus();
            else
                System.out.println(gatherString());
                //or use whichever action should retrieve the string
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

};

For the meat of it, you'll want to do this. I went ahead and constructed it on-the-fly in the middle of a JFrame, but if you will conceivably be doing this more than once, you'll want to adapt it to the initializer of a constructor.

private JComponent getDistField() {
    JPanel panel = new JPanel();
    panel.setLayout(new GridLayout(1, charCount));

    for(int i = 0; i < charCount; i++) {
        fields.add(new JTextField(1));

        /* All customizations to look and feel, for each component, go here */

        //document must be unique for each field, or text will duplicate.
        fields.getLast().setDocument(new PlainDocument(){
            @Override
            public void insertString(int offs, String str, AttributeSet a)
                    throws BadLocationException {
                if(getLength() + str.length() <= 1)
                    super.insertString(offs, str, a);
            }
        });
        fields.getLast().addKeyListener(fieldKeyListener);
        panel.add(fields.getLast());
    }

    return panel;
}

I'm presuming that "charCount" was set earlier (or even passed in as a parameter) for the number of characters that the field accepts. That iterator creates a sequence of sub-text-fields; each of which will handle one character and immediately update the position of the caret. Note that the document needs to be unique to each field, or the text will duplicate itself the whole way across!

You could also, in theory, do this with a caret listener instead of a key listener; however, they're tangential to key presses and are completely unaware of which key was just down. Doesn't mean it's impossible to figure out, but I can't find a clean way to do it.

When you're ready for the full content of your entered string, call something like this:

public String gatherString() {
    StringBuilder builder = new StringBuilder();
    for(JTextField field : fields) {
        builder.append(field.getText());
    }
    return builder.toString();
}

It assembles the string from the content of every field. Thankfully, empty fields make no contribution; if you want them to also be uneditable, you could try setting them to disabled and triggering their enablement with the population of the field preceding them; or even add a mouse listener that finds the last unedited field and highlights it on any click. It's all about the listeners (/observers).

Hopefully this will do it for you, and I'm sure you can see why you might want this to be its own module. I wish you luck!

Additionally, it will help very much if you look into proper formatting of your question in the future. I can't upvote it in its current condition, and it will be received much better by the community. It will help you to look into "How do I ask a good question?".

Community
  • 1
  • 1
Michael Macha
  • 1,729
  • 1
  • 16
  • 25
  • This solution is very expensive. I have to re-implement all events (caret, selection, navigation with arrow keys, undo, redo....) – user2358141 May 29 '15 at 17:05