JEdi​​torPane、JScrollPane 和辅助功能

发布于 2024-11-24 04:02:53 字数 1622 浏览 6 评论 0原文

我有一个简单的 Swing 应用程序,其中 JEditorPane 封装在 JScrollPane 中。

不幸的是,屏幕阅读器软件如 JAWSNVDA 行为不正确。

当焦点进入 JEditorPane 时,它​​仅读取可访问名称,后跟“文本”,然后停止,而预期行为是继续读取 JEditorPane 的内容。

如果我不将 JEditorPane 包装在 JScrollPane 中,它会按预期工作。

我尝试使用 Monkey 检查可访问的树,但我看不到封装在 JScrollPane 中的 JEditorPane 与未封装的 JEditorPane 之间有任何相关差异。

有什么想法吗?

这是演示该问题的一个简短示例。如果焦点进入第一个 JEditorPane,JAWS 将读取“第一个编辑器窗格 - 编辑”。如果焦点进入第二个 JEditorPane,JAWS 会读取“第二个编辑器窗格 - 编辑 - 栏”。

public final class SmallExample {
  public static void main(String... aArgs){
    JFrame frame = new JFrame("Test Frame"); 

    JPanel panel = new JPanel();

    JEditorPane editorPane1 = new JEditorPane();
    editorPane1.setText("Foo");
    editorPane1.getAccessibleContext().setAccessibleName("first editorpane");
    editorPane1.getAccessibleContext().setAccessibleDescription("");

    JScrollPane scrollPane = new JScrollPane( editorPane1, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
    panel.add(scrollPane);

    JEditorPane editorPane2 = new JEditorPane();
    panel.add(editorPane2);
    editorPane2.setText("Bar");
    editorPane2.getAccessibleContext().setAccessibleName("second editorpane");
    editorPane2.getAccessibleContext().setAccessibleDescription("");    

    frame.getContentPane().add(panel);      

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }
}

I have a simple Swing application with a JEditorPane wrapped in a JScrollPane.

Unfortunately screen reader software like JAWS or NVDA does not behave correctly.

When focus enters the JEditorPane it only reads the accessible name followed by "text" and then stops, when the expected behavior is to continue reading the contents of the JEditorPane.

If I do not wrap the JEditorPane in the JScrollPane it works as expected.

I have tried inspecting the accessible tree using Monkey, but I cannot see any relevant difference between a JEditorPane wrapped in a JScrollPane and one that is not wrapped.

Any ideas?

Here is a brief sample that demonstrates the problem. If focus enters the first JEditorPane, JAWS reads "first editorpane - edit". If focus enters the second JEditorPane, JAWS reads "second editorpane - edit - bar".

public final class SmallExample {
  public static void main(String... aArgs){
    JFrame frame = new JFrame("Test Frame"); 

    JPanel panel = new JPanel();

    JEditorPane editorPane1 = new JEditorPane();
    editorPane1.setText("Foo");
    editorPane1.getAccessibleContext().setAccessibleName("first editorpane");
    editorPane1.getAccessibleContext().setAccessibleDescription("");

    JScrollPane scrollPane = new JScrollPane( editorPane1, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
    panel.add(scrollPane);

    JEditorPane editorPane2 = new JEditorPane();
    panel.add(editorPane2);
    editorPane2.setText("Bar");
    editorPane2.getAccessibleContext().setAccessibleName("second editorpane");
    editorPane2.getAccessibleContext().setAccessibleDescription("");    

    frame.getContentPane().add(panel);      

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

红ご颜醉 2024-12-01 04:02:55

我自己找到了一个解决方法:

如果我

  1. 修改可访问树以跳过 JScrollPane 和 JViewPort,
  2. 避免从 JEditorPane 发送可访问属性更改事件,

那么它就可以工作。

我仍然非常感谢任何关于为什么这样做有效的见解(我找到了解决方法,将 editorPane2 的 AccessibleContext 替换为 editorPane1 的 AccessibleContext,并逐渐切换回方法,直到找到我需要的方法覆盖)。

这是一个工作示例(不再那么简短了):

public final class Example {

  public static void main(String... aArgs){
    JFrame frame = new JFrame("Test Frame"); 

    final JPanel panel = new JPanel(){
        public AccessibleContext getAccessibleContext() {
            if(accessibleContext==null){
                accessibleContext = new AccessibleContextWrapper(super.getAccessibleContext()){
                    public Accessible getAccessibleChild(int i) {
                        Accessible accessibleChild = super.getAccessibleChild(i);
                        while(accessibleChild!=null && (accessibleChild instanceof JScrollPane || accessibleChild instanceof JViewport)){
                            accessibleChild = accessibleChild.getAccessibleContext().getAccessibleChild(0);
                        }
                        return accessibleChild;
                    }
                };
            }
            return accessibleContext;
        }
    };

    final JEditorPane editorPane = new JEditorPane(){
        public AccessibleContext getSuperAccessibleContext() {
            return super.getAccessibleContext();
        }
        @Override
        public AccessibleContext getAccessibleContext() {
            return new AccessibleContextWrapper(super.getAccessibleContext()){
                public Accessible getAccessibleParent() {
                    Accessible parent = super.getAccessibleParent();
                    while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
                        parent = parent.getAccessibleContext().getAccessibleParent();
                    }
                    return parent;
                }

                public int getAccessibleIndexInParent() {
                    int res = super.getAccessibleIndexInParent();                           
                    Accessible parent = super.getAccessibleParent();
                    while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
                        res = parent.getAccessibleContext().getAccessibleIndexInParent();
                        parent = parent.getAccessibleContext().getAccessibleParent();
                    }
                    return res;
                }

                public void addPropertyChangeListener(
                        PropertyChangeListener listener) {                  
                }

                public void removePropertyChangeListener(
                        PropertyChangeListener listener) {                  
                }           
            };
        }       
    };
    editorPane.setText("Foo");
    editorPane.getAccessibleContext().setAccessibleName("first editorpane");
    editorPane.getAccessibleContext().setAccessibleDescription("");
    editorPane.getAccessibleContext();
    JScrollPane scrollPane = new JScrollPane( editorPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );  

    panel.add(scrollPane);

    frame.getContentPane().add(panel);      

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }

  public static class AccessibleContextWrapper extends AccessibleContext {
    private final AccessibleContext inner;

    public AccessibleContextWrapper(AccessibleContext inner) {
        this.inner = inner;
    }

    public String getAccessibleName() {
        return inner.getAccessibleName();
    }

    public void setAccessibleName(String s) {
        inner.setAccessibleName(s);
    }

    public String getAccessibleDescription() {
        return inner.getAccessibleDescription();
    }

    public void setAccessibleDescription(String s) {
        inner.setAccessibleDescription(s);
    }

    public AccessibleRole getAccessibleRole() {
        return inner.getAccessibleRole();
    }

    public AccessibleStateSet getAccessibleStateSet() {
        return inner.getAccessibleStateSet();
    }

    public Accessible getAccessibleParent() {
        return inner.getAccessibleParent();
    }

    public void setAccessibleParent(Accessible a) {
        inner.setAccessibleParent(a);
    }

    public int getAccessibleIndexInParent() {
        return inner.getAccessibleIndexInParent();
    }

    public int getAccessibleChildrenCount() {
        return inner.getAccessibleChildrenCount();
    }

    public Accessible getAccessibleChild(int i) {
        return inner.getAccessibleChild(i);
    }

    public Locale getLocale() throws IllegalComponentStateException {
        return inner.getLocale();
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        inner.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        inner.removePropertyChangeListener(listener);
    }

    public AccessibleAction getAccessibleAction() {
        return inner.getAccessibleAction();
    }

    public AccessibleComponent getAccessibleComponent() {
        return inner.getAccessibleComponent();
    }

    public AccessibleSelection getAccessibleSelection() {
        return inner.getAccessibleSelection();
    }

    public AccessibleText getAccessibleText() {
        return inner.getAccessibleText();
    }

    public AccessibleEditableText getAccessibleEditableText() {
        return inner.getAccessibleEditableText();
    }

    public AccessibleValue getAccessibleValue() {
        return inner.getAccessibleValue();
    }

    public AccessibleIcon[] getAccessibleIcon() {
        return inner.getAccessibleIcon();
    }

    public AccessibleRelationSet getAccessibleRelationSet() {
        return inner.getAccessibleRelationSet();
    }

    public AccessibleTable getAccessibleTable() {
        return inner.getAccessibleTable();
    }

    public void firePropertyChange(String propertyName, Object oldValue,
            Object newValue) {
        inner.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

}

I found a workaround myself:

If I

  1. modify the accessible tree to skip the JScrollPane and JViewPort
  2. avoid sending accessible property-changed events from the JEditorPane

then it works.

I would still very much appreciate any insight into why this works (I found the workaround by replacing the AccessibleContext for editorPane2 with the one for editorPane1 and gradually switching the methods back until I found the ones that I needed to override).

Here is a working example (it is not so brief anymore):

public final class Example {

  public static void main(String... aArgs){
    JFrame frame = new JFrame("Test Frame"); 

    final JPanel panel = new JPanel(){
        public AccessibleContext getAccessibleContext() {
            if(accessibleContext==null){
                accessibleContext = new AccessibleContextWrapper(super.getAccessibleContext()){
                    public Accessible getAccessibleChild(int i) {
                        Accessible accessibleChild = super.getAccessibleChild(i);
                        while(accessibleChild!=null && (accessibleChild instanceof JScrollPane || accessibleChild instanceof JViewport)){
                            accessibleChild = accessibleChild.getAccessibleContext().getAccessibleChild(0);
                        }
                        return accessibleChild;
                    }
                };
            }
            return accessibleContext;
        }
    };

    final JEditorPane editorPane = new JEditorPane(){
        public AccessibleContext getSuperAccessibleContext() {
            return super.getAccessibleContext();
        }
        @Override
        public AccessibleContext getAccessibleContext() {
            return new AccessibleContextWrapper(super.getAccessibleContext()){
                public Accessible getAccessibleParent() {
                    Accessible parent = super.getAccessibleParent();
                    while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
                        parent = parent.getAccessibleContext().getAccessibleParent();
                    }
                    return parent;
                }

                public int getAccessibleIndexInParent() {
                    int res = super.getAccessibleIndexInParent();                           
                    Accessible parent = super.getAccessibleParent();
                    while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
                        res = parent.getAccessibleContext().getAccessibleIndexInParent();
                        parent = parent.getAccessibleContext().getAccessibleParent();
                    }
                    return res;
                }

                public void addPropertyChangeListener(
                        PropertyChangeListener listener) {                  
                }

                public void removePropertyChangeListener(
                        PropertyChangeListener listener) {                  
                }           
            };
        }       
    };
    editorPane.setText("Foo");
    editorPane.getAccessibleContext().setAccessibleName("first editorpane");
    editorPane.getAccessibleContext().setAccessibleDescription("");
    editorPane.getAccessibleContext();
    JScrollPane scrollPane = new JScrollPane( editorPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );  

    panel.add(scrollPane);

    frame.getContentPane().add(panel);      

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }

  public static class AccessibleContextWrapper extends AccessibleContext {
    private final AccessibleContext inner;

    public AccessibleContextWrapper(AccessibleContext inner) {
        this.inner = inner;
    }

    public String getAccessibleName() {
        return inner.getAccessibleName();
    }

    public void setAccessibleName(String s) {
        inner.setAccessibleName(s);
    }

    public String getAccessibleDescription() {
        return inner.getAccessibleDescription();
    }

    public void setAccessibleDescription(String s) {
        inner.setAccessibleDescription(s);
    }

    public AccessibleRole getAccessibleRole() {
        return inner.getAccessibleRole();
    }

    public AccessibleStateSet getAccessibleStateSet() {
        return inner.getAccessibleStateSet();
    }

    public Accessible getAccessibleParent() {
        return inner.getAccessibleParent();
    }

    public void setAccessibleParent(Accessible a) {
        inner.setAccessibleParent(a);
    }

    public int getAccessibleIndexInParent() {
        return inner.getAccessibleIndexInParent();
    }

    public int getAccessibleChildrenCount() {
        return inner.getAccessibleChildrenCount();
    }

    public Accessible getAccessibleChild(int i) {
        return inner.getAccessibleChild(i);
    }

    public Locale getLocale() throws IllegalComponentStateException {
        return inner.getLocale();
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        inner.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        inner.removePropertyChangeListener(listener);
    }

    public AccessibleAction getAccessibleAction() {
        return inner.getAccessibleAction();
    }

    public AccessibleComponent getAccessibleComponent() {
        return inner.getAccessibleComponent();
    }

    public AccessibleSelection getAccessibleSelection() {
        return inner.getAccessibleSelection();
    }

    public AccessibleText getAccessibleText() {
        return inner.getAccessibleText();
    }

    public AccessibleEditableText getAccessibleEditableText() {
        return inner.getAccessibleEditableText();
    }

    public AccessibleValue getAccessibleValue() {
        return inner.getAccessibleValue();
    }

    public AccessibleIcon[] getAccessibleIcon() {
        return inner.getAccessibleIcon();
    }

    public AccessibleRelationSet getAccessibleRelationSet() {
        return inner.getAccessibleRelationSet();
    }

    public AccessibleTable getAccessibleTable() {
        return inner.getAccessibleTable();
    }

    public void firePropertyChange(String propertyName, Object oldValue,
            Object newValue) {
        inner.firePropertyChange(propertyName, oldValue, newValue);
    }
  }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文