Android – Kéo và thả (Drag and drop)


Android cung cấp framework kéo/thả (drag/drop) cho phép người dùng chuyển dữ liệu từ một View này sang View khác trong Layout hiện tại bằng cách kéo và thả. Từ API 11 việc kéo và thả (drag và drop)View lên các View khác hoặc các nhóm View được hỗ trợ. Framework có chứa các thành phần cơ bản quan trọng hỗ trợ tính năng drag và drop:

  • Drag event class (lớp Event Drag);
  • Drag listeners;
  • Helper methods and classes (Phương thức và lớp hỗ trợ).

Quá trình Drag/Drop (Drag/Drop Process)

Có 4 bước (hay trạng thái) trong Process Drag/Drop:

  • Started (Đã kích hoạt) −Event này được kích hoạt khi bạn bắt đầu kéo một Item nào đó trong Layout, ứng dụng của bạn sẽ gọi phương thức startDrag() để ra lệnh cho hệ thống kích hoạt việc drag Item. Các tham số (argument) bên trong phương thức startDrag() cung cấp dữ liệu được drag, metadata cho dữ liệu này và một phương thức callback để vẽ bóng cho việc drag. Hệ thống đầu tiên trả lời bằng cách gọi ứng dụng để lấy bóng của việc drag. Sau đó, hệ thống hiển thị bóng lên thiết bị. Kế đến, hệ thống gửi một Event drag với Action kiểu ACTION_DRAG_STARTED tới Event Listener drag đã đăng ký với tất cả các đối tượng View trong Layout hiện tại.Để tiếp tục nhận Event drag, bao gồm Event drop, một Event Listener phải trả về true. Nếu Event Listener drag trả về false thì nó sẽ không nhận Event drag đối với thao tác hiện hành cho tới khi hệ thống gửi một Event drag với một Action kiểu ACTION_DRAG_ENDED.
  • Continuing (Tiếp tục)− Người dùng tiếp tục drag. Hệ thống gửi Action ACTION_DRAG_ENTERED và Action ACTION_DRAG_LOCATION cho Event Listener drag đã đăng ký cho View đang được drag. Listener nay có thể thay đổi cách mà đối tượng View hiển thị trên màn hình (thường là sáng lên). Event Listener drag nhận một Action ACTION_DRAG_EXITED sau khi người dùng đã dời bóng drag ra khỏi Bounding Box của View.
  • Dropped − Người dùng drop Item được drag trong Bounding Box của View. Hệ thống gửi Listener của đối tượng View một Event với Action ACTION_DROP.
  • Ended − Sau khi gửi Action ACTION_DROP, hệ thống gửi một Event drag với Action ACTION_DRAG_ENDED để đánh dấu rằng đã kết thúc thao tác drag.

The DragEvent Class

The DragEvent represents an event that is sent out by the system at various times during a drag and drop operation. This class provides few Constants and important methods which we use during Drag/Drop process.

Constants

Following are all constants integers available as a part of DragEvent class.

Sr.No. Constants & Description
1 ACTION_DRAG_STARTEDSignals the start of a drag and drop operation.
2 ACTION_DRAG_ENTEREDSignals to a View that the drag point has entered the bounding box of the View.
3 ACTION_DRAG_LOCATIONSent to a View after ACTION_DRAG_ENTERED if the drag shadow is still within the View object’s bounding box.
4 ACTION_DRAG_EXITEDSignals that the user has moved the drag shadow outside the bounding box of the View.
5 ACTION_DROPSignals to a View that the user has released the drag shadow, and the drag point is within the bounding box of the View.
6 ACTION_DRAG_ENDEDSignals to a View that the drag and drop operation has concluded.

Methods

Following are few important and most frequently used methods available as a part of DragEvent class.

Sr.No. Constants & Description
1 int getAction()Inspect the action value of this event..
2 ClipData getClipData()Returns the ClipData object sent to the system as part of the call to startDrag().
3 ClipDescription getClipDescription()Returns the ClipDescription object contained in the ClipData.
4 boolean getResult()Returns an indication of the result of the drag and drop operation.
5 float getX()Gets the X coordinate of the drag point.
6 float getY()Gets the Y coordinate of the drag point.
7 String toString()Returns a string representation of this DragEvent object.

Listening for Drag Event

If you want any of your views within a Layout should respond Drag event then your view either implements View.OnDragListener or setup onDragEvent(DragEvent) callback method. When the system calls the method or listener, it passes to them a DragEvent object explained above. You can have both a listener and a callback method for View object. If this occurs, the system first calls the listener and then defined callback as long as listener returns true.

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 in old versions of Android.

Starting a Drag Event

You start with creating 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.

Next either you can extend extend View.DragShadowBuilder to create a drag shadow for dragging the view or simply you can use View.DragShadowBuilder(View) to create 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.

Example

Following example shows the functionality of a simple Drag & Drop using View.setOnLongClickListener(), View.setOnTouchListener()and View.OnDragEventListener().

Step Description
1 You will use Android studio IDE to create an Android application and name it as My Application under a package com.example.saira_000.myapplication. While creating this project, make sure you Target SDK and Compile With at the latest version of Android SDK to use higher levels of APIs.
2 Modify src/MainActivity.java file and add the code to define event listeners as well as a call back methods for the logo image used in the example.
3 Copy image abc.png in res/drawable-* folders. You can use images with different resolution in case you want to provide them for different devices.
4 Modify layout XML file res/layout/activity_main.xml to define default view of the logo images.
5 Run the application to launch Android emulator and verify the result of the changes done in the application.

Following is the content of the modified main activity file src/MainActivity.java. This file can include each of the fundamental lifecycle methods.

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;
            }
         }
      });
   }
   
   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
      // Inflate the menu; this adds items to the action bar if it is present.
      getMenuInflater().inflate(R.menu.menu_main, menu);
      return true;
   }
   
   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
      // Handle action bar item clicks here. The action bar will
      // automatically handle clicks on the Home/Up button, so long
      // as you specify a parent activity in AndroidManifest.xml.
      
      int id = item.getItemId();
      
      //noinspection SimplifiableIfStatement
      if (id == R.id.action_settings) {
         return true;
      }
      return super.onOptionsItemSelected(item);
   }
}

Following will be the content of res/layout/activity_main.xml file −

<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>

Following will be the content of res/values/strings.xml to define two new constants −

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string name="app_name">My Application</string>
   <string name="action_settings">Settings</string>
</resources>

Following is the default content of AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.guidemo"
   android:versionCode="1"
   android:versionName="1.0" >
   
   <uses-sdk
      android:minSdkVersion="11"
      android:targetSdkVersion="22" />
      
   <application
      android:allowBackup="true"
      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
      android:theme="@style/AppTheme" >
      
      <activity
         android:name="com.example.guidemo.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>

Let’s try to run your My Application application. I assume you had created your AVD while doing environment setup. To run the app from Android Studio, open one of your project’s activity files and click Run Eclipse Run Icon icon from the toolbar. Android studio installs the app on your AVD and starts it and if everything is fine with your setup and application, it will display following Emulator window −

Android Drag and Drop

Now do long click on the displayed TutorialsPoint logo and you will see that logo image moves a little after 1 seconds long click from its place, its the time when you should start dragging the image. You can drag it around the screen and drop it at a new location.

2611 Total Views 6 Views Today