前言:
- 本文翻译于 sonymobile 的一系列教程,指导 Android 开发人员如何用一个手指控制图片的缩放,接第二篇。这一篇在上一篇的基础上,修改了输入模式的切换方式,原来是通过菜单选项,选择zoom或者pan,然后进行操作,这样使用起来很不方便,在这一篇,将会通过长按的方式进入缩放模式,普通触摸滑动就是平放模式。进入缩放模式会给一个震动反馈。
原文:
Welcome to the third part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click [here] to read the second part of the tutorial.
译文:
欢迎来到如何制作你自己的缩放控制课程的第三部分,就像Sony Ericsson X10 Mini 的相机和相册应用中使用的一样。点击这里阅读第这个课程的第二部分。
原文:
Don’t forget to go to Android Market and download Sony Ericsson Tutorials, the app that collects all sample apps in this and other Sony Ericsson tutorials. Get the app [here]. Below is a link to the source code of part 3, prepared for you to set up your own project in e.g. Eclipse.
译文:
不要忘了去安卓市场下载Sony Ericsson Tutorials,这个app收集了这个课程和其他Sony Ericsson课程的所有示例应用。这儿下载app(译注:找不到下载地址了,可以运行代码看效果)。下面是第三部分代码的连接,为你建立自己的Eclipse工程准备的。(译注:链接已经失效了,我会在文章的结尾给出地址。)
原文:
This tutorial part will focus on introducing a new way of interacting with the zoom, a new input paradigm as our designers would say. In the previous tutorial we laid the ground for exactly this when we created a new class for controlling the zoom state. Separating the state control from the input control handled by an OnTouchListener implementation.
译文:
这部分课程将会重点介绍一个新的缩放交互方式,和我们的设计人员所说的一样,一个新的输入模式,在前面的课程我们给缩放状态控制创建了一个新类,为这个打下了基础。从输入控制中分离出状态控制,通过一个OnTouchListener 实现来处理。
原文:
Besides being nice for easier code overview this separation is great for us in at least two other ways:
译文:
除了更容易地浏览代码,这个分离对我们来说至少有其他两方面的好处。
原文
First it it is very useful for trying out different way of controlling the zoom, something I had great use for in the X10 mini project. Here at Sony Ericsson we have the privilege of working with several great and driven designers wanting to test out every idea for controlling the zoom you can think of. Being able to easily switch between variants meant we could evaluate several good designs before settling.
译文:
首先对于尝试不同的缩放控制方式很有帮助,这在X10 mini项目中有很大作用。在Sony Ericsson我们有好处是和一些优秀的主导设计师一起工作,他们想试验每一个你能想到的缩放控制的想法。能够在多种不同方式中轻松地切换,在确定之前我们能够评估多个好的设计。
原文:
Secondly it means we can easily have different ways of controlling the zoom on different devices. Multiple screen support is something you should always think of when designing for android. For small devices like the X10 mini one handed touch is very useful, while larger screens will be operated with two hands most of the time, one hand holding the phone and another free to touch the screen.
译文:
其次这意味着我们能够容易地在不同设备上使用不同的缩放控制方式。为android做设计的时候,支持多屏幕是你应该经常考虑的。对于小设备例如 X10 mini 单手触摸是非常有用的,然而大屏幕大多数时候使用双手操作,一个手握住手机,空出来的另一个手触摸屏幕。
原文:
Implementing a new paradigm
As you might remember, the zoom control in the previous tutorial parts had options menu alternatives for switching between pan and zoom mode. Having menu alternatives such as these are usually not a good idea as in many Android applications the options menu is already overflowing. Also you don’t want navigation more than one click away.
译文:
实现一个新模式
你也许还记得,前面部分课程中的缩放控制使用选项菜单,在缩放和平放模式中二选一切换。使用像这些菜单选项往往不是一个好的办法,因为在很多android应用中选项菜单已经过剩了。你也不想比一个点击有更多操作。
原文:
Our new input paradigm will be similar to the one on the X10 mini. We’ll use long press to enter zoom mode and vertical touch dragging for zooming in and out. Pan will be left unchanged, activated simply by dragging.
译文:
我们新的输入模式将会和X10 mini上相似,我们使用长按进入缩放模式,纵向触摸拖拽放大和缩小。平放会保持不变,简单地拖拽激活。
原文:
We’ll start the implementation of a new listener by modifying the BasicZoomListener class we’ve built on so far, removing all references to the ControlType enum and renaming the class to LongPressZoomListener. Then we create a new constructor for the listener.
译文:
我们将通过修改目前已经建造好的 BasicZoomListener 类来实现新的listener ,删除所有指向ControlType 枚举的引用,并把类重命名成LongPressZoomListener,然后我们给listener创建一个新的构造方法。
private final int mScaledTouchSlop;
private final int mLongPressTimeout;
public LongPressZoomListener(Context context) {
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
原文:
To implement long press we need to define the long press timeout, that is the duration before a press turns into a long press. We also need to define the touch slop, that is the amount touch can wander before we decide it’s a drag event rather than a long press. As luck might have it the system defines good default values we can use for this. The touch slop value is even screen size dependent.
译文:
为了实现长按,我们需要定义长按的超时时间,这是按变成长按的时间间隔。我们还需要定义触摸溢出(译注:按着屏幕的时候手指可能会动或者其他原因,并不能保证一直在一个点上按着,所以指定一个范围,在这个范围之内算是按着,而不是滑动),这是在我们决定它是一个拖拽事件而不是长按事件之前,触摸可以晃荡的一个数量。幸运的是系统定义好了默认的值,我们可以用这个。触摸溢出值是屏幕尺寸依赖的。
原文:
To implement the long touch logic we first need to define a flag for holding the classification of the current touch event. The classification mode have three states, undefined means the touch hasn’t been classified yet and can become either pan or zoom. Pan means the user has started dragging and has entered pan mode while zoom means the user has made a long press and entered zoom mode where vertical touch movement zooms in or out.
译文:
为了实现长按逻辑,我们首先需要定义一个标记来保存当前触摸的类型,分类模式有三个状态,undefined 意味着触摸还没有被归类,可以成为平放或者缩放,Pan 意味着用户已经开始拖拽并且已经进入平放模式,同样zoom 意味着用户已经长按过并进入缩放模式,在这种模式垂直触摸移动会缩小或者放大。
private enum Mode {
UNDEFINED, PAN, ZOOM
}
private Mode mMode = Mode.UNDEFINED;
原文:
Next up we will re-implement the onTouch callback method to classify long press and drag and interpret these as either zoom or pan.
译文:
紧接着我们将会重新实现onTouch 回调方法来归类长按和拖拽,并以缩放或者平放来打断长按。
private final Runnable mLongPressRunnable = new Runnable() {
public void run() {
mMode = Mode.ZOOM;
}
};
public boolean onTouch(View v, MotionEvent event) {
final int action = event.getAction();
final float x = event.getX();
final float y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
v.postDelayed(mLongPressRunnable, mLongPressTimeout);
mDownX = x;
mDownY = y;
mX = x;
mY = y;
break;
原文:
Starting with the action down event, this is largely unchanged from our previous tutorials with the exception of posting a runnable on the view handler. This runnable, called mLongPressRunnable, is posted with a delay meaning it will run once the delay time has passed, in this case when the long press duration has passed. The runnable simply sets the mode to zoom mode, meaning once the long press duration has passed we’ll enter zoom mode.
译文:
以按下动作事件(action down event)开始,这对比前面课程基本没有改动,例外之处是在view的handler上post了一个runnable,这个runnable ,叫作mLongPressRunnable,用一个延迟(delay)post意味着一旦延迟时间过了,就会运行。在这种情况下,当长按间隔已经度过,这个runnable 仅仅设置模式为缩放模式,意味着一旦长按间隔已经过去我们就会进入缩放模式。
case MotionEvent.ACTION_MOVE: {
final float dx = (x - mX) / v.getWidth();
final float dy = (y - mY) / v.getHeight();
if (mMode == Mode.ZOOM) {
mZoomControl.zoom(
(float)Math.pow(20, -dy),
mDownX / v.getWidth(),
mDownY / v.getHeight());
} else if (mMode == Mode.PAN) {
mZoomControl.pan(-dx, -dy);
} else {
final float scrollX = mDownX - x;
final float scrollY = mDownY - y;
final float dist = (float)
Math.sqrt(scrollX * scrollX + scrollY * scrollY);
if (dist >= mScaledTouchSlop) {
v.removeCallbacks(mLongPressRunnable);
mMode = Mode.PAN;
}
}
mX = x;
mY = y;
break;
}
原文:
The action move event handling code is also similar to what we had in the previous tutorials. What’s new is the else block where we check if the the touch has wandered far enough to be classified as a drag. This is done by calculating the distance from the touch down location using the Pythagorean theorem and comparing it to the touch slop we got for the view configuration. If the touch has wandered far enough we set the pan mode flag and we remove the mLongPressRunnable from the view callback queue, making sure we don’t enter zoom mode after the long press timeout duration has passed.
译文:
移动动作事件(action move event)处理代码也和我们前面课程的代码相似,不同的是我们在else代码块检查触摸是否已经晃荡足够远,可以归类成一个拖拽。这个是通过毕达哥拉斯定理(译注:勾股定理)计算与按下位置的距离,和我们从view配置得到的触摸溢出相比较。如果触摸已经晃荡足够远,我们设置平放模式标记(pan mode flag),并且从view的回调队列移除mLongPressRunnable ,确保长按超时间隔度过以后,我们不会进入缩放模。
default:
v.removeCallbacks(mLongPressRunnable);
mMode = Mode.UNDEFINED;
break;
}
return true;
}
原文:
Finally we add support for handling the remaining touch events, such as action up and action cancel. In these cases we remove the long press callback and reset the mode flag, putting the component back into default state.
译文:
最后我们为处理剩下的触摸事件增加支持,例如抬起动作(action up)和取消动作(action cancel),在这些情形下我们删除长按回调并重置模式标记,把组件设置回默认状态。
原文:
Tactile feedback
Now we have implemented a nicer input paradigm, making it easy and swift to zoom and pan. It is however it’s a bit confusing to the user that no indication is given when they enter zoom or pan mode. To fix this we’ll use tactile feedback, giving the user a quick vibration when entering zoom mode.
译文:
触觉反馈
现在我们已经实现了一个更好的输入模式,使得缩放和平放更加容易和敏捷。然而它对用户来说有点困惑,因为在他们进入缩放或者平放模式时没有给出提示。为了解决这个,我们将使用触觉反馈,当进入缩放模式时给用户一个急促的震动。
原文:
Using the vibrator on Android devices is easy. We get access to the vibrator service through the context we get in the constructor like this.
译文:
在Android设备上使用振动器是容易的,我们通过在构造器得到的context这样访问振动器服务(vibrator service)
private final Vibrator mVibrator;
public LongPressZoomListener(Context context) {
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mVibrator = (Vibrator)context.getSystemService("vibrator");
}
原文:
Then we modify our runnable to give a quick vibration when entering zoom mode, 50 ms gives a nice and short feedback.
译文:
然后我们修改我们的runnable,当进入缩放模式时给出一个急促的震动, 50毫秒给出了一个很好而且很短的反馈。
private static final long VIBRATE_TIME = 50;
private final Runnable mLongPressRunnable = new Runnable() {
public void run() {
mMode = Mode.ZOOM;
mVibrator.vibrate(VIBRATE_TIME);
}
};
原文:
This is all the java code required for tactile feedback, however if you try to run this you’ll get an exception due to the application not having permission to use the vibrator service. This is easily remedied by adding the line below in the manifest.
译文:
这是触觉反馈所需要的所有java代码,然而,如果你试着运行这些代码,你会得到一个异常,因为应用没有权限使用震动服务(vibrator service)。这个很容易补救,通过在manifest添加下面的一行代码。
<uses-permission android:name="android.permission.VIBRATE" />
原文:
And we’re done! Our zoom is starting to feel quite nice and useful, we’ve got a nice input paradigm and a zoom that behaves reasonably. This is the perfect time for you readers to try out your own take on a input paradigm if you want to, or maybe you’ve got ideas on how to improve the one we’ve just implemented.
译文:
我们完成了,我们的缩放开始感觉到非常好并且很有用。我们得到了一个很好的输入模式并且缩放表现得很合理。这对你们尝试具有一个输入模式的读者来说是完美的时刻,如果你想要,或者也许你有其他如何提升我们已有实现的想法。
原文:
Next up we’ll implement a very nice addition to our zoom, fling and bounce. Currently a downside with the zoom is that it feels stiff and panning far requires a lot of dragging. This will all change in the next tutorial where we introduce a more dynamic behavior.
译文:
接下来我们将会实现一个很好的附属特效给我们的缩放,速滑和跳跃。当前缩放的一个缺点是感觉呆板,并且比平放远一点需要拖拽很多,这些在下一节课都将会改变,那时我们会介绍一个更加动力感的行为。
原文:
Until then, good luck!
译文:
回见,好运。