有没有办法防止光标被某个字符串点击?

时间:2016-12-07 23:28:35

标签: java user-interface javafx mouseevent

我试图这样做,以便当我点击某个字符串时,闪烁的光标不会被放置在那里。类似于命令行界面中的工作目录显示。 我以为我可以

  • 首先,获取文本的像素宽度和高度
  • 然后,我可以找到阻止鼠标事件到达组件部分的方法(例如激活玻璃窗格)

问题是,文本位于TextArea中,因此我必须覆盖TextAreas MouseEvent侦听器。

对于这个问题,似乎有一个更好,更少hacky的解决方案。我更喜欢JavaFX中的答案,但如果不可能,那么Swing答案就可以了。

1 个答案:

答案 0 :(得分:1)

尝试以下解决方案,看看它是否合适。它的作用是覆盖positionCaret中的TextArea方法,并防止它在用户点击阻止范围内时执行任何操作。在该示例中,用户无法在其中放置插入符号的被阻止文本是" Lorem Ipsum:"每行都有前缀。

sample

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class InputBlocker extends Application {
    private static final String TEXT =
            "Lorem ipsum: dolor sit amet, consectetur adipiscing elit. Nulla in nisi sed neque porttitor volutpat. \n" +
            "\n" +
            "Lorem ipsum: Maecenas non mauris scelerisque, egestas felis quis, venenatis risus. Etiam eu ornare justo, at pulvinar tortor. ";

    private static final String FORBIDDEN_PREFIX = "Lorem ipsum: ";

    @Override
    public void start(final Stage stage) throws Exception {
        TextArea textArea = new BlockingTextArea(TEXT, FORBIDDEN_PREFIX);
        textArea.setWrapText(true);

        stage.setScene(new Scene(textArea));
        stage.show();
    }

    public static void main(String[] args) throws Exception {
        launch(args);
    }
}

class BlockingTextArea extends TextArea {
    private final String forbiddenPrefix;
    private List<Range> blockedRanges;

    public BlockingTextArea(String text, String forbiddenPrefix) {
        super(text);

        this.forbiddenPrefix = forbiddenPrefix;
        this.blockedRanges = findBlockedRanges(text);

        textProperty().addListener((observable, oldValue, newValue) ->
                blockedRanges = findBlockedRanges(newValue)
        );
    }

    @Override
    public void positionCaret(int pos) {
        Optional<Range> blockedRange =
                blockedRanges.stream()
                        .filter(range -> range.containsExclusive(pos))
                        .findFirst();

        if (blockedRange.isPresent()) {
            return;
        }

        super.positionCaret(pos);
    }

    private List<Range> findBlockedRanges(String text) {
        List<Range> blocked = new ArrayList<>();

        int idx = 0;
        while (idx < text.length()) {
            if (text.startsWith(forbiddenPrefix, idx)) {
                blocked.add(new Range(idx, idx + forbiddenPrefix.length()));
                idx += forbiddenPrefix.length();
            }

            int nextIdx = text.indexOf("\n", idx);
            if (nextIdx == -1) {
                break;
            }

            idx = nextIdx + 1;
        }

        return blocked;
    }

    private class Range {
        private final int min;
        private final int max;

        Range(int min, int max) {
            this.min = min;
            this.max = max;
        }

        public int getMin() {
            return min;
        }

        public int getMax() {
            return max;
        }

        boolean containsExclusive(int pos) {
            return min <= pos && pos < max;
        }
    }
}

上述解决方案存在一些可用性问题:

  • 用户仍然可以在被阻止的文本之后定位角色,然后退格以删除到被阻止的文本区域。也许覆盖replaceTextdeleteText可能有助于解决这个问题。
  • 拖动并单击通过阻塞文本区域的选择因为它们依赖于插入符号位置而变得有点不稳定。也许覆盖selectPositionCaretselectRange或其他一些方法可能有助于解决这个问题。

但无论如何,也许上面的代码可以举例说明如何解决问题并解决问题。

而不是TextArea,第三方RichTextFX可能是一个更好的候选控件,可用于此类应用程序,因为它允许样式化文本内容,可以提供更好的用户体验,其中插入符号的文本无法定位的方式与文本的其余部分的方式不同。我还没有就如何为RichTextFX实现插入符号阻止功能进行任何调查。