一段时间后,我终于找到了一个解决方案,即满足我所有要求的编码策略。也许它在技术上不是一个好的解决方案,但它是一个功能性的解决方案,因为它产生了我需要提供的体验,让我不用接触 所有 已经存在的 Java 代码。好了!
1。一个托管 bean,用于翻转可访问性模式
下面的托管 bean 负责打开和关闭可访问性以响应用户单击某个命令链接。这个想法是在可访问性模式打开时向会话添加一个属性,并在可访问性模式关闭时将其删除。
这些操作会返回当前页面的重定向,以便用户立即看到界面从一种模式更改为另一种模式。
打开辅助功能后,视图路径更改为附加一个/ac,因为我所有的可访问视图都具有与不可访问视图相同的文件名,但位于名为ac 的子目录中。
@ManagedBean
@RequestScoped
public class AccessibilityMB {
private boolean accessibilityOn = false;
@PostConstruct
public void init() {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session!=null) {
Boolean accessibilityMode = (Boolean)session.getAttribute("AccessibilityMode");
accessibilityOn = accessibilityMode!=null && accessibilityMode;
}
}
public String turnAccessibilityOff() {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session!=null) {
session.setAttribute("AccessibilityMode", accessibilityOn = true);
}
String viewId = context.getViewRoot().getViewId();
return viewId+"?faces-redirect=true";
}
public String turnAccessibilityOn() {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session!=null) {
accessibilityOn = false;
session.removeAttribute("AccessibilityMode");
}
String viewId = context.getViewRoot().getViewId();
int index = viewId.lastIndexOf("/ac/");
if(index>-1)
viewId = viewId.substring(0, index)+viewId.substring(index+3);
return viewId+"?faces-redirect=true";
}
public boolean getAccessibilityOn() {
return accessibilityOn;
}
}
2。一个 PhaseListener 用于在可访问性模式开启时更正视图的路径
PhaseListener 只检查可访问性模式,并在这种情况下重写视图在 ac 子目录中查找的路径。如果该视图存在,当前组件树将被丢弃并从同一视图的可访问版本重建。
这里的解决方案不是很好,因为 JSF 已经构建了一个组件树,而我只是将其丢弃以从另一个文件中重新构建它。
public class AccessibilityPhaseListener implements PhaseListener{
private static final long serialVersionUID = 1L;
@Override
public void beforePhase(PhaseEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
if(session==null) {
return;
}
Boolean acessibilityMode = (Boolean)session.getAttribute("AcessibilityMode");
if(acessibilityMode==null || !acessibilityMode)
return;
String viewId = context.getViewRoot().getViewId();
if(acessibilityMode) {
int index = viewId.lastIndexOf("/");
viewId = viewId.substring(0, index+1)+"ac/"+viewId.substring(index+1);
} else {
int index = viewId.lastIndexOf("/");
if(viewId.substring(index-3, index).equals("/ac"))
viewId = viewId.substring(0, index-3)+viewId.substring(index);
}
URL url = null;
try {
url = context.getExternalContext().getResource(viewId);
} catch (MalformedURLException e) {
}
if(url==null)
return;
ViewHandler handler = context.getApplication().getViewHandler();
UIViewRoot root = handler.createView(context, viewId);
root.setViewId(viewId);
context.setViewRoot(root);
}
@Override
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}
结论
我可以满足我所有的要求:
- 无需重构 Java 代码,应用程序现在可以在两种不同的模式下运行;
- 我只重建了我想要的视图,以防原始视图与屏幕阅读器一起正常工作,它保持原样;
- 可随时打开和关闭辅助功能模式,用户可以立即响应此类操作;
我不明白这个解决方案适用于任何类型的应用程序模式,而不仅仅是可访问性。任何时候有人需要根据应用程序或会话参数选择某个视图而不是另一个视图,它都会起作用。例如,文化定制比颜色和语言更进一步,需要完全重新设计视图的多文化应用程序可以利用此模型。
所有这一切的缺点是,当可访问性模式打开并且某个视图存在可访问版本时,JSF 将工作两次,一次构建原始视图,第二次构建可访问性版本。