JavaFX"控制台"输出的可访问屏幕阅读

Zir*_*con 9 java javafx

我正在尝试创建一个可供屏幕阅读器访问的简单UI.我一直很成功,但我无法以屏幕阅读器读取新文本输出的方式设计UI.

目前,我有一个TextArea显示匿名PrintStream创建和设置的输出System.setOut.有时我打开一个TextFieldfor字符串输入,但我一直只是TextArea用来测试文本的读取(现在它只是监听按键以显示更多文本用于测试目的).

问题是:当通过System.out将新文本添加到TextArea屏幕阅读器时,屏幕阅读器不会读取它.我仍然可以使用箭头键向上导航以读取添加的内容,但在首次添加时不会读取.有没有办法让屏幕阅读器TextArea更像是一个标准控制台(在其中自动读取所有新文本)?我正在使用NVDA.

事情我已经尝试:
-使用TextArea.notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT)
-使用TextArea.requestFocus()并TextArea.notifyAccessibleAttributeChanged(AccessibleAttribute.FOCUS_NODE)
-对禁用自动冲洗PrintStream使用,而TextArea.setAccessibleText(theNewText)冲洗时
-使用一个隐藏的Label一套新的文字和关注它(我还在这一个摆弄;屏幕阅读器无法读取实际"隐藏的"文字,所以我试图找到一种方法来绘制它,但也是"看不见的",也许在TextArea某种程度上背后)
- 改变焦点到另一个Node和后面,这不是我喜欢的工作,因为它读取其他Nodes可访问东西,然后读取整个身体TextArea
- 这些的各种组合

我似乎无法让它工作.我觉得我在这里缺少一些简单而明显的东西,但JavaFX Accessibility API仍然相对较新,我无法找到像这样的特定问题的解决方案.

这是我的相关代码Application,如果有帮助的话:

@Override
public void start(Stage primaryStage) {
    try {
        primaryStage.setTitle("Test");
        root = new BorderPane();
        root.setFocusTraversable(false);
        Scene scene = new Scene(root,800,600);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        //Create middle
        output = new TextArea();
        output.setEditable(false);
        output.setFocusTraversable(false); //I've tried true also, just to test
        output.setAccessibleRole(AccessibleRole.TEXT_AREA);
        root.setCenter(output);
        ...
        //Begin
        primaryStage.show();
        Thread th = new Thread(new AppMain());
        th.start();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

@Override
public void init() {
    //Set output to TextArea
    System.setOut(new PrintStream(new OutputStream() {
        @Override
        public void write(int b) throws IOException {
            appendTextArea(String.valueOf((char) b));
        }
    }, true)); //I've also overriden flush while this is false, see above
}

public void appendTextArea(String str) {
    Platform.runLater(() -> {
        output.appendText(str);
    });
}
Run Code Online (Sandbox Code Playgroud)

我非常感谢您提供的任何帮助或建议.我一直在搞乱这个小问题太久了,我还是JavaFX的新手.谢谢!

Mic*_*dis 1

这是基于您的代码的完整工作示例。

披露:对于屏幕阅读器,我在 Mac 上使用“Voice Over Utility”,但希望这与您的环境没有太大差异。

关键是利用Control#executeAccessibleAction方法。

例子:

TextArea.executeAccessibleAction(AccessibleAction.SET_TEXT_SELECTION, start, finish);
Run Code Online (Sandbox Code Playgroud)

应用类

package application;

import java.io.PrintStream;
import java.io.IOException;
import java.io.OutputStream;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleRole;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;

public class Main extends Application
{
    private TextArea    output;
    private BorderPane  root;

    private final StringBuilder STR_BUFFER = new StringBuilder();
    private static final String NEW_LINE   = System.lineSeparator();

    @Override
    public void start(Stage primaryStage)
    {
        try
        {
            primaryStage.setTitle("Test");
            root = new BorderPane();
            root.setFocusTraversable(false);
            Scene scene = new Scene(root, 800, 600);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);

            // Create middle
            output = new TextArea();
            output.setEditable(false);
            output.setFocusTraversable(true); // I've tried true also, just to test 

            // ----------------------------------------------
            // Tell the Screen Reader what it needs to access
            // ----------------------------------------------
            output.setAccessibleRole(AccessibleRole.TEXT_AREA);

            root.setCenter(output);
            // ...

            // Begin
            primaryStage.show();

            // start the thread
            Thread th = new Thread(new AppMain());
            th.start();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    @Override
    public void init()
    {
        // Set output to TextArea when we have a full string
        System.setOut(new PrintStream(new OutputStream()
        {
            @Override
            public void write(int b) throws IOException
            {
                if (b == '\r')
                {
                    return;
                }
                if (b == '\n')
                {
                    final String text = STR_BUFFER.toString() + NEW_LINE;
                    appendTextArea(text);
                    STR_BUFFER.setLength(0);
                }
                else
                {
                    STR_BUFFER.append((char) b);
                }
            }
        }, true));
    }

    public void appendTextArea(String str)
    {
        Platform.runLater(() ->
        {
            int anchor = output.getText().length();

            output.appendText(str);

            // just to clear it
            output.positionCaret(0);

            // ----------------------------------------------
            // Tell the Screen Reader what it needs to do
            // ----------------------------------------------
            output.executeAccessibleAction(AccessibleAction.SET_TEXT_SELECTION, anchor, anchor + str.length());
        });
    }

    public static void main(String[] args)
    {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

线程类

/*
 * Just to simulate a feed to the console (textArea).
 * This will die after 1 minute.
 */
package application;

public class AppMain implements Runnable
{
    @Override
    public void run()
    {
        int i = 0;
        long start = System.currentTimeMillis();

        while (System.currentTimeMillis() - start < 60000)
        {
            try
            {
                Thread.sleep(3000);
            }
            catch (InterruptedException e)
            {}
            System.out.println("This is line number " + ++i);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)