Android - 拖放
Android 拖放框架允许您的用户使用图形拖放手势将数据从一个视图移动到当前布局中的另一个视图。从 API 11 开始,支持将视图拖放到其他视图或视图组上。该框架包括以下三个重要组件以支持拖放 & 丢弃功能 −
拖动事件类。
拖动监听器。
辅助方法和类。
拖放过程
拖放过程基本上有四个步骤或状态 −
Started − 当您开始拖动布局中的项目时发生此事件,您的应用程序调用 startDrag() 方法告诉系统开始拖动。 startDrag() 方法中的参数提供要拖动的数据、此数据的元数据以及用于绘制拖动阴影的回调。
系统首先通过回调您的应用程序以获取拖动阴影来响应。 然后它会在设备上显示拖动阴影。
接下来,系统会发送一个动作类型为 ACTION_DRAG_STARTED 的拖动事件到当前布局中所有 View 对象的已注册拖动事件监听器。
要继续接收拖动事件,包括可能的放置事件,拖动事件侦听器必须返回 true,如果拖动事件侦听器返回 false,则在系统发送操作类型为 ACTION_DRAG_ENDED 的拖动事件之前,它不会接收当前操作的拖动事件。
Continuing − 用户继续拖动。 系统将 ACTION_DRAG_ENTERED 动作后跟 ACTION_DRAG_LOCATION 动作发送到拖动点进入的 View 的已注册拖动事件侦听器。 侦听器可以选择更改其 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 向 View 发出信号,表明拖动点已进入 View 的边界框。 |
| 3 | ACTION_DRAG_LOCATION 如果拖动阴影仍在视图对象的边界框内,则在 ACTION_DRAG_ENTERED 之后发送到视图。 |
| 4 | ACTION_DRAG_EXITED 表示用户已将拖动阴影移动到视图的边界框之外。 |
| 5 | ACTION_DROP 向 View 发出信号,表明用户已释放拖动阴影,并且拖动点位于 View 的边界框内。 |
| 6 |
ACTION_DRAG_ENDED 向 View 发出拖放操作已结束的信号。 |
方法
以下是 DragEvent 类中一些重要且最常用的方法。
| 序号 | 常量 & 描述 |
|---|---|
| 1 | int getAction() 检查这个事件的动作值.. |
| 2 | ClipData getClipData() 返回作为 startDrag() 调用的一部分发送到系统的 ClipData 对象。 |
| 3 |
ClipDescription getClipDescription() 返回 ClipData 中包含的 ClipDescription 对象。 |
| 4 |
boolean getResult() 返回拖放操作结果的指示。 |
| 5 |
float getX() 获取拖动点的 X 坐标。 |
| 6 | float getY() 获取拖动点的 Y 坐标。 |
| 7 | String toString() 返回此 DragEvent 对象的字符串表示形式。 |
监听拖动事件
如果您希望 Layout 中的任何视图都响应 Drag 事件,那么您的视图要么实现 View.OnDragListener 要么设置 onDrag Event(DragEvent) 回调方法。当系统调用该方法或侦听器时,它会将上述 DragEvent 对象传递给它们。 您可以同时拥有 View 对象的侦听器和回调方法。 如果发生这种情况,系统首先调用监听器,然后只要监听器返回 true,就定义回调。
onDragEvent(DragEvent) 方法和 View.OnDragListener 的组合类似于旧版本 Android 中用于触摸事件的 onTouchEvent() 和 View.OnTouchListener 的组合。
开始拖动事件
您首先为要移动的数据创建 ClipData 和 ClipData.Item。 作为 ClipData 对象的一部分,提供存储在 ClipData 内的 ClipDescription 对象中的元数据。对于不代表数据移动的拖放操作,您可能希望使用 null 而不是实际对象。
接下来,您可以扩展扩展 View.DragShadowBuilder 以创建用于拖动视图的拖动阴影,或者您可以简单地使用 View.DragShadowBuilder(View) 创建与传递给它的 View 参数大小相同的默认拖动阴影,触摸点位于拖动阴影的中心。
示例
以下示例显示了简单拖动的功能 & 使用 View.setOnLongClickListener()、 View.setOnTouchListener() 和 View.OnDragEventListener() 拖放。
| 步骤 | 描述 |
|---|---|
| 1 | 您将使用 Android Studio IDE 创建一个 Android 应用程序,并将其命名为 My Application,位于包 com.example.saira_000.myapplication 下。 |
| 2 | 修改 src/MainActivity.java 文件并添加代码以定义事件侦听器以及示例中使用的徽标图像的回调方法。 |
| 3 | 将图像 abc.png 复制到 res/drawable-* 文件夹中。 如果您想为不同的设备提供图像,您可以使用不同分辨率的图像。 |
| 4 | 修改布局 XML 文件 res/layout/activity_main.xml 以定义徽标图像的默认视图。 |
| 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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img=(ImageView)findViewById(R.id.imageView);
img.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public 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() {
@Override
public 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 nothing
break;
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 nothing
break;
case DragEvent.ACTION_DROP:
Log.d(msg, "ACTION_DROP event");
// Do nothing
break;
default: break;
}
return true;
}
});
img.setOnTouchListener(new View.OnTouchListener() {
@Override
public 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 表示 tutorialspoint.com 的标志
<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">
<TextView
android: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" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tutorials Point"
android:id="@+id/textView2"
android:layout_below="@+id/textView"
android:layout_centerHorizontal="true"
android:textSize="30dp"
android:textColor="#ff14be3c" />>
<ImageView
android: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" >
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android: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 运行应用程序,请打开项目的一个活动文件,然后单击工具栏中的 Run
图标。 Android Studio 在您的 AVD 上安装应用程序并启动它,如果您的设置和应用程序一切正常,它将显示以下 Emulator 窗口 −
现在长按显示的 TutorialsPoint 标志,你会看到标志图像从它的位置长按 1 秒后移动了一点,这是你应该开始拖动图像的时间。 您可以在屏幕上拖动它并将其放在新位置。