我正在尝试创建一个可供屏幕阅读器访问的简单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的新手.谢谢!
这是基于您的代码的完整工作示例。
披露:对于屏幕阅读器,我在 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)