安卓用户体验新探索,“C手势”的解释以及实现方法

   Greg Nudelman是一位安卓系统方面的专家,他提出了用“C手势”导航代替现有安卓导航的构想,而且提供了两种设计方案并对关键代码做了详尽的解释。 “C手势”是作者提出的解决安卓导航碎片化的方案。强调的是“在需要的时候随时从屏幕任何区域调用上下文导航是适用于任何触屏设备的功能模型。Greg Nudelman认为“C手势”是最自然,最符合人机工程学的手势,可以很好地弥补大屏幕触屏设备导航不便的缺陷。

原文链接:http://mobile.smashingmagazine.com/2013/03/25/c-swipe-navigation-on-android/#top

目前市面上使用安卓系统的设备多达3997种。你的产品可能在多种设备上运行,因此你不得不考虑跨平台导航的问题。幸好“C手势”(C-Swipe)可以助你一臂之力:这是一种可用于平板电脑和移动设备的替代性导航方法(可以取代目前的导航方式),具有新颖(安卓设备汇中没有用到这种手势)、符合人机工程学(最大减小手指应变)和区域性(在屏幕局部随时可以调用)的特点。本文详尽介绍了这种导航方式的设计方法和代码,并且提供了一个可以下载的小应用,你可以亲测一下,试试“C手势”是否适于你的产品。

尺寸和复杂性不断升级
触屏设备的数量与日俱增,新设备、新功能日新月异。如果平时关注Windows OS的发展,我们不难发现这样一个趋势:触屏设备越来越大。目前,安卓设备大军中已经出现了12英寸、15英寸和21英寸触摸屏;触屏设备中的应用越来越复杂,功能日益齐全,甚至标准版的 Microsoft Office 应用都针对触摸操作做了优化。如果说最新的Chromebook(拥有一块12.85英寸,2560×1700像素的触摸显示屏)能显示出某些迹象的话,那一定是谷歌同样乐于将触摸操作集成到更大的硬件上。
这只是一个时间问题,只待Andriod系统迎头赶上。但是,通过缩放当前安卓4.x(4.0、4.1、4.2…)系统的操作栏(Action Bar)来适配所有设备和应用是不现实的也是不符合人机工程学的。
我建议用“C手势”作为替代操作栏的导航方式,这是基于手部自然人机工学提出的一种方法。使用“C手势”可以在屏幕任何位置调用上下文菜单(contextual menu),而你要做的只是用拇指在屏幕上很自然的画一个半圆弧。当你用右手操作的时候,这种手势看起来像个字母“C”——因此,我把它命名为“C手势”(C-Swipe。当你用左手操作时,可以称为“反C手势”,Reverse C)。
内容为王(Content Is King)
想象一下,把移动设备或平板电脑的屏幕全部填上内容,用拇指在屏幕的任何位置很自然的画一个半圆就能调用应用的功能和导航。用户可以在任何姿势、任何角度下完成这个动作,例如用两只手拿着设备时,把设备平方放在桌子上时,以及在床上阅读时等等。
拇指在屏幕上滑动会触发一个半圆形的上下文菜单。当菜单出现后,最常用的功能在顶部,也就是拇指自然状态下能触及的地方。Icon和文字放在拇指不会挡住的地方。用户可以点选他们的目标选项,点选或点击屏幕其他位置后,半圆形菜单就会消失。
看到容易,点击难
Flipboard是一款非常优秀的应用,以其优质的UI设计和内容导航功能赢得了大量忠实粉丝的青睐。然而,Flipboard详情页有一些选项——“返回”(Back),“最爱”(Favorite)和“喜欢”(Like)——被放在了顶部操作栏中。
(图1)
在Andriod 4中顶部操作栏是导航和功能选项的必争之地,而且Andriod 4设计规范也建议把导航和功能选项放在此处。顶栏位置有利于发现应用的功能:在顶栏,按钮可以很容易被看到,而且永远不会被用户的手遮住。
可是,把按钮放在屏幕的顶部实际上是一把双刃剑:对有些设备来说,顶栏是手指很难触及到的。即使在小型设备上也需要费一番功夫。如果设备再大一点(如很受欢迎的Galaxy Note),要想点击顶栏的按钮,你不得不用上另外一只手。这样一来,不管是多任务操作还是放松随意的使用都会变得艰难。
而且,操作栏占据了屏幕顶部一块非常重要的位置。当用户在学习使用这款应用的时候,可见的功能选项可以给他们提供有效的帮助。不幸的是,用户一旦学会了如何使用,顶栏就会变成“视觉噪声”,而且浪费了屏幕最引人注目、最宝贵的位置。
导航无处不在
如果有一种方法可以让整个屏幕100%只显示内容,同时又允许用户毫不费力的调用功能菜单(不管手指在什么位置,在屏幕什么区域),而且可以轻而易举的点击任何选项,这样的方法是不是很好?是的,这就是“C手势”要做的。
(图2)
用拇指在屏幕任何地方画一个半圆即可调用一个隐藏的菜单,菜单中的选项和顶部操作栏中的相同。菜单出现后,用同一个手指点击所需的选项也是一件很容易的事。
两种菜单设计
我介绍的“C手势”有两个形式不同但一样重要的方案:“滑动—松开”( swipe and release)和“仅滑动”(swipe only)。两个方案调用的菜单在形式上不太一样,你需要选一种适合自己的。“滑动—松开”方案把icon放在菜单选项内部,如上方图2所示。
使用这个方案的的用户可以先在屏幕表面画一个半圆,然后手指移开屏幕。在这里移开手指是很必要的,由于icon在菜单选项内部,不移开手指大部分选项会被遮住,那会造成使用上的不便。
第二种方案只需要滑动一下,不需要移开手指。如下方图3所示。系统识别手势的方法和上面的方案一样。但此时的菜单会随着手指的滑动即时出现在屏幕上(不是弹出)。由于拇指挡住了部分选项,icon需要展示在选项外部才能保证其可见性。
(图3)
你应该选择哪种交互方式?
“仅滑动”方案,拇指和设备时刻处于接触状态,能够即时触发菜单。这一点保证了导航效率:理论上没有“无用功”(no wasted motion)。但是很多被测用户更喜欢icon在菜单内部的设计方案。试用一下附件中的小应用,自己抉择吧。附件中的小应用是这两种方案的合体:它使用“滑动—松开”手势,但把icon放在了菜单选项外面。因此,你可以同时感受两种方案。
提示:谨记,我们提供的小应用调用菜单的滑动手势非常简单,你只要在屏幕上画一个很小的半圆就行,可能比你想象的小得多。我希望这个最简单不过的demo可以在任何设备上运行,不管屏幕大还是小,这种方案都能适用。(我们希望可以完美适用3997种屏幕)。画小半圆的原因是出于对人体解剖学的考虑:大手掌的人画小半圆远比小手掌的人画大半圆容易。
替换操作栏
基本上可以用“C手势”替换目前所有的Android导航栏菜单。也就是说使用导航栏的地方都可以换成“C手势”。正如我在及即将出版的一本书( Android Design Patterns: Interaction Solutions for Developers)中提到的一样,这种很不错的“C手势”,是熄灯模式(lights-out mode,屏幕显示内容为主,不显示导航控件。即隐藏式瑞士军刀风格的导航)即将引领潮流的先声。
另外一个重点是“C手势”几乎可以无限扩展:它容纳的选项比屏幕上能显示的选项多很多,可能比一级菜单、二级菜单、三级菜单的总和还要多。半圆形菜单的内环部分可以作为切换按钮,点击即可看到更多选项,如下面图4所示,点击中心按钮,在半圆菜单外圈显示更多选项,再次点击返回初始菜单界面。这样,用户就能很轻松地访问8-12个优先级最高的功能。

![](https://upload-images.jcodecraeer.com/upload-images-old/uploads/20130505/1271367732767.png "图片4")
你可以尝试分别为“滑动—松开”和“仅滑动”两种方案添加子菜单。

(图5)
上图第一排展示的是“滑动—松开”显示子菜单的方式。点击“Favorite”选项,在半圆菜单外圈出现一串星形按钮。用户把拇指移开即可看到子菜单上的功能选项,之后就可以按需点选了。
第二排展示了“滑动—按住”显示子菜单的方式,工作原理和上一中有些相似。拇指在屏幕上画“C”,保持手指接触屏幕,菜单出现后将手指滑动到目标选项上(例如“Favorite”),松开手指后子菜单就会代替原先的主菜单。
要点在于“C手势”有很多种版本,而且子菜单不需要也是半圆形,它可以是一串文本或icon也可以是一个特定的收藏夹。只要保证子菜单出现在“C手势”菜单附近即可。
为什么要用“C手势”?
“C手势”有很多优点:
1、提供沉浸式体验(Immersive Experiences)
这种模式具有高度沉浸式特性,所有的功能按钮都被隐藏起来,直到你需要的时候才会出现。它可以100%利用安卓设备的屏幕(包含全部3997种),为购物、阅读、看视频、虚拟现实等提供沉浸式体验。
2、最大限度减小手臂应变(Arm Strain)
“C手势”尤其适合大尺寸触屏设备,如即将上市的15-、17-、21-英寸平板电脑。在目前Andriod 4系统导航模式中,点击顶部或底部操作栏需要较大幅度手臂动作,很快就会造成手臂疲劳。相反,“C手势”可以让菜单出现在手指能触及的区域附近,不需要手臂大幅度运动就能完成操作。
3、符合费茨定律(Fitts’Law)
费茨定律指出点击按钮的速度和难易程度与目标的大小和到目标的距离有关。也就是说,在大屏平板电脑上点击一个小按钮必定是一件非常痛苦的事。相比之下“C手势”具有独到之处:它可以在屏幕任何位置调用导航,而且菜单出现的位置永远在手指放置的位置附近。如此一来定位选项的时间就减少了,点击的动作会更加精确和自然。
4、触发动作唯一性
目前看来“C手势”还没用在其他功能上(作者发表文章时,2013.3.25)。因此,使用“C手势”并不会导致使用上的冲突,而且点击菜单以外的任何位置(取消菜单的动作)同样很简单。
强调一下,当用左手操作时,触发动作为“反C手势”。下面提供的源代码和小应用都支持左右手完成操作。
其他建议
“C手势”不一定在屏幕边缘才能被触发。当用户站着使用大屏幕设备时,他可能需要将屏幕倾斜找到一个舒服的姿势。这时,用户可以用手指在屏幕任何位置画个半圆调用“C手势”导航。通常情况下,用户可以用任何一只手指,如食指,来完成这个动作。
“C手势”是很独特的,因为菜单出现的时候伴随着“天然的”动画过渡效果。随着拇指划过屏幕,半圆形菜单也紧接着出现在拇指划过的区域,非常自然。
警告
“C手势”隐藏的很深,并不容易被发现。不过这个缺点可以通过添加水印动画弥补,或者用其他优雅的方式作为引导。给用户一些提示如:“用手指在屏幕任意位置画个”C”。”或者用水印动画在屏幕的不同位置提示几次,就能帮助用户发现这一手势的奥秘。


当用户发现这种手势之后,水印动画就可以永久消失了。“C手势”非常易学,因为我们对这种手势非常熟悉,而且使用时手臂和手指都很放松。
有些人认为“C手势”的缺陷在于它只是人机工程学上符合用户要求,没有映射现实世界中真实的事物(如卷轴或铁锅)。很多设计师喜欢Windows现代 风格的UI设计,其中用到的都是“大手势”(larger gestures)。他们很喜欢那种整条手臂从左滑到右从上滑到下的感觉。还有一些设计师喜欢用替代“大手势”的方法:一种特殊的多点触控手势——如五指轻点,或五指一起捏。
我是不同意这种观点的
尽管人们提出了很多导航方法,而且每个都承诺很好用。但我仍然觉得“C手势”是最自然、最真实、最经济的触摸动作。它是很自然的动作,就像用户在轻挠或抓取屏幕上的内容一样。
不管设备处于什么样的姿态,在什么样的任务场景中,这个滑动动作可以轻易的用一只手完成。为了验证这一观点的正确性,我们还需要扩大被测用户的范围做更多测试。
但是有一点可以明确:不管手势如何变化,在需要的时候从屏幕局部随时调用上下文导航是适用于任何触摸屏设备的功能模型——所以我最大的建议莫过于一定不要忘了这个重要的趋势。

源代码解释

可以想象到这一功能确实复杂,下面的代码只是一个粗略的预览,看的时候关注重点就好。源码下载在这里 source code (ZIP).

你需要先用Android SDK中的GestureBuilder记录自定义“C手势”。详细步骤可以查看Micha Kops的文章:“Creating a Simple Gesture App With Android” 然后,在 CSwipeActivity 类(class)里,从库(library)中载入自定义“C手势”:

// Load gestures library
        mGestureLib = GestureLibraries.fromRawResource(this, R.raw.gestures_cswipe);
        if (!mGestureLib.load()) {
            // Show toast if gestures library could not be loaded
            Toast.makeText(this, R.string.KMsgErrLoadingGestures, Toast.LENGTH_SHORT).show();
        }

接下来,建立一个监听器(listener)来监视目标手势(识别“C手势”):

// Register gestures listener
        mGestureOverlayView.addOnGesturePerformedListener(new GestureOverlayView.OnGesturePerformedListener() {
            @Override
            public void onGesturePerformed(GestureOverlayView gestureOverlayView, Gesture gesture) {
                onGesture(gestureOverlayView, gesture);
            }
        });

执行任何手势时都可以用onGesture()判断一下该手势是否满足“C手势”条件。你可以通过对比预测值是否高于某些预定值来判断用户的手势是否满足要求。在我们提供的demo中,我们把预定值设置成一个中间值(middle-of-the-road value)—3D,这个值是试验得出来的,如果数值过低,简单的滑动都有可能意外触发这个功能,如果门槛太高,这个手势就很难被触发。当检测到正确手势之后,应用就会调用特殊的半圆菜单,同时填充预置的选项:

if (prediction.score > 3D) {
                // Switch content from gesture overlay view to original content view
                mGestureOverlayView.removeView(mContentView);
                setContentView(mContentView);
// Inflate the CSwipe control view
                final View cSwipePopupContentView = getLayoutInflater().inflate(R.layout.view_cswipe, null);
                mCSwipe = (CSwipe) cSwipePopupContentView.findViewById(R.id.cswipe);

我们提供的小应用demo,支持两种方向的“C手势”:向左和向右。可以用下面的代码判断,然后设置菜单锚点的方向:

// Check the orientation of the CSwipe control based on the selected gesture prediction
                final String predictionName = prediction.name;
                CSwipe.Anchor cSwipeAnchor = CSwipe.Anchor.RIGHT;
                if (predictionName.equals(GESTURE_CSWIPE_LEFT_MARGIN)) { cSwipeAnchor = CSwipe.Anchor.LEFT; }
                else if (predictionName.equals(GESTURE_CSWIPE_RIGHT_MARGIN)) { cSwipeAnchor = CSwipe.Anchor.RIGHT; }
                // Set the CSwipe control anchor according to the selected gesture prediction
                mCSwipe.setAnchor(cSwipeAnchor);

让菜单尽可能出现在手势锚点附近,从而呈现出一种菜单伴随手势平滑出现的视觉效果:

// Show the CSwipe control popup window as close as possible to the gesture bounding rectangle
                final RectF gestureBoundingRect = gesture.getBoundingBox();
                mCSwipePopupWindow.showAtLocation(mContentView, Gravity.NO_GRAVITY, (int) gestureBoundingRect.left, (int) gestureBoundingRect.top);

用CSwipe类来启动这个由外圆和内圆定义的特殊半圆菜单。你可以在Widget folder中找到完整的CSwipe.java代码:

public CSwipe(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CSwipe);
        mInnerArcRadius = attributes.getDimensionPixelSize(R.styleable.CSwipe_innerArcRadius,
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_INNER_RADIUS_DP, getResources().getDisplayMetrics()));
        mOuterArcRadius = attributes.getDimensionPixelSize(R.styleable.CSwipe_outerArcRadius,
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_OUTER_RADIUS_DP, getResources().getDisplayMetrics()));

总结:
你可以继续润色代码以提供更多增强功能:提高反应速度;创建平滑的转场效果;调试判断手势的初始值;甚至在学习中随着时间的推移重新编写手势检测的算法,从而最大程度上匹配各种设备型号和用户手掌尺寸。现在,是时候感受一下我们提供的demo了,我想它应该可以体现出“C手势”导航的潜力。

附:demo下载地址:(安装方法同普通安卓APP)

Download the demo mini-app and source code (ZIP).