Android 简明教程

Android - Drag and Drop

Android 拖放框架允许您的用户使用图形拖放手势将数据从一个视图移动到当前布局中的另一个视图。从 API 11 开始,支持将视图拖放到其他视图或视图组。该框架包含以下三个重要组件以支持拖放功能 -

  1. Drag event class.

  2. Drag listeners.

  3. Helper methods and classes.

The Drag/Drop Process

拖放过程中基本上有四个步骤或状态 -

  1. Started * − This event occurs when you start dragging an item in a layout, your application calls startDrag() method to tell the system to start a drag. The arguments inside startDrag() method provide the data to be dragged, metadata for this data, and a callback for drawing the drag shadow. 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 registered 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 ,如果拖动事件监听器返回 false,则在系统发送具有动作类型 ACTION_DRAG_ENDED 的拖动事件之前,它将不会收到当前操作的拖动事件。

  2. Continuing - 用户继续拖动。系统向视图的注册拖动事件监听器发送 ACTION_DRAG_ENTERED 动作,然后发送 ACTION_DRAG_LOCATION 动作,拖动点进入此视图。监听器可能会选择对其视图对象的外观进行更改以响应事件,或者可以通过突出显示其视图来做出反应。在用户将拖动阴影移出视图的边界框之后,拖动事件监听器会收到一个 ACTION_DRAG_EXITED 动作。

  3. Dropped - 用户在视图的边界框内释放拖动的项目。系统向视图对象的监听器发送一个具有动作类型 ACTION_DROP 的拖动事件。

  4. Ended - 刚好在动作类型 ACTION_DROP 之后,系统发送一个具有动作类型 ACTION_DRAG_ENDED 的拖动事件,以表明拖动操作已结束。

The DragEvent Class

DragEvent 表示在拖放操作期间由系统在不同时间发送的事件。此类提供了一些常量和重要方法,我们在拖放过程中使用它们。

Constants

以下是作为 DragEvent 类的部分可用的所有常量整数。

Sr.No.

Constants & Description

1

ACTION_DRAG_STARTED 指示拖放操作的开始。

2

ACTION_DRAG_ENTERED 向 View 发出信号,表明拖动手柄已进入 View 的边框框。

3

ACTION_DRAG_LOCATION 在 ACTION_DRAG_ENTERED 后发送到 View,如果拖动阴影仍在 View 对象的边框框内。

4

ACTION_DRAG_EXITED 指示用户已将拖动阴影移出 View 的边框框。

5

ACTION_DROP 向 View 发出信号,表明用户已释放拖动阴影,并且拖动手柄位于 View 的边框框内。

6

ACTION_DRAG_ENDED 向 View 发出信号,表明拖放操作已结束。

Methods

以下是作为 DragEvent 类的部分可用的一些重要且最常用的方法。

Sr.No.

Constants & Description

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 对象的字符串表示形式。

Listening for Drag Event

如果您希望布局中的任何视图都应响应拖动事件,那么您的视图要么实现 View.OnDragListener ,要么设置 onDragEvent(DragEvent) 回调方法。当系统调用方法或侦听器时,它会将上述 DragEvent 对象传递给它们。您可以对 View 对象同时使用侦听器和回调方法。如果发生这种情况,系统首先调用侦听器,然后定义回调,只要侦听器返回 true。

onDragEvent(DragEvent) 方法和 View.OnDragListener 的组合类似于 Android 旧版本中与触摸事件一起使用的 onTouchEvent()View.OnTouchListener 的组合。

Starting a Drag Event

您从创建一个 ClipDataClipData.Item 开始,用于正在移动的数据。作为 ClipData 对象的一部分,提供存储在 ClipData 中的 ClipDescription 对象中的元数据。对于不表示数据移动的拖放操作,您可能希望使用 null 而不是实际对象。

接下来,您可以扩展 View.DragShadowBuilder 来为拖动视图创建拖动阴影,或者您只需使用 View.DragShadowBuilder(View) 来创建一个默认拖动阴影,该阴影与传递给它的 View 参数大小相同,且触摸点居中在拖动阴影中。

Example

以下示例显示了一个简单的拖放功能,其中使用了 View.setOnLongClickListener() * View.setOnTouchListener() and *View.OnDragEventListener()

Step

Description

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 文件的内容——

<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 并启动它,如果你的安装和应用没有任何问题,那么它将显示以下模拟器窗口——

drag

现在,长按显示的 TutorialsPoint 徽标,你会看到在从其位置长按 1 秒后徽标图像稍微移动,这是你应该开始拖动图像的时间。你可以将它拖动到屏幕周围,然后将其放到一个新位置。

drag1