Android(安卓)拖放
Android 拖放框架支持用户使用图形拖放手势将数据从当前布局中的一个视图移动到另一个视图。从 API 11 开始,支持将视图拖放到其他视图或视图组。该框架包括以下三个重要组件,以支持拖放功能:
- Drag 事件类
- Drag 监听器
- Helper 方法和类
Drag/Drop(拖放)过程
拖放过程基本上有 4 个步骤或状态:
Started(开始) − 当您开始拖动布局中的项目时,会发生此事件,应用程序调用
startDrag()方法通知系统开始拖动。startDrag()方法中的参数提供要拖动的数据、该数据的元数据以及用于绘制拖动阴影的回调。系统首先通过调用您的应用程序来获得拖动阴影来进行响应。然后在设备上显示拖动阴影。接下来,系统将动作类型为ACTION_DRAG_STARTED的拖动事件发送给当前布局中所有 View 对象的注册拖动事件监听器。要继续接收拖动事件,包括可能的拖放事件,拖动事件监听器必须返回 true。如果拖动事件侦听程序返回 false,则在系统发送动作类型为ACTION_DRAG_ENDED的拖动事件之前,它将不会接收当前操作的拖动事件。Continuing(继续) − 用户继续拖动。系统将
ACTION_DRAG_ENTERED动作后跟ACTION_DRAG_LOCATION动作发送给拖动点进入的视图的注册拖动事件监听器。侦听器可以选择更改其 View 对象的外观以响应事件,也可以通过突出显示其 View 来做出反应。在用户将拖动阴影移动到视图的边界框之外之后,拖动事件监听器接收ACTION_DRAG_EXITED动作。Dropped(放) − 用户在视图的边界框内释放拖动的项目。系统向 View 对象的监听器发送动作类型为
ACTION_DROP的拖动事件。Ended(结束) − 就在动作类型
ACTION_DROP之后,系统发送一个动作类型为ACTION_DRAG_ENDED的拖拽事件,以指示拖拽操作结束。
DragEvent 类
DragEvent 表示在拖放操作期间系统在不同时间发出的事件。这个类提供了一些常量和我们在拖放过程中使用的重要方法。
常量
以下是作为 DragEvent 类的一部分可用的所有常量整数。
| 编号 | 常量 & 描述 |
|---|---|
| 1 | ACTION_DRAG_STARTED 表示开始拖放操作。 |
| 2 | ACTION_DRAG_ENTERED 向视图发出拖动点已进入视图边界框的信号。 |
| 3 | ACTION_DRAG_LOCATION 如果拖动阴影仍在视图对象的边界框内,则在 ACTION_DRAG_ENTERED 之后发送到视图。 |
| 4 | ACTION_DRAG_EXITED 表示用户已将拖动阴影移动到视图的边界框之外。 |
| 5 | ACTION_DROP 向视图发出信号,表示用户已释放拖动阴影,并且拖动点在视图的边界框内。 |
| 6 | ACTION_DRAG_ENDED 向视图发出拖放操作已结束的信号。 |
方法
以下是作为 DragEvent 类的一部分可用的几个重要且最常用的方法。
| 编号 | 常量 & 描述 |
|---|---|
| 1 | int getAction() 检查此事件的 Action 值。 |
| 2 | ClipData getClipData() 返回作为 startDrag() 调用的一部分发送到系统的 ClipData 对象。 |
| 3 | ClipDescription getClipDescription() 返回 ClipData 中包含的 ClipDescription 对象。 |
| 4 | boolean getResult() 返回拖放操作结果的指示。 |
| 5 | floatgetX() 获取拖动点的 X 坐标。 |
| 6 | float getY() 获取拖动点的 Y 坐标。 |
| 7 | String toString() 返回此 DragEvent 对象的字符串表示形式。 |
监听拖动事件
如果您希望布局中的任何视图都应响应 Drag 事件,那么您的视图要么实现 View.OnDragListener 或设置 onDragEvent(DragEvent) 回调方法。当系统调用方法或监听器时,它会向它们传递一个上面解释过的 DragEvent 对象。您可以同时拥有 View 对象的监听器和回调方法。如果发生这种情况,系统首先调用监听器,然后定义回调,只要监听器返回 true。
onDragEvent(DragEvent) 方法和 View.OnDragListener 的组合类似于 onTouchEvent() 和 View 的组合。OnTouchListener 用于旧版本 Android 中的触摸事件。
启动拖动事件
首先为要移动的数据创建 ClipData 和 ClipDataItem。作为 ClipData 对象的一部分,提供存储在 ClipData 中的 ClipDescription 对象中的元数据。对于不表示数据移动的拖放操作,您可能希望使用 null 而不是实际对象。
接下来,您可以扩展 View.DragShadowBuilder 以创建用于拖动视图的拖动阴影,也可以简单地使用 View.DragShadowBuilder(View) 创建与传递给它的 view 参数大小相同的默认拖动阴影,触摸点位于拖动阴影的中心。
实例
以下实例显示了使用了 View.setOnLongClickListener(), View.setOnTouchListener() 和 View.OnDragEventListener() 进行简单拖放的功能。
| 步骤 | 描述 |
|---|---|
| 1 | 您将使用 Android studio IDE 创建一个 Android 应用程序,并将其命名为 com.example.saira_000.myapplication 包下的 My Application。 |
| 2 | 修改 src/MainActivity.java 文件,并添加代码来定义事件监听器以及示例中使用的 logo 图像的回调方法。 |
| 3 | 复制图像 abc.png 在 res/drawable-* 文件夹中您可以使用不同分辨率的图像,以防您想为不同设备提供图像。 |
| 4 | 修改布局 XML 文件 res/layout/activity_main.XML 以定义 logo 图像的默认视图。 |
| 5 | 运行应用程序以启动 Android 模拟器并验证应用程序中所做更改的结果。 |
以下是修改后的主活动文件 src/MainActivity.java 的内容。该文件可以包括每个基本生命周期方法。
package com.example.saira_000.myapplication;import android.app.Activity;import android.content.ClipData;import android.content.ClipDescription;import android.support.v7.app.ActionBarActivity;import android.os.Bundle;import android.util.Log;import android.view.DragEvent;import android.view.Menu;import android.view.MenuItem;import android.view.MotionEvent;import android.view.View;import android.widget.ImageView;import android.widget.RelativeLayout;public class MainActivity extends Activity {ImageView img;String msg;private android.widget.RelativeLayout.LayoutParams layoutParams;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);img=(ImageView)findViewById(R.id.imageView);img.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {ClipData.Item item = new ClipData.Item((CharSequence)v.getTag());String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};ClipData dragData = new ClipData(v.getTag().toString(),mimeTypes, item);View.DragShadowBuilder myShadow = new View.DragShadowBuilder(img);v.startDrag(dragData,myShadow,null,0);return true;}});img.setOnDragListener(new View.OnDragListener() {@Overridepublic boolean onDrag(View v, DragEvent event) {switch(event.getAction()) {case DragEvent.ACTION_DRAG_STARTED:layoutParams = (RelativeLayout.LayoutParams)v.getLayoutParams();Log.d(msg, "Action is DragEvent.ACTION_DRAG_STARTED");// Do nothingbreak;case DragEvent.ACTION_DRAG_ENTERED:Log.d(msg, "Action is DragEvent.ACTION_DRAG_ENTERED");int x_cord = (int) event.getX();int y_cord = (int) event.getY();break;case DragEvent.ACTION_DRAG_EXITED :Log.d(msg, "Action is DragEvent.ACTION_DRAG_EXITED");x_cord = (int) event.getX();y_cord = (int) event.getY();layoutParams.leftMargin = x_cord;layoutParams.topMargin = y_cord;v.setLayoutParams(layoutParams);break;case DragEvent.ACTION_DRAG_LOCATION :Log.d(msg, "Action is DragEvent.ACTION_DRAG_LOCATION");x_cord = (int) event.getX();y_cord = (int) event.getY();break;case DragEvent.ACTION_DRAG_ENDED :Log.d(msg, "Action is DragEvent.ACTION_DRAG_ENDED");// Do nothingbreak;case DragEvent.ACTION_DROP:Log.d(msg, "ACTION_DROP event");// Do nothingbreak;default: break;}return true;}});img.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {ClipData data = ClipData.newPlainText("", "");View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(img);img.startDrag(data, shadowBuilder, img, 0);img.setVisibility(View.INVISIBLE);return true;} else {return false;}}});}}
以下是 res/layout/activity_main.xml 文件的内容−在下面的代码中,
abc 表示 logo
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:paddingBottom="@dimen/activity_vertical_margin"tools:context=".MainActivity"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Drag and Drop Example"android:id="@+id/textView"android:layout_alignParentTop="true"android:layout_centerHorizontal="true"android:textSize="30dp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="demo"android:id="@+id/textView2"android:layout_below="@+id/textView"android:layout_centerHorizontal="true"android:textSize="30dp"android:textColor="#ff14be3c" />><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/imageView"android:src="@drawable/abc"android:layout_below="@+id/textView2"android:layout_alignRight="@+id/textView2"android:layout_alignEnd="@+id/textView2"android:layout_alignLeft="@+id/textView2"android:layout_alignStart="@+id/textView2" /></RelativeLayout>
下面是 res/values/strings.xml 的内容,定义常量:
<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">My Application</string></resources>
下面是 AndroidManifest.xml 的默认内容:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.saira_000.myapplication" ><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name=".MainActivity"android:label="@string/app_name" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
让我们尝试运行 My Application 应用程序。我假设您在进行环境设置时创建了 AVD。要从 Android Studio 运行应用程序,请打开项目的 activity 文件之一,然后单击工具栏上的运行 Eclipse
图标。Android studio 在您的 AVD 上安装应用程序并启动它,如果您的设置和应用程序一切正常,它将显示以下模拟器窗口:

现在,长时间单击显示的图标,您将看到徽标图像在长时间单击1秒后从其位置稍微移动,此时您应该开始拖动图像。您可以在屏幕上拖动它并将其放置在新位置。
