草庐IT

vivo官网APP全机型UI适配方案

Xu Jie 2023-03-28 原文
作者 | vivo 互联网客户端团队- Xu Jie 

日益新增的机型,给开发人员带来了很多的适配工作。代码能不能统一、apk能不能统一、物料如何选取、样式怎么展示等等都是困扰开发人员的问题,本方案就是介绍不同机型的共线方案,打消开发人员的疑虑。

一、日益纷繁的机型带来的挑战

1.1  背景

科技是进步的,人们对美的要求也是逐渐提升的,所以才有了现在市面上形形色色的机型

(1)比如vivo X60手机采用纤薄曲面屏设计,属于直板机型。

(2)比如vivo 折叠屏高端手机,提供更优质的视觉体验,属于折叠屏机型。

(3)比如vivo pad,拥有优秀的操作手感和高级的质感,属于平板机型。

1.2  我们的挑战

在此之前,我们主要是为直板手机去服务,我们的开发只要适配这种主流的直板机器,我们的UI主要去设计这种直板手机的效果图,我们的产品和运营主要为这种直板机型去选择物料。

可是随着这种形形色色机型的出现,那么问题就来了:

  • 开发人员的适配成本高了,是不是针对每一种机型,都要做个单独的应用进行适配呢?
  • UI设计师要做的效果图要多了,是不是要针对每种机型都要设计一套效果图呢?
  • 产品和运营需要选择的物料更受限制了,会不会这个物料在一个机器上正常。在其他机器上就不正常了呢?
为什么这么说,下面以开发者的角度来做介绍,把我们面临的问题,做说明。

二、 开发者的窘境

2.1 全机型适配成本太高

日渐丰富的机型适配让我们这些android开发人员疲于奔命,虽然可以按照要求进行适配,但是大屏幕的机型适配成本依然比较高,因为这些机型不同于传统的直板手机的宽高比例(9:16)。所以有的应用干脆就直接两边留白,内容区域展示在屏幕正中央,这种效果,当然很差。

案例1:某个视频APP页面,未做pad上的适配,打开之后的效果如下,两边大量留白,是不可操作的区域。

案例2:某新闻资讯类APP,在pad上的适配效果如下,可见的范围内,信息流展示内容较少,图片有拉伸、模糊的问题。

2.2 全机型适配成本高在哪

上面的案例其实只是表面的问题之一,作为开发人员,需要考虑的因素有很多,首先要想到这些机型有什么特点:

然后才是需要解决的问题:

三、寻找全机型适配方案之旅

3.1 方案讨论与确定

页面拉伸、左右留白是现象,这也是用户的直接体验。那么这就是我们要改善的地方,所以现在就有方向了,围绕着 “如何在可见区域内,展示更多的信息” 。这不是布局的简单重新排列组合,因为  方案绝对不是只有开发决定如何实现就可以怎么实现的,一个apk承载着功能到用户手里涉及了多方角色的介入。产品经理需要整理需求、运营人员需要配置物料、发布apk,测试需要测试等等,所以最终的方案不是一方定下来的,而是一个协调统一后的结果。

既然要去讨论方案,那么就要有依据,在此省略讨论、评审、定稿的过程。

先来看看直板、折叠屏、pad的外部轮廓图,知道页面形态如何。

3.2 方案落地示意图

每个应用要展示的内容不一致,但是原理一致,此处就以下面几个样式为基础介绍原理。原则也比较简单,尽可能展示更多内容,不要出现大面积的空白区域。

下面没有介绍分栏模式的适配,因为分栏的模式也可能被用户关闭,最终成为全屏模式,所以说,可以选择只适配全屏模式,这样的适配成本较低。当然,这个也要根据自己模块的情况来确定,比如微信,更适合左右屏的分栏模式。

3.2.1 直板机型适配方案骨骼图

直板机型,目前主流的机型,宽高比基本是9:16,可以最大限度地展示比较多的内容,比如下图中的模块1、模块2、 模块3的图片。

3.2.2 折叠屏机型适配方案骨骼图

折叠屏机型,屏幕可旋转,但是宽高比基本是1:1,高度和直板机器基本差不多,可以达到2000px的像素,所以在纵向上,也可以最大限度地展示比较多的内容,比如下图中的模块1、模块2、 模块3的图片。

3.2.3 PAD机型适配方案骨骼图

pad平板,屏幕可旋转,并且旋转后的宽高比差异较大,纵向时,宽高比是5 : 8,横向时,宽高比是8 : 5。

在pad纵向时,其实高度像素是足够展示很多内容的,比如下图中的模块1、模块2、 模块3的图片;

但是在pad横向时,没办法展示更多的内容(倒是有个方案,最后再说),只能下图中的模块1、模块2的图片。

3.3 方案落地规范

3.3.1 一套代码适配所有机型

确定一个apk能不能适配所有机型,首先要解决的是要符合不同机型的特性,比如直板手机只能纵向显示,折叠屏和pad支持横竖屏旋转。

描述如下:

(1)需求

  • 直板屏:强制固定竖屏;
  • 折叠屏:外屏固定竖屏、内屏(大屏)支持横竖屏切换;
  • PAD端:支持横竖屏切换;
我们需要在以上三端通过一套代码实现上面的需求。

(2)横竖屏切换

有以下2种方法:

方式1)

通过在AndroidManifest.xml中设置:

android:screenOrientation属性

a) android:screenOrientation="portrait" 

强制竖屏;

b) android:screenOrientation="landscape" 

强制横屏;

c) android:screenOrientation="unspecified" 

默认值,可以横竖屏切换;

方式2)

在代码中设置:

activity.setRequestedOrientation(****);

a) setRequestedOrientation

(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);    设置竖屏;

b)setRequestedOrientation

(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 设置横屏;

c)setRequestedOrientation

(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 可以横竖屏切换;

(3)不同设备支持不同的屏幕横竖屏方式

1)直板屏:

因为是强制竖屏,所以,可以通过在AndroidManifest.xml中给Activity设置android:screenOrientation="portrait"。

2)折叠屏:

外屏与直板屏是保持一致的,暂且不讨论。但是内屏(大屏)要支持横竖屏切换。如果是一套代码,显然是无法通过AndroidManifest文件来实现的。这里其实系统框架已经帮我们实现了对应内屏时横竖屏的逻辑。总结就是,折叠屏可以与直板屏保持一致,在AndroidManifest.xml中给Activity设置android:screenOrientation="portrait",如果切换到内屏时,系统自动忽略掉screenOrientation属性值,强行支持横竖屏切换。

3)PAD端:

当然了,并不是所有的项目对应的系统都会自动帮我们忽略screenOrientation属性值,这时候就需要我们自己来实现了。

我们通过在Activity的基类中设置setRequestedOrientation

(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED),发现确实能够使当前页面横竖屏自由切换了。但是在启动activity的时候遇到了问题。当我们从横屏状态A界面启动一个acitivity的B界面时,发现B界面先是竖屏,然后切换到了横屏(如图1所示)。再试了多次依旧如此,肉眼可见的切换过程显然不能满足我们的需求。这说明通过java代码动态调整横竖屏的技术方向是行不通的。综上所述,通过同一份代码无法满足PAD端和直板屏的互斥的需求。

那还有没有其他方式呢。别忘了,我们Android打包全流程是通过gradle完成的,我们是不是可以通过切面编程的思维,针对不同的设备打出不同的包。

方案确定了,在此进行技术验证。

gradle编译其中一个重要环节就是对依赖的aar、本地module中的AndroidManifest文件进行merge,最终输出一份临时的完整清单文件,存放在*/app/build/intermediates/merged_manifest/**Release/路径下。

因此,我们可以在AndroidManifest文件merge完成之后对该临时文件中的android:screenOrientation字段值信息进行动态修改,修改完成之后再存回去。这样针对pad端就可以单独打出一份apk文件。

核心代码如下:

//pad支持横竖屏
def processManifestTask = project.tasks.getByName("processDefaultNewSignPadReleaseManifest");
if (processManifestTask != null) {
processManifestTask.doLast { pmt ->
def manifestPath = pmt.getMultiApkManifestOutputDirectory().get().toString() + "/AndroidManifest.xml"
if (new File(manifestPath).exists()) {
String manifest = file(manifestPath).getText()
manifest = manifest.replaceAll("android:screenOrientation=\"portrait\"", "android:screenOrientation=\"unspecified\"");
file(manifestPath).write(manifest)
println(" =============================================================== manifestPath: " + manifestPath)
}
}
}
(4)apk的数量

到这里为止,java代码是完全一致,没有区分的,关键就在于框架有没有提供出忽略screenOrientation的能力,如果提供了,我们只需要输出一个apk,就能适配所有机型,

如果没有这个能力,我们就需要使用gradle打出额外的一个apk,满足可旋转的要求。

3.3.2 一套物料配所有机型

1、等比放大物料

通过上面的落地方案的要求,对于模块2的图片,展示效果是不一样的,如下图:

  • 直板手机上面,模块2的图片1在上面,图片2、3分布于左下角和右下角
  • 折叠屏或者pad上面,模块2的图片1在左边,图片2、3分布于右侧
  • 折叠屏和pad上的模块2的图片,相对于直板手机来说,做了样式的调整,上下的样式改为了左右。图片也做了对应的放大,保证横向上可以填充整个屏幕的宽度。

  • 为了形象地表示处理后的效果,看下下面的示意图即可。

2、高度不变,裁剪物料

对于模块3的图片,可以回顾3.2中的展示样式,要求是

  • 直板手机上面,模块3中图片1的高度此处为300px。
  • 折叠屏或者pad上面,模块3的图片1的高度也是300px,但是内容不能减少。
  • 解决方案就是提供一张原始大图,假如规格为2400px*300px,在直板手机上左右进行裁剪,如下图所示。折叠屏和pad上面直接进行展示。而裁剪这一步,放在服务端进行,因为客户端做裁剪,比较耗时。

  • 为了形象地表示处理后的效果,看下下面的示意图即可。

3.3.4 无感刷新

无感刷新,主要是体现在折叠屏的内外屏切换,pad的横竖屏旋转这些场景,如何保证页面不会出现切换、旋转时候的闪现呢?

  • 这就要提前准备好数据源,保证在页面变化时,立即notify。
  • 我们的页面列表最好使用recyclerview,因为recyclerview支持局部刷新。
  • 数据源驱动UI,千万不要在UI层面判断机型做UI的动态计算,页面会闪屏,体验不好。

3.4 方案落地实战

上面介绍了不同机型的适配规范,这个没有疑问之后,直接通过案例来看下具体如何实施。

如上图所示,选购页可以大致分为 分类导航栏区域 和 内容区域,其中内容区域是由多个楼层组成。

3.4.1 UI如何设计的

如图所示,能够直观地感受到,从直板手机到折叠屏内屏再到Pad横屏,当设备的可显示面积增大时,页面充分利用空间展示更多的商品信息。

3.4.2 不同设备的区分方式

通过前面的简单介绍,对选购页的整体布局及不同设备上的UI展示有所了解,下面来看下如何在多个设备上实现一套代码的适配。

首先第一步,要如何区分不同的设备。

在区分不同的设备前,先看下能够从设备中获得哪些信息?

  • 分辨率
  • 机型
  • 当前屏幕的横、竖状态
先说结论:

  • 直板手机:通过分辨率来区分
  • 折叠屏:通过机型和内外屏状态来区分
  • Pad:通过机型和当前屏幕的横、竖状态来区分
所以这里根据这几个特点,提供一个工具。

不同设备的区分方式。

/**
* @function 判断当前手机的屏幕是处于哪个屏幕类型:目前三个屏幕范围:分别为 <= 528dp、528 ~ 696dp、> 696dp,对应的分别是正常直板手机、折叠屏手机内屏和Pad竖屏、和Pad横屏
*/
public class ScreenTypeUtil {
public static final int NORMAL_SCREEN_MAX_WIDTH_RESOLUTION = 1584; // 正常直板手机:屏幕最大宽度分辨率;Pad的分辨率(1600*2560), 1584 = 528 * 3, 528dp是UI在精选页标注的直板手机范围
public static final int MIDDLE_SCREEN_MAX_WIDTH_RESOLUTION = 2088; // 折叠屏手机:屏幕最大宽度分辨率(1916*1964, 旋转:1808*2072),2088 = 696 * 3, 2088dp是UI在精选页标注的折叠屏展开范围
public static final int LARGE_SCREEN_MAX_WIDTH_RESOLUTION = 2560; // 大屏幕设备:屏幕宽度暂定为 Pad的高度
public static final int NORMAL_SCREEN = 0; // 正常直版手机屏幕
public static final int MIDDLE_SCREEN = 1; // 折叠屏手机内屏展开、Pad竖屏
public static final int LARGE_SCREEN = 2; // Pad横屏
public static int getScreenType() {
Configuration configuration = BaseApplication.getApplication().getResources().getConfiguration();
return getScreenType(configuration);
}
// 注意这里的newConfig 在Activity、Fragment、View 中的onConfigurationChanged中获得的newConfig传入,如果获得不了该值,可以使用getScreenType()方法
public static int getScreenType(@NonNull Configuration newConfig) {
// Pad 通过机型标志位及当前处于横竖屏状态 来判断当前屏幕类型
if (SystemInfoUtils.isPadDevice()) {
return newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? LARGE_SCREEN : MIDDLE_SCREEN;
}
// Fold折叠屏 通过机型标志及内外屏状态 来判断当前屏幕类型
if (SystemInfoUtils.isFoldableDevice()) {
return SystemInfoUtils.isInnerScreen(newConfig) ? MIDDLE_SCREEN : NORMAL_SCREEN;
}
// 普通手机 通过分辨率判断
return AppInfoUtils.getScreenWidth() <= NORMAL_SCREEN_MAX_WIDTH_RESOLUTION ? NORMAL_SCREEN : (AppInfoUtils.getScreenWidth() <= MIDDLE_SCREEN_MAX_WIDTH_RESOLUTION ? MIDDLE_SCREEN : LARGE_SCREEN);
}
}

3.4.3 实现方案

(1)数据源驱动UI改变的思想

  • 对于直板手机来说,选购页只有一种状态,保持竖屏展示
  • 对于折叠屏来说,折叠屏可以由内屏切换到外屏,也就涉及到了两种不同状态的切换。
  • 对于Pad来说,Pad支持横竖屏切换,所以也是两种不同状态切换。
当屏幕类型、横竖屏切换、内外屏切换时,Activity\Fragment\View 会调用onConfigurationChanged方法,因此针对直板手机、折叠屏及Pad可以将数据源的切换放在此处。

无论是哪种设备,最多是只有两种不同的状态,因此,数据源这里可以准备两套:一种是Normal、一种是Width,对直板手机而言:因为只有一种竖屏状态,因此只需要一套数据源即可;对折叠屏而言:Normal存放的是折叠屏外屏数据源,Width存放的是折叠屏内屏数据源;对Pad而言:Normal存放的是Pad竖屏状态数据源,Width存放的是Pad横屏状态数据源。

(2)内容区域

右侧的内容区域是一个Fragment,在这个Fragment里面包含了一个RecyclerView。

每个子楼层

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_classify_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<xxx.widget.HeaderAndFooterRecyclerView
android:id="@+id/shop_product_multi_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
每个楼层也是一个单独的RecyclerView,以楼层4为例,楼层4的每一行商品都是一个RecyclerView,每个RecyclerView使用GridLayoutManager来控制布局的展现列数。

(3)数据源

以折叠屏为例:针对每个子楼层的数据,在解析时,就先准备两套数据源:一种是Normal、一种是Width。

在请求网络数据回来后,在解析数据完成后,存放两套数据源。这两套数据源要根据UI设计的规则来组装,例如以折叠屏的楼层4为例:

  • 折叠屏-外屏-楼层4:一行展示2个商品信息。
  • 折叠屏-内屏-楼层4:一行展示3个商品信息。
注意:这里的2、3数字是UI设计之初就定下来的,每行商品都是一个RecyclerView,并且使用GridLayoutManager来控制其列数,因此这个2、3也是传入到GridLayoutManager的列数值,这里要保持一致。

子楼层的数据源解析

//这里的normalProductMultiClassifyUiBeanList集合中存放了2个商品信息
for (ProductMultiClassifyUiBean productMultiClassifyUiBean : normalProductMultiClassifyUiBeanList) {
productMultiClassifyUiBean.setFirstFloor(isFirstFloor);
shopListDataWrapper.addNormalBaseUiBeans(productMultiClassifyUiBean);
}
//这里的normalProductMultiClassifyUiBeanList集合中存放了3个商品信息
for (ProductMultiClassifyUiBean productMultiClassifyUiBean : widthProductMultiClassifyUiBeanList) {
productMultiClassifyUiBean.setFirstFloor(isFirstFloor);
shopListDataWrapper.addWidthBaseUiBeans(productMultiClassifyUiBean);
}
因此,到这里就已经获取了所需的数据源部分

(4)屏幕类型切换

还是以折叠屏为例,折叠屏外屏切换到内屏,此时Fragment会走onConfigurationChanged方法。

屏幕类型切换-数据源切换-更新RecyclerView。

public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//1、 首先进行内容区域中的RecyclerViewAdapter、数据源判空
if (mRecyclerViewAdapter == null || mPageBeanAll == null) {
return;
}
//2、判断当前的屏幕类型,注意:这个地方是调用3提供的方法:ScreenTypeUtil.getScreenType(newConfig)
// 直板手机、折叠屏外屏
if (ScreenTypeUtil.NORMAL_SCREEN == ScreenTypeUtil.getScreenType(newConfig)) {
mPageBeanAll.setBaseUiBeans(mPageBeanAll.getNormalBaseUiBeans());
} else if (ScreenTypeUtil.MIDDLE_SCREEN == ScreenTypeUtil.getScreenType(newConfig)) {
if (SystemInfoUtils.isPadDevice()) {
// Pad的竖屏
mPageBeanAll.setBaseUiBeans(mPageBeanAll.getNormalBaseUiBeans());
} else {
// 折叠屏的内屏
mPageBeanAll.setBaseUiBeans(mPageBeanAll.getWidthBaseUiBeans());
}
} else {
// Pad的横屏、大分辨率屏幕
mPageBeanAll.setBaseUiBeans(mPageBeanAll.getWidthBaseUiBeans());
}
//获取当前屏幕类型的最新数据源
mRecyclerViewAdapter.setDataSource(mPageBeanAll.getBaseUiBeans());
//数据源驱动楼层UI改变
mRecyclerViewAdapter.notifyDataSetChanged();
}
通过onConfigurationChanged方法,能够看到数据源是如何根据不同屏幕类型进行切换的,当数据源切换后,会通过notifyDataSetChanged方法来改变UI。

四、至简之路的铸就

大道至简,遵循规范和原则,就可以想到如何对多机型进行适配,别陷入细节。

以这个作为指导思想,可以做很多其他的适配。下面做些列举,但不讲解实现方式了。

1、文字显示区域放大

如下图所示,标题的长度,在整个容器显示宽度变宽的同时,也跟着一起变化,保证内容的长度可以自适应的变化。

2、弹框样式的兼容

如下图所示,蓝色区域是键盘的高度,在屏幕进行旋转的时候,键盘的高度也是变化的,此时可能会出现遮挡住原本展示的内容,此处的处理方式是:让内容区域可以上下滑动。

3、摄像头位置的处理

如下图所示,在屏幕旋转之后,摄像头可以出现在右下角,此时如果不对页面进行设置,那么就可能出现内容区域无法占据整个屏幕区域的问题,体验比较差,此处的处理方式是:设置页面沉浸式,摄像头可以合理地覆盖一部分内容。

五、我们摆脱困扰了吗

5.1 解决原先的问题

通过前面的介绍,我们知道了,vivo官网的团队针对折叠屏和pad这种大屏,采取了全屏展示的方案,一开始的时候,我们遇到的问题也得到了解决:

(1)开发人员的适配成本高了,是不是针对每一种机型,都要做个单独的应用进行适配呢?

Answer:按照全屏模式的设计方案,折叠屏和pad也就是一种大尺寸的机器,开发人员判断机型的分辨率和尺寸,选择一种对应的布局展示就好了,只用一个应用就能搞定。

(2)UI设计师要做的效果图要多了,是不是要针对每种机型都要设计一套效果图呢?

Answer:制定一套规范,大于某个尺寸时,展示其他样式,所有信息内容都按照这种规范来,不会出现设计混乱的情况。

(3)产品和运营需要选择的物料更受限制了,会不会这个物料在一个机器上正常。在其他机器上就不正常了呢?

Answer:以不变应万变,使用一套物料,适配不同的机型已经可以落地了,不用再担心在不同的机器上展示不统一的问题。

5.2 我们还可以做什么

5.2.1 我们的优点

折叠屏和pad两款机器,已经在市面上使用较长时间,各家厂商也纷纷采取了不同的适配方案来提升交互体验,但是往往存在下面几个问题:

1、针对不同机型,采用了不同的安装包。

这种方案,其实会增加维护成本,后期的开发要基于多个安装包去开发,更加耗时。

2、适配了不同的机型,但是在一些场景下的样式不理想。

比如有些APP做了分栏的适配,但是没有做全屏的适配,效果就比较差,这里可能也是考虑到了投入产出比。

3、目前的适配指导文档对于开发人员来说指导性较弱。

各种适配指导文档,还是比较偏向于官方,对于开发人员来说,还是无法提前识别问题,遇到问题还是要实际去解决。

基于此,我们的优点如下:

1、我们只有一个安装包。

我们是一个安装包适配所有机型,每种机型的APP展示的样式虽然不同,对于开发者来说,就是增加了一个样式,思路比较清晰。

2、全场景适配。

不同机型的纵向、横竖屏切换,都做到了完美适配,一套物料适配所有机型也是我们的一个特色。

3、有针对性地提供适配方案。

本方案是基于实际开发遇到的问题,进行的梳理,可以帮忙开发人员解决实际可能遇到的问题,具备更好的参考性。

5.2.2 我们还有什么要改进

回首方案,我们这里做到的是使用全屏模式去适配不同机型,更多的适用于像京东、淘宝、商城等电商类APP上,实际上,现在有些非APP会采用分栏的形式做适配,这也是一种跟用户交互的方式,本方案没有提到分栏,后续分栏落地后,对这部分会再进行补充。

有关vivo官网APP全机型UI适配方案的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  3. ruby-on-rails - 每次我尝试部署时,我都会得到 - (gcloud.preview.app.deploy) 错误响应 : [4] DEADLINE_EXCEEDED - 2

    我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie

  4. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  5. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  6. ruby - 使用 postgres.app 在 rvm 下要求 pg 时出错 - 2

    我正在使用Postgres.app在OSX(10.8.3)上。我已经修改了我的PATH,以便应用程序的bin文件夹位于所有其他文件夹之前。Rammy:~phrogz$whichpg_config/Applications/Postgres.app/Contents/MacOS/bin/pg_config我已经安装了rvm并且可以毫无错误地安装pggem,但是当我需要它时我得到一个错误:Rammy:~phrogz$gem-v1.8.25Rammy:~phrogz$geminstallpgFetching:pg-0.15.1.gem(100%)Buildingnativeextension

  7. Ruby 守护进程和 JRuby - 备选方案 - 2

    我有一个应用程序正在从Ruby迁移到JRuby(由于需要通过Java提供更好的Web服务安全支持)。我使用的gem之一是daemons创建后台作业。问题在于它使用fork+exec来创建后台进程,但这对JRuby来说是禁忌。那么-是否有用于创建后台作业的替代gem/wrapper?我目前的想法是只从shell脚本调用rake并让rake任务永远运行......提前致谢,克里斯。更新我们目前正在使用几个与Java线程相关的包装器,即https://github.com/jmettraux/rufus-scheduler和https://github.com/philostler/acts

  8. ruby - 为 capybara 设置 app_host 的内容 - 2

    我的测试尝试访问网页并验证页面上是否存在某些元素。例如,它访问http://foo.com/homepage.html并检查Logo图像,然后访问http://bar.com/store/blah.html并检查页面上是否出现了某些文本。我的目标是访问经过Kerberos身份验证的网页。我发现Kerberos代码如下:主文件uri=URI.parse(Capybara.app_host)kerberos=Kerberos.new(uri.host)@kerberos_token=kerberos.encoded_tokenkerberos.rb文件classKerberosdefini

  9. ruby-on-rails - 如何使用 grape swagger ui 传递数组? - 2

    我在下面定义了api端点:paramsdorequires:ids,type:Array,desc:'Arrayofgroupids'end我无法从Swagger生成的UI传递数组。如果我输入[1,2,3,4]或ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3然后两者都无效.如果我使用数组调用spec中的api,它就可以工作。我的客户想尝试Swagger的整个api,所以我想要一个适用于SwaggerUI的解决方案。 最佳答案 我对所有情况的解决方案:paramsdorequires:ids,type:Arra

  10. ruby - 导轨 3 : Creating app with internal plugin system - 2

    我想在Rails中使用插件系统创建一个应用程序。潜在用户应该能够上传(或更好地从存储库安装)一个插件并安装它,使我的应用程序能够做更多的事情。这应该在没有FTP/SSH/对服务器的任何低级别访问的情况下完成。关于如何在Rails3中完成它,是否有任何好的gems或教程? 最佳答案 你看过http://edgeguides.rubyonrails.org/plugins.html了吗??它似乎不是100%兼容Rails3,但它可以帮助您入门。我看过的大多数插件文章都涉及Rails2。 关于

随机推荐