【问题标题】:Play Framework 2 - Different views for different devicesPlay Framework 2 - 不同设备的不同视图
【发布时间】:2014-06-23 15:26:22
【问题描述】:

我正在构建一个需要具有 2 个不同版本的 Play 2.1.0 (Java) 应用程序:一个桌面网络应用程序和一个移动网络应用程序。我正在寻找一种不修改控制器逻辑但依赖于路由的方法。理想的行为应该是:

  • 桌面和移动设备的路由相同
  • 台式机和移动设备的控制器相同
  • 移动设备和桌面设备的视图不同,但共享命名约定。

我是否可以在某个地方连接路由行为,例如,将 .mob 附加到视图名称,以便呈现的视图对于桌面是 main.scala.html,对于移动设备是 main.scala.mob.html?这将是理想的,因为控制器不需要更改(或丑陋的 if),并且每个视图都需要有自己的移动版本。我想我此时需要请求来执行设备检测。如果没有为特定操作实现移动视图,它可以回退到桌面视图,那就更酷了。

有什么想法吗?

谢谢, 贡萨洛

【问题讨论】:

    标签: playframework-2.0


    【解决方案1】:

    我最终将需要移动版本的操作与另一个操作组合在一起。为了实现这一点,我创建了一个 @Mobile 运行时注解,我用移动视图的名称来注解这些动作。所有带注释的操作都将由以下MobileAction 组成,它执行设备检测:

    public class MobileAction extends Action<Mobile> {
        public MobileAction() {
        }
    
        public MobileAction(Mobile configuration, Action<?> delegate) {
            this.configuration = configuration;
            this.delegate = delegate;
        }
    
        @Override
        public Result call(Http.Context ctx) throws Throwable {
            final Http.Request request = ctx.request();
            final String userAgent = request.getHeader("User-Agent");
    
            // See https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent
            if (userAgent.contains("Mobi")) {
                ctx.args.put("viewName", configuration.value());
            }
    
            return delegate.call(ctx);
        }
    }
    

    我实现了一个DynamicRendered 类,它查找viewName 参数(可选由@Mobile 注入)并使用反射来呈现适当的视图。

    public class DynamicRenderer {
    
        public static Html render(String viewName, Object... args) {
            final Map<String, Object> ctxArgs = Http.Context.current().args;
            final String view = ctxArgs.containsKey("viewName") ? ((String) ctxArgs.get("viewName")) : viewName;
    
            // Get argument classes
            final Class[] argClasses = new Class[args.length];
            for (int i=0; i<args.length; i++) {
                argClasses[i] = args[i].getClass();
            }
    
            try {
                // Get view render method and invoke
                final Class<?> clazz = Class.forName(view);
                final Method render = clazz.getDeclaredMethod("render", argClasses);
                final Html html = ((Html) render.invoke(null, args));
    
                return html;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    然后,我没有在控制器中调用ok(viewName.render(...)),而是调用ok(DynamicRenderer.render("viewName", ...))

    【讨论】:

      【解决方案2】:

      听起来您需要使用 CSS + JS 样式库,例如 Foundation 5 或 Bootstrap。这些可以允许您通过在生成的 html 中使用 css 标签来指定视图应如何以不同的屏幕尺寸显示。 Lookup Foundation 文档,他们解释了如何去做。

      【讨论】:

      • 谢谢,但观点会完全不同。所以一个 CSS + JS 解决方案是不够的。
      • 由你决定......只是 FYI Foundation 确实有能力根据屏幕大小动态隐藏/显示 UI 的某些部分......如果你想将所有内容呈现为 HTML 然后显示只需要什么。如果您有 5 分钟的空闲时间,请看这里:foundation.zurb.com/docs/components/visibility.html
      • 我投了赞成票。今天我的任务是让我的视图在台式机、平板电脑和移动屏幕上看起来都不错。我在 Bootstrap 3.3.6 及其响应式实用程序的帮助下解决了这个问题。不知道为什么这个答案被某人否决,但实际上这个建议在 2014 年很好,并且在 2016 年 Boostrap 发展了它的响应式工具时甚至更好。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多