`

【翻译】(21)拖放

 
阅读更多

 

【翻译】(21)拖放

 

see

http://developer.android.com/guide/topics/ui/drag-drop.html

 

原文见

http://developer.android.com/guide/topics/ui/drag-drop.html

 

-------------------------------

 

Drag and Drop

 

拖放

 

-------------------------------

 

Quickview

 

快速概览

 

* Allow users to move data within your Activity layout using graphical gestures.

 

* 允许用户在你的Activity布局内使用图形手势来移动数据。

 

* Supports operations besides data movement.

 

* 支持除数据移动外的操作。

 

* Only works within a single application.

 

* 只工作在单一应用程序内。

 

* Requires API 11.

 

* 需要API 11。

 

In this document

 

本文目录

 

* Overview 概览

* The drag/drop process 拖放处理

* The drag event listener and callback method 拖动事件监听器和回调方法

* Drag events 拖动事件

* The drag shadow 拖动影子

* Designing a Drag and Drop Operation 设计一个拖放操作

* Starting a drag 开始拖动

* Responding to a drag start 响应开始拖动

* Handling events during the drag 在拖动期间处理事件

* Responding to a drop 响应放下

* Responding to a drag end 响应拖动结束

* Responding to drag events: an example 响应拖动事件:一个示例

 

Key classes

 

关键类

 

View

OnLongClickListener

OnDragListener

DragEvent

DragShadowBuilder

ClipData

ClipDescription

 

Related Samples

 

相关示例

 

Honeycomb Gallery. 蜂巢(注:Android 3.0代号)画廊

DragAndDropDemo.java and DraggableDot.java in Api Demos. API演示中的DragAndDropDemo.java和DraggableDot.java

 

See also

 

另见

 

Content Providers 内容提供者

Input Events 输入事件

 

-------------------------------

 

With the Android drag/drop framework, you can allow your users to move data from one View to another View in the current layout using a graphical drag and drop gesture. The framework includes a drag event class, drag listeners, and helper methods and classes.

 

使用Android拖放框架,你可以允许你的用户在当前布局内用一个图形拖放手势从一个View移动数据到另一个View。框架包含一个拖动事件类,拖动监听器,以及一些辅助方法和类。

 

Although the framework is primarily designed for data movement, you can use it for other UI actions. For example, you could create an app that mixes colors when the user drags a color icon over another icon. The rest of this topic, however, describes the framework in terms of data movement.

 

虽然框架主要设计用于数据的移动,但是你可以把它用于其他用户界面动作。例如,你应该创建一个应用,它在用户在另一个图标上方拖动一个颜色图标时混合颜色。然而,这个主题的其它部分将就数据移动的方面描述该框架。

 

-------------------------------

 

Overview

 

概览

 

A drag and drop operation starts when the user makes some gesture that you recognize as a signal to start dragging data. In response, your application tells the system that the drag is starting. The system calls back to your application to get a representation of the data being dragged. As the user's finger moves this representation (a "drag shadow") over the current layout, the system sends drag events to the drag event listener objects and drag event callback methods associated with the View objects in the layout. Once the user releases the drag shadow, the system ends the drag operation.

 

拖放操作从用户作出一些你认为是开始拖动数据的信号的动作的时候开始执行。作为响应,你的应用程序告诉系统拖动正在开始。系统调用回来你的应用程序以获得正在被拖动的数据的表现。当用户的手指在当前布局上方移动这个表现(一个“拖动影子”)时,系统发送拖动事件到拖动事件监听器对象以及在布局中与View对象有关的拖动事件回调方法。一旦用户放开拖动影子,系统就结束拖动操作。

 

You create a drag event listener object ("listeners") from a class that implements View.OnDragListener. You set the drag event listener object for a View with the View object's setOnDragListener() method. Each View object also has a onDragEvent() callback method. Both of these are described in more detail in the section The drag event listener and callback method.

 

你从一个实现View.OnDragListener的类中创建一个拖动事件监听器对象(“监听器”)。你用View对象的setOnDragListener()设置View的拖动事件监听器对象。每个View对象还拥有一个onDragEvent()回调方法。它们都将在拖动事件监听器和回调方法一节中更详细地描述。

 

-------------------------------

 

Note: For the sake of simplicity, the following sections refer to the routine that receives drag events as the "drag event listener", even though it may actually be a callback method.

 

注意:简单起见,以下章节把接收拖动事件的例程称为“拖动事件监听器”,即便它可能实际上是一个回调方法。

 

-------------------------------

 

When you start a drag, you include both the data you are moving and metadata describing this data as part of the call to the system. During the drag, the system sends drag events to the drag event listeners or callback methods of each View in the layout. The listeners or callback methods can use the metadata to decide if they want to accept the data when it is dropped. If the user drops the data over a View object, and that View object's listener or callback method has previously told the system that it wants to accept the drop, then the system sends the data to the listener or callback method in a drag event.

 

当你开始拖动,你包含你正在移动的数据以及描述该数据的元数据作为传给系统的调用的一部分。在拖动期间,系统发送拖动事件到拖动事件监听器或布局中每个View的回调方法。监听器或回调方法可以使用元数据以决定是否希望接受被拖动时的数据。如果用户在一个View对象上放下数据,而那个View对象的监听器或回调方法之前曾经告诉系统它希望接受放下,那么系统用一个拖动事件来发送数据到监听器或回调方法。

 

Your application tells the system to start a drag by calling the startDrag() method. This tells the system to start sending drag events. The method also sends the data that you are dragging.

 

你的应用程序通过调用startDrag()告诉系统开始拖动。它告诉系统开始发送拖动事件。该方法还发送你正在拖动的数据。

 

You can call startDrag() for any attached View in the current layout. The system only uses the View object to get access to global settings in your layout.

 

你可以对当前布局内任意被依附的View调用startDrag()。系统在你的布局内只使用该View对象来获得对全局设置的访问。

 

Once your application calls startDrag(), the rest of the process uses events that the system sends to the View objects in your current layout.

 

一旦你的应用程序调用startDrag(),剩下来的处理使用系统发送给你的当前布局内的View对象的事件。

 

The drag/drop process

 

拖放处理

 

There are basically four steps or states in the drag and drop process:

 

在拖放处理中基本上有四个步骤或状态:

 

* Started

 

* 开始

 

In response to the user's gesture to begin a drag, your application calls startDrag() to tell the system to start a drag. The arguments startDrag() provide the data to be dragged, metadata for this data, and a callback for drawing the drag shadow.

 

为了响应用户的手势以开始拖动,你的应用程序调用startDrag()以告诉系统开始拖动。startDrag()的参数提供要被拖动的数据,该数据的元数据,以及一个用于绘画拖动影子的回调。

 

The system first responds by calling back to your application to get a drag shadow. It then displays the drag shadow on the device.

 

系统首先通过回调到你的应用程序来响应一个拖动影子。然后它在设备上显示拖动影子。

 

Next, the system sends a drag event with action type ACTION_DRAG_STARTED to the drag event listeners for all the View objects in the current layout. To continue to receive drag events, including a possible drop event, a drag event listener must return true. This registers the listener with the system. Only registered listeners continue to receive drag events. At this point, listeners can also change the appearance of their View object to show that the listener can accept a drop event.

 

接下来,系统发送一个类型为ACTION_DRAG_STARTED的拖动事件到当前布局内的所有View对象的拖动事件监听器。为了继续接收拖动事件,包括一个可能的放下事件,一个拖动事件监听器必须返回true。它注册监听器到系统上。只有被注册的监听器才继续接收拖动事件。此时,监听器还可以改变它们的View对象的外观以展示监听器可以接受放下事件。

 

If the drag event listener returns false, then it will not receive drag events for the current operation until the system sends a drag event with action type ACTION_DRAG_ENDED. By sending false, the listener tells the system that it is not interested in the drag operation and does not want to accept the dragged data.

 

如果拖动事件监听器返回false,那么它将不接收当前操作的拖动事件,直至系统发送一个动作类型为ACTION_DRAG_ENDED的拖动事件。通过发送false,监听器告诉系统它对拖动操作不感兴趣,并且不希望接受拖动数据。

 

* Continuing

 

* 继续

 

The user continues the drag. As the drag shadow intersects the bounding box of a View object, the system sends one or more drag events to the View object's drag event listener (if it is registered to receive events). The listener may choose to alter its View object's appearance in response to the event. For example, if the event indicates that the drag shadow has entered the bounding box of the View (action type ACTION_DRAG_ENTERED), the listener can react by highlighting its View.

 

用户继续拖动。当拖动影子与一个View对象的绑定盒(注:边框)相交时,系统发送一个或多个拖动事件到View对象的拖动事件监听器(如果它被注册以接收事件)。监听器可以选择改变它的View对象的外观以响应事件。例如,如果该事件指示拖动影子已经进入View对象的绑定盒(动作类型为ACTION_DRAG_ENTERED),那么监听器可以通过高亮它的View来做出反应。

 

* Dropped

 

* 放下

 

The user releases the drag shadow within the bounding box of a View that can accept the data. The system sends the View object's listener a drag event with action type ACTION_DROP. The drag event contains the data that was passed to the system in the call to startDrag() that started the operation. The listener is expected to return boolean true to the system if code for accepting the drop succeeds.

 

用户释放在可以接受数据的View的绑定盒内释放拖动影子。系统把一个动作类型为ACTION_DROP的拖动事件发送到View对象的监听器。拖动事件包含对startDrag()的调用以开始操作时传递给系统的数据。如果用于接受放下的代码成功,那么监听器期待返回布尔值true给系统。

 

Note that this step only occurs if the user drops the drag shadow within the bounding box of a View whose listener is registered to receive drag events. If the user releases the drag shadow in any other situation, no ACTION_DROP drag event is sent.

 

注意这个步骤发生仅当用户在一个View的绑定盒内放下拖动影子,而那个View的监听器被注册以接收拖动事件。如果在其它任意情况下用户释放拖动影子,那么不会发送任何ACTION_DROP的拖动事件。

 

* Ended

 

* 结束

 

After the user releases the drag shadow, and after the system sends out (if necessary) a drag event with action type ACTION_DROP, the system sends out a drag event with action type ACTION_DRAG_ENDED to indicate that the drag operation is over. This is done regardless of where the user released the drag shadow. The event is sent to every listener that is registered to receive drag events, even if the listener received the ACTION_DROP event.

 

在用户释放拖动影子之后,并且在系统发送出(如果有必要)一个动作类型为ACTION_DROP的拖动事件之后,系统发送出一个动作类型为ACTION_DRAG_ENDED的拖动事件以指出拖动操作已经结束。不管用户在哪里释放拖动影子,都会这样做。事件被发送到每个被注册以接收拖动事件的监听器,即便监听器接收了ACTION_DROP事件。

 

Each of these four steps is described in more detail in the section Designing a Drag and Drop Operation.

 

这四个步骤的其中一步在设计一个拖放操作的章节中更详细地描述。

 

The drag event listener and callback method

 

拖动事件监听器和回调方法

 

A View receives drag events with either a drag event listener that implements View.OnDragListener or with its onDragEvent(DragEvent) callback method. When the system calls the method or listener, it passes to them a DragEvent object.

 

一个View用一个实现了View.OnDragListener的拖动事件监听器或者用它的onDragEvent(DragEvent)回调方法接收拖动事件。当系统调用该方法或监听器时,它传给它们一个DragEvent对象。

 

You will probably want to use the listener in most cases. When you design UIs, you usually don't subclass View classes, but using the callback method forces you to do this in order to override the method. In comparison, you can implement one listener class and then use it with several different View objects. You can also implement it as an anonymous inline class. To set the listener for a View object, call setOnDragListener().

 

在大多数情况下你将很可能希望使用监听器。当你设计用户界面时,你通常不会子类化View类,但使用回调方法会强迫你做这件事以便覆盖该方法。作为比较,你可以实现一个监听器类,然后用几个不同的View对象来使用它。你还可以实现它为一个匿名的行内类。为了为一个View对象设置监听器,请调用setOnDragListener()。

 

You can have both a listener and a callback method for View object. If this occurs, the system first calls the listener. The system doesn't call the callback method unless the listener returns false.

 

你可以为View对象同时拥有监听器和一个回调方法。如果这种情况发生,那么系统首先调用监听器。系统不会调用回调方法除非监听器返回false。

 

The combination of the onDragEvent(DragEvent) method and View.OnDragListener is analogous to the combination of the onTouchEvent() and View.OnTouchListener used with touch events.

 

onDragEvent(DragEvent)方法和View.OnDragListener的组合是可以比拟用于触碰事件的onTouchEvent()和View.OnTouchListener的组合。

 

Drag events

 

拖动事件

 

The system sends out a drag event in the form of a DragEvent object. The object contains an action type that tells the listener what is happening in the drag/drop process. The object contains other data, depending on the action type.

 

系统以DragEvent对象的形式发送一个拖动事件。该对象包含一个动作类型,它告诉将监听器在拖放过程中发生什么。对象包含其它数据,依赖于动作类型。

 

To get the action type, a listener calls getAction(). There are six possible values, defined by constants in the DragEvent class. These are listed in table 1.

 

为了获取动作类型,一个监听器调用getAction()。有六个可能值,由DragEvent类的常量定义。它们列举在表1中。

 

The DragEvent object also contains the data that your application provided to the system in the call to startDrag(). Some of the data is valid only for certain action types. The data that is valid for each action type is summarized in table 2. It is also described in detail with the event for which it is valid in the section Designing a Drag and Drop Operation.

 

DragEvent对象还包含你的应用程序在调用startDrag()时提供给系统的数据。一些数据仅对于某些动作类型是可用的,对于各种动作类型可用的数据被总结在表2中。还在设计拖放操作的章节中详细描述哪个事件是可用的。

 

Table 1. DragEvent action types

 

表1. DragEvent动作类型

 

-------------------------------

 

* getAction() value Meaning

 

* getAction()值 含义

 

* ACTION_DRAG_STARTED A View object's drag event listener receives this event action type just after the application calls startDrag() and gets a drag shadow.

 

* ACTION_DRAG_STARTED:一个View对象的拖动事件监听器接收这个事件动作类型,正好在应用程序调用startDrag()并获取一个拖动影子之后。

 

* ACTION_DRAG_ENTERED A View object's drag event listener receives this event action type when the drag shadow has just entered the bounding box of the View. This is the first event action type the listener receives when the drag shadow enters the bounding box. If the listener wants to continue receiving drag events for this operation, it must return boolean true to the system.

 

* ACTION_DRAG_ENTERED:一个View对象的拖动事件监听器接收这个事件动作类型,在拖动影子正好进入该View的绑定盒的时候。这是在拖动影子进入绑定盒时监听器接收的第一个事件动作类型。如果监听器希望继续为这个操作接收拖动事件,那么它必须返回布尔值true到系统。

 

* ACTION_DRAG_LOCATION A View object's drag event listener receives this event action type after it receives a ACTION_DRAG_ENTERED event while the drag shadow is still within the bounding box of the View.

 

* ACTION_DRAG_LOCATION:一个View对象的拖动事件监听器在它接收一个ACTION_DRAG_ENTERED之后而拖动影子仍然在该View的绑定盒内时,接收这个事件动作类型。

 

* ACTION_DRAG_EXITED A View object's drag event listener receives this event action type after it receives a ACTION_DRAG_ENTERED and at least one ACTION_DRAG_LOCATION event, and after the user has moved the drag shadow outside the bounding box of the View.

 

* ACTION_DRAG_EXITED:一个View对象的拖动事件监听器在它接收到一个ACTION_DRAG_ENTERED并且至少一个ACTION_DRAG_LOCATION后接收这个事件动作类型,并且在用户已经把拖动影子移出该View的绑定盒以外之后。

 

* ACTION_DROP A View object's drag event listener receives this event action type when the user releases the drag shadow over the View object. This action type is only sent to a View object's listener if the listener returned boolean true in response to the ACTION_DRAG_STARTED drag event. This action type is not sent if the user releases the drag shadow on a View whose listener is not registered, or if the user releases the drag shadow on anything that is not part of the current layout.

 

* ACTION_DROP:一个View对象的拖动事件监听器接收这个事件动作类型,当用户在该View对象上释放拖动影子的时候。如果监听器返回布尔值true以响应ACTION_DRAG_STARTED拖动事件,这个动作类型仅被发送到一个View对象的监听器。如果用户在一个View上释放拖动影子,而该View的监听器并没有被注册,或者如果用户在不是当前布局一部分的任意东西上释放拖动影子,这个动作类型不会被发送。

 

The listener is expected to return boolean true if it successfully processes the drop. Otherwise, it should return false.

 

监听器期待返回布尔值true,如果它成功地处理拖动。否则,它应该返回false。

 

* ACTION_DRAG_ENDED A View object's drag event listener receives this event action type when the system is ending the drag operation. This action type is not necessarily preceded by an ACTION_DROP event. If the system sent a ACTION_DROP, receiving the ACTION_DRAG_ENDED action type does not imply that the drop operation succeeded. The listener must call getResult() to get the value that was returned in response to ACTION_DROP. If an ACTION_DROP event was not sent, then getResult() returns false.

 

* ACTION_DRAG_ENDED:一个View对象的拖动事件监听器接收这个事件动作类型,当系统正在结束拖动操作。这个动作类型不一定在ACTION_DROP事件的前面。如果系统发送一个ACTION_DROP,接收到ACTION_DRAG_ENDED动作类型并不暗示拖动操作成功。监听器必须调用getResult()以获取被返回以响应ACTION_DROP的值。如果一个ACTION_DROP没有被发送,那么getResult()返回false。

 

-------------------------------

 

Table 2. Valid DragEvent data by action type

 

表2. 根据动作类型列出的可用DragEvent数据。

 

-------------------------------

 

getAction() value getClipDescription() value getLocalState() value getX() value getY() value getClipData() value getResult() value

 

getAction()值 getClipDescription()值 getLocalState()值 getX()值 getY()值 getClipData()值 getResult()值

 

ACTION_DRAG_STARTED X X X  

ACTION_DRAG_ENTERED X X X X  

ACTION_DRAG_LOCATION X X X X  

ACTION_DRAG_EXITED X X  

ACTION_DROP X X X X X  

ACTION_DRAG_ENDED X X X

 

-------------------------------

 

The getAction(), describeContents(), writeToParcel(), and toString() methods always return valid data.

 

getAction(),describeContents(),writeToParcel()和toString()方法总是返回可用的数据。

 

If a method does not contain valid data for a particular action type, it returns either null or 0, depending on its result type.

 

如果一个方法对于一个特定的动作类型不包含可用数据,那么它返回null或0,依赖于它的结果类型。

 

The drag shadow

 

拖动影子

 

During a drag and drop operation, the system displays a image that the user drags. For data movement, this image represents the data being dragged. For other operations, the image represents some aspect of the drag operation.

 

在拖放操作期间,系统显示用户拖动的图片。对于数据移动,这个图片代表被拖动的数据。对于其它操作,该图片代表拖动操作的一些方面。

 

The image is called a drag shadow. You create it with methods you declare for a View.DragShadowBuilder object, and then pass it to the system when you start a drag using startDrag(). As part of its response to startDrag(), the system invokes the callback methods you've defined in View.DragShadowBuilder to obtain a drag shadow.

 

那个图片被称为拖动影子。你用你为一个View.DragShadowBuilder对象声明的方法来创建它,然后当你使用startDrag()开始拖动时传递它给系统。作为它对startDrag()响应的一部分,系统调用你定义在View.DragShadowBuilder内的回调方法以取得拖动影子。

 

The View.DragShadowBuilder class has two constructors:

 

View.DragShadowBuilder有两个构造函数:

 

* View.DragShadowBuilder(View)

 

This constructor accepts any of your application's View objects. The constructor stores the View object in the View.DragShadowBuilder object, so during the callback you can access it as you construct your drag shadow. It doesn't have to be associated with the View (if any) that the user selected to start the drag operation.

 

这个构造函数接受你的应用程序的任意View对象。构造函数在View.DragShadowBuilder对象内存储View对象,所以在回调期间在你构造你的拖动影子的时候你可以访问它。它不必与用户选择的以开始执行拖动操作的View(如果有的话)关联。

 

If you use this constructor, you don't have to extend View.DragShadowBuilder or override its methods. By default, you will get a drag shadow that has the same appearance as the View you pass as an argument, centered under the location where the user is touching the screen.

 

如果你使用这个构造函数,你不必扩展View.DragShadowBuilder或覆盖它的方法。默认,你将获取一个拥有与你传递作为参数的View相同外观的拖动影子,在用户正在触碰屏幕的位置下居中。

 

* View.DragShadowBuilder()

 

If you use this constructor, no View object is available in the View.DragShadowBuilder object (the field is set to null). If you use this constructor, and you don't extend View.DragShadowBuilder or override its methods, you will get an invisible drag shadow. The system does not give an error.

 

如果你使用这个构造函数,那么在View.DragShadowBuilder对象内没有View对象是可用的(域被设置为null)。如果你使用这个构造函数,而你不扩展View.DragShadowBuilder或覆盖它的方法,那么你将获得一个不可见的拖动影子。系统不给出一个错误。

 

The View.DragShadowBuilder class has two methods:

 

View.DragShadowBuilder类有两个方法:

 

* onProvideShadowMetrics()

 

The system calls this method immediately after you call startDrag(). Use it to send to the system the dimensions and touch point of the drag shadow. The method has two arguments:

 

紧接着你调用startDrag()之后系统调用这个方法。使用它以发送给系统拖动影子的尺寸和触碰点。该方法有两个参数:

 

* dimensions

 

A Point object. The drag shadow width goes in x and its height goes in y.

 

一个Point对象。拖动影子的宽度代入x而它的高度代入y。

 

* touch_point

 

A Point object. The touch point is the location within the drag shadow that should be under the user's finger during the drag. Its X position goes in x and its Y position goes in y

 

一个Point对象。触碰点是拖动影子内的位置,它应该是在拖动期间在用户手指下面。它的X坐标代入x而它的Y坐标代入y。

 

* onDrawShadow()

 

Immediately after the call to onProvideShadowMetrics() the system calls onDrawShadow() to get the drag shadow itself. The method has a single argument, a Canvas object that the system constructs from the parameters you provide in onProvideShadowMetrics() Use it to draw the drag shadow in the provided Canvas object.

 

紧接在调用onProvideShadowMetrics()之后,系统调用onDrawShadow()以获取拖动影子本身。该方法拥有一个单一参数,一个Canvas对象,系统从你提供给onProvideShadowMetrics()的参数来构造这个Canvas。(注:这里缺句号)使用它以在提供的Canvas对象中绘制拖动影子。

 

To improve performance, you should keep the size of the drag shadow small. For a single item, you may want to use a icon. For a multiple selection, you may want to use icons in a stack rather than full images spread out over the screen.

 

为了提高性能,你应该保持拖动影子的大小尽量地小。对于单选项,你可能希望使用一个图标。对于多选,你可能希望使用堆栈内的图标而非整张图片被铺开在屏幕上。

 

-------------------------------

 

Designing a Drag and Drop Operation

 

设计拖放操作

 

This section shows step-by-step how to start a drag, how to respond to events during the drag, how respond to a drop event, and how to end the drag and drop operation.

 

本章节一步步地展示如何开始拖动,如何响应拖动期间的事件,如何响应放下事件,以及如何结束拖放操作。

 

Starting a drag

 

开始拖动

 

The user starts a drag with a drag gesture, usually a long press, on a View object. In response, you should do the following:

 

用户开始用拖动手势拖动,通常是在一个View对象上的一次长按。作为响应,你应该做以下事情:

 

1. As necessary, create a ClipData and ClipData.Item for the data being moved. As part of the ClipData object, supply metadata that is stored in a ClipDescription object within the ClipData. For a drag and drop operation that does not represent data movement, you may want to use null instead of an actual object.

 

1. 当有必要时,为被拖动的数据创建一个ClipData和ClipData.Item。作为ClipData对象的一部分,用ClipData提供存储在一个ClipDescription对象内的元数据。对于不代表数据移动的拖放操作,你可能希望使用null代替一个实际对象。

 

For example, this code snippet shows how to respond to a long press on a ImageView by creating a ClipData object that contains the tag or label of an ImageView. Following this snippet, the next snippet shows how to override the methods in View.DragShadowBuilder:

 

例如,这段代码片段展示如何通过创建一个ClipData,它包含一个ImageView的标签或标志,来响应在ImageView上的长按下。在这个片段后面,下一个代码片段展示如何覆盖View.DragShadowBuilder内的方法:

 

-------------------------------

 

// Create a string for the ImageView label

// 为ImageView标签创建一个字符串

private static final String IMAGEVIEW_TAG = "icon bitmap"

 

// Creates a new ImageView

// 创建一个新的ImageView

ImageView imageView = new ImageView(this);

 

// Sets the bitmap for the ImageView from an icon bit map (defined elsewhere)

// 设置

imageView.setImageBitmap(mIconBitmap);

 

// Sets the tag

// 设置标志

imageView.setTag(IMAGEVIEW_TAG);

 

    ...

 

// Sets a long click listener for the ImageView using an anonymous listener object that

// implements the OnLongClickListener interface

// 使用实现OnLongClickListener接口的匿名监听器对象设置ImageView的长点击监听器

imageView.setOnLongClickListener(new View.OnLongClickListener() {

 

    // Defines the one method for the interface, which is called when the View is long-clicked

    // 定义接口的一个方法,它在View被长点击时被调用。

    public boolean onLongClick(View v) {

 

    // Create a new ClipData.

    // This is done in two steps to provide clarity. The convenience method

    // ClipData.newPlainText() can create a plain text ClipData in one step.

    // 创建一个新的ClipData。

    // 它用两步来完成以提供清晰。

    // 便利方法ClipData.newPlainText()可以用一步创建一个纯文本ClipData。

 

    // Create a new ClipData.Item from the ImageView object's tag

    // 从ImageView对象的标签创建一个新的ClipData.Item

    ClipData.Item item = new ClipData.Item(v.getTag());

 

    // Create a new ClipData using the tag as a label, the plain text MIME type, and

    // the already-created item. This will create a new ClipDescription object within the

    // ClipData, and set its MIME type entry to "text/plain"

    // 使用标签作为一个标签创建一个新的ClipData,纯文本MIME类型,

    ClipData dragData = new ClipData(v.getTag(),ClipData.MIMETYPE_TEXT_PLAIN,item);

 

    // Instantiates the drag shadow builder.

    // 实例化拖动影子构建器

    View.DrawShadowBuilder myShadow = new MyDragShadowBuilder(imageView);

 

    // Starts the drag

    // 开始拖动

 

            v.startDrag(dragData,  // the data to be dragged 被拖动的数据

                        myShadow,  // the drag shadow builder 拖动影子构建器

                        null,      // no need to use local data 不需要使用本地数据

                        0          // flags (not currently used, set to 0) 标志(当前不使用,设置为0)

            );

 

    }

}

 

-------------------------------

 

2. The following code snippet defines myDragShadowBuilder It creates a drag shadow for dragging a TextView as a small gray rectangle:

 

2. 以下代码片段定义myDragShadowBuilder,它为拖动一个TextView创建一个拖动影子成为一个小的灰色矩形。

 

-------------------------------

 

    private static class MyDragShadowBuilder extends View.DragShadowBuilder {

 

    // The drag shadow image, defined as a drawable thing

    // 拖动影子图像,被定义为可绘画对象

    private static Drawable shadow;

 

        // Defines the constructor for myDragShadowBuilder

        // 为myDragShadowBuilder定义构造函数

        public MyDragShadowBuilder(View v) {

 

            // Stores the View parameter passed to myDragShadowBuilder.

            // 存储传递给myDragShadowBuilder的View参数。

            super(v);

 

            // Creates a draggable image that will fill the Canvas provided by the system.

            // 创建一个可拖动图片,它将填充由系统提供的Canvas。

            shadow = new ColorDrawable(Color.LTGRAY);

        }

 

        // Defines a callback that sends the drag shadow dimensions and touch point back to the

        // system.

        // 定义一个回调,它发送拖动影子尺寸和触碰点返回系统。

        @Override

        public void onProvideShadowMetrics (Point size, Point touch)

            // Defines local variables

            // 定义局部变量

            private int width, height;

 

            // Sets the width of the shadow to half the width of the original View

            // 设置影子宽度为原始View宽度的一半

            width = getView().getWidth() / 2;

 

            // Sets the height of the shadow to half the height of the original View

            // 设置影子高度为原始View高度的一半

            height = getView().getHeight() / 2;

 

            // The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the

            // Canvas that the system will provide. As a result, the drag shadow will fill the

            // Canvas.

            // 拖动影子是ColorDrawable。这里设置的尺寸与系统提供的Canvas相同。

            // 作为结果,拖动影子将填充Canvas。

            shadow.setBounds(0, 0, width, height);

 

            // Sets the size parameter's width and height values. These get back to the system

            // through the size parameter.

            // 设置size参数的宽和高值。这些值通过size参数返回给系统。

            size.set(width, height);

 

            // Sets the touch point's position to be in the middle of the drag shadow

            // 设置触碰点的位置为拖动影子的正中间。

            touch.set(width / 2, height / 2);

        }

 

        // Defines a callback that draws the drag shadow in a Canvas that the system constructs

        // from the dimensions passed in onProvideShadowMetrics().

        // 定义一个回调,它在系统从传递进onProvideShadowMetrics()的尺寸中

        // 构造的Canvas中绘画拖动影子

        @Override

        public void onDrawShadow(Canvas canvas) {

 

            // Draws the ColorDrawable in the Canvas passed in from the system.

            // 在从系统中传递进来的Canvas中绘画ColorDrawable

            shadow.draw(canvas);

        }

    }

 

-------------------------------

 

-------------------------------

 

Note: Remember that you don't have to extend View.DragShadowBuilder. The constructor View.DragShadowBuilder(View) creates a default drag shadow that's the same size as the View argument passed to it, with the touch point centered in the drag shadow.

 

注意:记住你不必扩展View.DragShadowBuilder。构造函数View.DragShadowBuilder(View)创建一个默认的拖动影子,它和传给它的View参数的大小相同,带有在拖动影子中居中的触碰点。

 

-------------------------------

 

Responding to a drag start

 

响应拖动开始

 

During the drag operation, the system dispatches drag events to the drag event listeners of the View objects in the current layout. The listeners should react by calling getAction() to get the action type. At the start of a drag, this methods returns ACTION_DRAG_STARTED.

 

拖动操作期间,系统派发拖动事件到在当前布局内该View对象的拖动事件监听器。监听器应该通过调用getAction()获取动作类型以作出反应。在拖动开始时,这个方法返回ACTION_DRAG_STARTED。

 

In response to an event with the action type ACTION_DRAG_STARTED, a listener should do the following:

 

为了响应带有动作类型ACTION_DRAG_STARTED的事件,一个监听器应该做以下事情:

 

1. Call getClipDescription() to get the ClipDescription. Use the MIME type methods in ClipDescription to see if the listener can accept the data being dragged.

 

1. 调用getClipDescription()以获取ClipDescription。使用ClipDescription内的MIME类型方法以查看监听器是否可以接受被拖动的数据。

 

If the drag and drop operation does not represent data movement, this may not be necessary.

 

如果拖放操作不代表数据移动,那么这种方式是非必需的。

 

2. If the listener can accept a drop, it should return true. This tells the system to continue to send drag events to the listener. If it can't accept a drop, it should return false, and the system will stop sending drag events until it sends out ACTION_DRAG_ENDED.

 

2. 如果监听器可以接受放下,那么它应该返回true。这告诉系统继续发送拖动事件给监听器。如果它不能接受放下,它应该返回false,并且系统将停止发送拖动事件直至它发出ACTION_DRAG_ENDED。

 

Note that for an ACTION_DRAG_STARTED event, these the following DragEvent methods are not valid: getClipData(), getX(), getY(), and getResult().

 

注意对于一个ACTION_DRAG_STARTED事件,以下这些DragEvent方法不可用:getClipData(),getX(),getY()和getResult()。

 

Handling events during the drag

 

在拖动期间处理事件

 

During the drag, listeners that returned true in response to the ACTION_DRAG_STARTED drag event continue to receive drag events. The types of drag events a listener receives during the drag depend on the location of the drag shadow and the visibility of the listener's View.

 

在拖动期间,返回true以响应ACTION_DRAG_STARTED拖动事件的监听器继续接收拖动事件。一个监听器在拖动期间接收的拖动事件的类型依赖于拖动影子的位置以及监听器的View的可见性。

 

During the drag, listeners primarily use drag events to decide if they should change the appearance of their View.

 

在拖动期间,监听器主要使用拖动事件以决定它们是否应该改变它们的View的外观。

 

During the drag, getAction() returns one of three values:

 

在拖动期间,getAction()返回三个值中其中一个:

 

* ACTION_DRAG_ENTERED: The listener receives this when the touch point (the point on the screen underneath the user's finger) has entered the bounding box of the listener's View.

 

* ACTION_DRAG_ENTERED:监听器接收它,当触碰点(屏幕上在用户手指选下面的点)已经进入监听器的View的绑定盒。

 

* ACTION_DRAG_LOCATION: Once the listener receives an ACTION_DRAG_ENTERED event, and before it receives an AACTION_DRAG_EXITED event, it receives a new ACTION_DRAG_LOCATION event every time the touch point moves. The getX() and getY() methods return the the X and Y coordinates of the touch point.

 

* ACTION_DRAG_LOCATION:一旦监听器接收一个ACTION_DRAG_ENTERED事件,并且在它接收到AACTION_DRAG_EXITED事件之前,每次触碰点移动,它都会接收一个新的ACTION_DRAG_LOCATION事件。getX()和getY()方法返回触碰点的X和Y坐标。

 

* ACTION_DRAG_EXITED: This event is sent to a listener that previously received ACTION_DRAG_ENTERED, after the drag shadow is no longer within the bounding box of the listener's View.

 

* ACTION_DRAG_EXITED:这个事件被发送到之前接收到

ACTION_DRAG_ENTERED的监听器,在拖动影子不再在监听器的View的绑定盒内之后。

 

The listener does not need to react to any of these action types. If the listener returns a value to the system, it is ignored. Here are some guidelines for responding to each of these action types:

 

监听器不需要对任意这些动作类型作出反应。如果该监听器返回一个值到系统,那么它被忽略。这里有一些用于响应每种这些动作类型的指引:

 

* In response to ACTION_DRAG_ENTERED or ACTION_DRAG_LOCATION, the listener can change the appearance of the View to indicate that it is about to receive a drop.

 

* 为了响应ACTION_DRAG_ENTERED或ACTION_DRAG_LOCATION,监听器可以改变View的外观以指示它将要接受放下。

 

* An event with the action type ACTION_DRAG_LOCATION contains valid data for getX() and getY(), corresponding to the location of the touch point. The listener may want to use this information to alter the appearance of that part of the View that is at the touch point. The listener can also use this information to determine the exact position where the user is going to drop the drag shadow.

 

* 一个动作类型为ACTION_DRAG_LOCATION的事件包含用于getX()和getY()的可用数据,对应触碰点的位置。监听器可能希望使用这个信息去修改该View在触碰点所在的那个部分的外观。监听器还可以使用这个信息来决定用户正在放下拖动影子的精确位置。

 

* In response to ACTION_DRAG_EXITED, the listener should reset any appearance changes it applied in response to ACTION_DRAG_ENTERED or ACTION_DRAG_LOCATION. This indicates to the user that the View is no longer an imminent drop target.

 

* 为了响应ACTION_DRAG_EXITED,监听器应该重置它应用了的任意外观改变以响应ACTION_DRAG_ENTERED和ACTION_DRAG_LOCATION。这指示用户该View不再是一个临近的放下目标。

 

Responding to a drop

 

响应放下

 

When the user releases the drag shadow on a View in the application, and that View previously reported that it could accept the content being dragged, the system dispatches a drag event to that View with the action type ACTION_DROP. The listener should do the following:

 

当用户在应用程序中的View上释放拖动影子,而且那个View之前被报告它应该接受被拖动的内容,那么系统派发一个动作类型为ACTION_DROP的拖动事件到那个View。监听器应该做以下事情:

 

1. Call getClipData() to get the ClipData object that was originally supplied in the call to startDrag() and store it. If the drag and drop operation does not represent data movement, this may not be necessary.

 

1. 调用getClipData()以获取最初在调用startDrag()时提供的ClipData对象并且保存它。如果拖放操作不表现数据移动,那么这可能不是必需的。

 

2. Return boolean true to indicate that the drop was processed successfully, or boolean false if it was not. The returned value becomes the value returned by getResult() for an ACTION_DRAG_ENDED event.

 

2. 返回布尔值true以指示拖动被成功处理,或者布尔值false如果它不成功。返回的值成为通过对ACTION_DRAG_ENDED事件的getResult()的返回值。

 

Note that if the system does not send out an ACTION_DROP event, the value of getResult() for an ACTION_DRAG_ENDED event is false.

 

注意如果系统不发送一个ACTION_DROP事件,那么getResult()对于一个ACTION_DRAG_ENDED事件的值是false。

 

For an ACTION_DROP event, getX() and getY() return the X and Y position of the drag point at the moment of the drop, using the coordinate system of the View that received the drop.

 

对于一个ACTION_DROP事件,getX()和getY()返回在拖动的瞬间拖动点的X和Y位置。使用接收拖动的View的坐标系统

 

The system does allow the user to release the drag shadow on a View whose listener is not receiving drag events. It will also allow the user to release the drag shadow on empty regions of the application's UI, or on areas outside of your application. In all of these cases, the system does not send an event with action type ACTION_DROP, although it does send out an ACTION_DRAG_ENDED event.

 

系统允许用户在一个View上释放拖动影子,该View的监听器并不正在接收拖动事件。它还将允许用户释放拖动影子在应用程序的用户界面的一个空区域,或在你的应用程序外的区域。在所有这些情况下,系统都不会发生一个动作类型为ACTION_DROP的事件,虽然它不发出一个ACTION_DRAG_ENDED事件。

 

Responding to a drag end

 

响应拖动结束

 

Immediately after the user releases the drag shadow, the system sends a drag event to all of the drag event listeners in your application, with an action type of ACTION_DRAG_ENDED. This indicates that the drag operation is over.

 

紧接在用户释放拖动影子之后,系统发送一个拖动事件到你的应用程序内的所有拖动事件监听器,其动作类型为ACTION_DRAG_ENDED。它指示拖动操作结束。

 

Each listener should do the following:

 

每个监听器应该做以下事情

 

1. If listener changed its View object's appearance during the operation, it should reset the View to its default appearance. This is a visual indication to the user that the operation is over.

 

1. 如果监听器在操作期间改变它的View对象的外观,那么它应该重置该View为它的默认外观。这是一个可视化的指示,告诉用户操作已经结束。

 

2. The listener can optionally call getResult() to find out more about the operation. If a listener returned true in response to an event of action type ACTION_DROP, then getResult() will return boolean true. In all other cases, getResult() returns boolean false, including any case in which the system did not send out a ACTION_DROP event.

 

2. 监听器可以可选地调用getResult()以找出更多关于该操作的东西。如果一个监听器返回true以响应一个动作类型为ACTION_DROP的事件。那么getResult()将返回布尔值true。在其它所有情况下,getResult()返回布尔值false,包括系统不发出ACTION_DROP事件的任意情况下。

 

3. The listener should return boolean true to the system.

 

3. 监听器应该返回布尔值true到系统。

 

Responding to drag events: an example

 

响应拖动事件:一个示例

 

All drag events are initially received by your drag event method or listener. The following code snippet is a simple example of reacting to drag events in a listener:

 

所有拖动事件最初通过你的拖动事件方法或监听器来接收。以下代码片段是在监听器中响应拖动事件的简单示例。

 

-------------------------------

 

// Creates a new drag event listener

// 创建一个新的拖动事件监听器

mDragListen = new myDragEventListener();

 

View imageView = new ImageView(this);

 

// Sets the drag event listener for the View

// 为View设置拖动事件监听器

imageView.setOnDragListener(mDragListen);

 

...

 

protected class myDragEventListener implements View.OnDragEventListener {

 

    // This is the method that the system calls when it dispatches a drag event to the

    // listener.

    // 这是在系统派发一个拖动事件到监听器时它调用的方法。

    public boolean onDrag(View v, DragEvent event) {

 

        // Defines a variable to store the action type for the incoming event

        // 定义一个变量以存储输入事件的动作类型

        final int action = event.getAction();

 

        // Handles each of the expected events

        // 处理每种预期的事件

        switch(action) {

 

            case DragEvent.ACTION_DRAG_STARTED:

 

                // Determines if this View can accept the dragged data

                // 决定这个View是否可以接受拖动数据

                if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {

 

                    // As an example of what your application might do,

                    // applies a blue color tint to the View to indicate that it can accept

                    // data.

                    // 作为你的应用程序可以做什么的示例,

                    // 应用一个蓝色色调到View以指示它可以接受数据。

                    v.setColorFilter(Color.BLUE);

 

                    // Invalidate the view to force a redraw in the new tint

                    // 无效化视图以强制用新的色调重画

                    v.invalidate();

 

                    // returns true to indicate that the View can accept the dragged data.

                    // 返回true以指示View可以接受拖动数据。

                    return(true);

 

                    } else {

 

                    // Returns false. During the current drag and drop operation, this View will

                    // not receive events again until ACTION_DRAG_ENDED is sent.

                    // 返回false。在当前拖放操作期间。这个View将不会再次接收事件,直至ACTION_DRAG_ENDED被发送。

                    return(false);

 

                    }

                break;

 

            case DragEvent.ACTION_DRAG_ENTERED: {

 

                // Applies a green tint to the View. Return true; the return value is ignored.

// 应用一个绿色色调到View。返回true;返回值被忽略。

                v.setColorFilter(Color.GREEN);

 

                // Invalidate the view to force a redraw in the new tint

                // 无效化视图以强制用新的色调重画

                v.invalidate();

 

                return(true);

 

                break;

 

                case DragEvent.ACTION_DRAG_LOCATION:

 

                // Ignore the event

                // 忽略事件

                    return(true);

 

                break;

 

                case DragEvent.ACTION_DRAG_EXITED:

 

                    // Re-sets the color tint to blue. Returns true; the return value is ignored.

                    // 重新设置色调为蓝色。返回true;返回值被忽略。

                    v.setColorFilter(Color.BLUE);

 

                    // Invalidate the view to force a redraw in the new tint

                    // 无效化视图以强制用新的色调重画

                    v.invalidate();

 

                    return(true);

 

                break;

 

                case DragEvent.ACTION_DROP:

 

                    // Gets the item containing the dragged data

                    // 获取包含拖放数据的条目

                    ClipData.Item item = event.getClipData().getItemAt(0);

 

                    // Gets the text data from the item.

                    // 从条目中获取文本数据。

                    dragData = item.getText();

 

                    // Displays a message containing the dragged data.

                    // 显示一个包含拖动数据的消息

                    Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_LONG);

 

                    // Turns off any color tints

                    // 关闭任意颜色色调

                    v.clearColorFilter();

 

                    // Invalidates the view to force a redraw

                    // 无效化视图以强制重画

                    v.invalidate();

 

                    // Returns true. DragEvent.getResult() will return true.

                    // 返回true。DragEvent.getResult()将返回true。

                    return(true);

 

                break;

 

                case DragEvent.ACTION_DRAG_ENDED:

 

                    // Turns off any color tinting

                    // 关闭任意颜色色调

                    v.clearColorFilter();

 

                    // Invalidates the view to force a redraw

                    // 无效化视图以强制重画

                    v.invalidate();

 

                    // Does a getResult(), and displays what happened.

                    // 执行getResult(),并且显示发生什么事。

                    if (event.getResult()) {

                        Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG);

 

                    } else {

                        Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG);

 

                    };

 

                    // returns true; the value is ignored.

                    // 返回true;值被忽略。

                    return(true);

 

                break;

 

                // An unknown action type was received.

                // 接收到一个未知的动作类型

                default:

                    Log.e("DragDrop Example","Unknown action type received by OnDragListener.");

 

                break;

        };

    };

};

 

-------------------------------

 

Except as noted, this content is licensed under Apache 2.0. For details and restrictions, see the Content License.

 

除特别说明外,本文在Apache 2.0下许可。细节和限制请参考内容许可证。

 

Android 4.0 r1 - 09 Dec 2011 11:54

 

-------------------------------

 

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

 

(此页部分内容基于Android开源项目,以及使用根据创作公共2.5来源许可证描述的条款进行修改)

 

分享到:
评论

相关推荐

    C++ GUI Qt4编程(第二版).pdf【第一部分】

    《C++ GUI Qt4编程(第二版)》,英文名《C++ GUI Programming with Qt4,Second Edition》,作者:【加拿大】Jasmin Blanchette、【英】Mark Summerfield,翻译:闫锋欣、曾泉人、张志强,审校:周莉娜、赵延兵,...

    C++ GUI Qt4编程(第二版).pdf【第二部分】

    《C++ GUI Qt4编程(第二版)》,英文名《C++ GUI Programming with Qt4,Second Edition》,作者:【加拿大】Jasmin Blanchette、【英】Mark Summerfield,翻译:闫锋欣、曾泉人、张志强,审校:周莉娜、赵延兵,...

    C++ GUI Qt4编程(第二版).pdf【第三部分】

    《C++ GUI Qt4编程(第二版)》,英文名《C++ GUI Programming with Qt4,Second Edition》,作者:【加拿大】Jasmin Blanchette、【英】Mark Summerfield,翻译:闫锋欣、曾泉人、张志强,审校:周莉娜、赵延兵,...

    精通Qt4编程(第二版)源代码

    \7.1.3 Graphics View框架下的拖放 \7.1.3 操作 215 \7.2 使用剪贴板 217 \7.3 小结 218 \第8章 文件处理 219 \8.1 读写文本文件 219 \8.2 操作二进制文件 220 \8.3 临时文件 222 \8.4 目录操作和文件管理 ...

    精通qt4编程(源代码)

    \ 第7章 拖放操作与剪贴板 蔡志明 本章简要地说明了基于MIME的拖放操作和剪贴板的使用,关于Graphics View框架的拖放操作也在本章。 212 \ 第8章 文件处理 蔡志明介绍了Qt的文件处理,包括基于流的文本文件和二进制...

    biblioteq:BiblioteQ致力于成为专业的归档,编目和库管理套件,利用Qt界面并提供与PostgreSQL和SQLite的连接。 Open Library,SRU和Z39.50协议用于检索书籍,期刊和杂志的数据。 AMD,ARM,Alpha,PowerPC,Sparc64等已完成

    BiblioteQ是免费的开放库软件。 ARM体系结构支持。 管理员角色。 Alpha体系结构支持。 存档重要文件。 书籍,DVD,灰色文学,期刊,杂志,音乐CD,照片集和视频游戏的分类。... MARC21和UNIMA

    精通ANDROID 3(中文版)1/2

    25.1.4 探索拖放操作  25.2 多点触摸  25.2.1 Android2.2之前的多点触摸  25.2.2 自Android 2.2开始的多点触摸  25.3 触摸地图  25.4 手势  25.4.1 捏合手势  25.4.2 GestureDetector和...

    精通Android 3 (中文版)2/2

    25.1.4 探索拖放操作  25.2 多点触摸  25.2.1 Android2.2之前的多点触摸  25.2.2 自Android 2.2开始的多点触摸  25.3 触摸地图  25.4 手势  25.4.1 捏合手势  25.4.2 GestureDetector和...

    文章管理器v3.3.6.5.rar

    04、搜索内容保存到配置文件/完善文件拖放防出错功能 05、窗口页面关闭后才刷新分类与链接,其他窗体忽略 06、添加全局输入法调整/文章标题触碰后变宽设置 07、提供资源共享平台,方便用户自由上传/下载数据 ...

    Sveil开源商城系统 1.0

    21、修复第一次加载产品页面的错误 22、修复Paypal express结账时的错误 23、计算在产品分类中产品数量 24、电话号码不能保存 25、付款模块  支付宝  Moneybookers  Amazon  Google Checkout  eWay ...

    WPF编程宝典 part1

    7.4.3 管理翻译过程 195 7.5 小结 200 第8章 元素绑定 201 8.1 将元素绑定到一起 201 8.1.1 绑定表达式 202 8.1.2 绑定错误 203 8.1.3 绑定模式 203 8.1.4 使用代码创建绑定 205 8.1.5 使用代码检索绑定 206 8.1.6 ...

    WPF编程宝典 part2

    7.4.3 管理翻译过程 195 7.5 小结 200 第8章 元素绑定 201 8.1 将元素绑定到一起 201 8.1.1 绑定表达式 202 8.1.2 绑定错误 203 8.1.3 绑定模式 203 8.1.4 使用代码创建绑定 205 8.1.5 使用代码检索绑定 206 8.1.6 ...

    PowerPoint.2007宝典 3/10

    1.4.3 幻灯片放映视图 21 1.4.4 备注页视图 22 1.5 放大和缩小 23 1.6 启用可选显示元素 24 1.6.1 标尺 24 1.6.2 网格线 24 1.6.3 参考线 25 1.6.4 颜色/灰度/纯黑白视图 26 1.7 打开新显示窗口 ...

    PowerPoint.2007宝典 10/10

    1.4.3 幻灯片放映视图 21 1.4.4 备注页视图 22 1.5 放大和缩小 23 1.6 启用可选显示元素 24 1.6.1 标尺 24 1.6.2 网格线 24 1.6.3 参考线 25 1.6.4 颜色/灰度/纯黑白视图 26 1.7 打开新显示窗口 ...

Global site tag (gtag.js) - Google Analytics