Android: Drag và Drop trong Android


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Mục lục bài viết:

Trong Android, framework Drag and Drop cho phép người dùng di chuyển view từ nơi này sang nơi khác bằng cách sử dụng thao tác kéo và thả. 

Drag and Drop sẽ bao gồm các chức năng sau để hỗ trợ di chuyển dữ liệu trong các ứng dụng Android.

  • Lớp Drag Event
  • Bộ lắng nghe Drag
  • Các lớp và phương thức Helper

Nói chung, quá trình Kéo và Thả bắt đầu khi người dùng thực hiện các cử chỉ được nhận dạng là tín hiệu để bắt đầu kéo dữ liệu và ứng dụng cho hệ thống biết rằng quá trình kéo đang bắt đầu.

Khi quá trình kéo bắt đầu, hệ thống sẽ gọi lại ứng dụng của ta để nhận trạng thái dữ liệu đang được kéo và nó sẽ gửi các sự kiện kéo đến trình nghe sự kiện kéo hoặc phương thức gọi lại của mỗi Chế độ xem trong Layout.

Quy trình kéo/thả

Trong Android, quy trình kéo và thả có 4 bước hoặc trạng thái như sau:

  • Started
  • Continuing
  • Dropped
  • Ended

Started

Sự kiện này sẽ xảy ra khi chúng ta bắt đầu kéo một view trong bố cục và ứng dụng của chúng ta sẽ gọi  phương thức startDrag() để yêu cầu hệ thống bắt đầu kéo. Các đối số của phương thức startDrag() sẽ cung cấp dữ liệu được kéo, siêu dữ liệu cho dữ liệu này và một lệnh gọi lại để vẽ bóng kéo.

Hệ thống sẽ phản hồi lại ứng dụng của chúng ta để lấy bóng kéo và nó sẽ hiển thị bóng kéo trên thiết bị.

Sau đó, hệ thống sẽ gửi một sự kiện kéo với kiểu hành động ACTION_DRAG_STARTED tới trình nghe sự kiện kéo cho tất cả các đối tượng View trong bố cục hiện tại. Để nhận các sự kiện kéo liên tục, bao gồm cả sự kiện thả có thể xảy ra, trình nghe sự kiện kéo phải trả về true và nó đăng ký trình nghe với hệ thống vì chỉ những trình nghe đã đăng ký mới tiếp tục nhận các sự kiện kéo. Tại thời điểm này, trình nghe cũng có thể thay đổi giao diện của đối tượng View để nó có thể chấp nhận một sự kiện drop.

Trong trường hợp nếu trình xử lý sự kiện kéo trả về false thì nó sẽ không nhận được bất kỳ sự kiện kéo nào cho hoạt động hiện tại cho đến khi hệ thống gửi một sự kiện kéo với loại hành động ACTION_DRAG_ENDED. Bằng cách gửi false, trình nghe sẽ nói với hệ thống rằng nó không quan tâm đến hoạt động kéo và không muốn chấp nhận dữ liệu được kéo.

Continuing

Khi người dùng tiếp tục kéo, bóng kéo sẽ giao với hộp giới hạn của một đối tượng View và hệ thống sẽ gửi một hoặc nhiều sự kiện kéo đến trình lắng nghe sự kiện kéo của đối tượng View (Trong trường hợp nó được đăng ký để nhận sự kiện).

Để đáp lại sự kiện, trình nghe có thể chọn thay đổi diện mạo của đối tượng View. Ví dụ: nếu sự kiện chỉ ra rằng bóng kéo đã đi vào hộp giới hạn của View (loại hành động ACTION_DRAG_ENTERED), thì trình nghe có thể phản ứng bằng cách đánh dấu View của nó.

Dropped

Bất cứ khi nào người dùng cũng có thể thả bóng kéo trong hộp giới hạn của View có thể chấp nhận dữ liệu. Hệ thống sẽ gửi cho trình nghe của đối tượng View một sự kiện kéo với kiểu hành động ACTION_DROP.

Sự kiện kéo sẽ chứa dữ liệu được chuyển đến hệ thống trong khi bắt đầu hoạt động bằng cách sử dụng  phương thức startDrag() và bộ lắng nghe sẽ trả về true cho hệ thống trong trường hợp thả thành công.

Ended

Sau khi hoàn thành loại hành động ACTION_DROP, hệ thống sẽ gửi sự kiện kéo với loại hành động ACTION_DRAG_ENDED để cho biết rằng thao tác kéo đã kết thúc. Điều này được thực hiện bất kể người dùng đã thả bóng kéo ở đâu.

Trình xử lý sự kiện Kéo và phương thức call-back

Đối tượng View nhận các sự kiện kéo từ trình nghe sự kiện kéo triển khai View.OnDragListener hoặc với phương thức gọi lại onDragEvent(DragEvent) của nó. Khi hệ thống gọi một phương thức hoặc trình nghe thì nó sẽ được chuyển đến một đối tượng DragEvent.

Chúng ta có thể sử dụng cả phương thức nghe và gọi lại cho các đối tượng View nhưng trong hầu hết các trường hợp, phương thức nghe được ưu tiên hơn. Trong trường hợp nếu ta sử dụng cả phương thức và trình nghe thì hệ thống trước tiên sẽ gọi trình nghe và sau đó xác định phương thức gọi lại miễn là trình nghe trả về true.

Sự kết hợp của phương thức onDragEvent(DragEvent) và View.OnDragListener tương tự như sự kết hợp của onTouchEvent() và View.OnTouchListener được sử dụng với các sự kiện cảm ứng.

Sự kiện kéo trên Android

Nói chung, hệ thống gửi một đối tượng sự kiện kéo (DragEvent) để thực hiện quá trình kéo/thả và đối tượng DragEvent chứa một loại hành động cho người nghe biết điều gì đang xảy ra trong quá trình kéo/thả.

Trình nghe gọi phương thức getAction() để lấy kiểu hành động từ đối tượng DragEvent. Sau đây là các loại kiểu hành động khác nhau có sẵn trong  đối tượng DragEvent.

Loại hành động Mô tả
ACTION_DRAG_STARTED Trình nghe sự kiện kéo của đối tượng View nhận được sự kiện loại hành động này ngay sau khi ứng dụng gọi startDrag() và nhận được bóng kéo.
ACTION_DRAG_ENTERED Trình nghe sự kiện kéo của đối tượng View nhận được kiểu hành động này khi bóng kéo đã vào hộp giới hạn của View. Đây là sự kiện loại hành động đầu tiên, người nghe nhận được khi bóng kéo đi vào hộp giới hạn.
ACTION_DRAG_LOCATION Trình xử lý sự kiện kéo của đối tượng Chế độ xem nhận sự kiện loại hành động này sau khi nó nhận được sự kiện ACTION_DRAG_ENTERED trong khi bóng kéo vẫn nằm trong hộp giới hạn của Chế độ xem.
ACTION_DRAG_EXITED Trình nghe sự kiện kéo của đối tượng Chế độ xem nhận được sự kiện loại hành động này sau khi nó nhận được một ACTION_DRAG_ENTERED và ít nhất một sự kiện ACTION_DRAG_LOCATION và sau khi người dùng đã di chuyển bóng kéo ra bên ngoài hộp giới hạn của Chế độ xem.
ACTION_DROP Trình nghe sự kiện kéo của đối tượng View nhận sự kiện loại hành động này khi người dùng thả bóng kéo lên đối tượng View.
ACTION_DRAG_ENDED Trình nghe sự kiện kéo của đối tượng View nhận sự kiện kiểu hành động này khi hệ thống kết thúc hoạt động kéo.

Các đối tượng DragEvent cũng chứa các dữ liệu mà ứng dụng của ta cung cấp cho hệ thống trong các lời gọi đến startDrag(). Một số dữ liệu chỉ hợp lệ cho một số loại hành động nhất định.

Drag Shadow (Bóng Kéo)

Trong Android, khi thực hiện thao tác kéo thả, hệ thống sẽ hiển thị một hình ảnh mà người dùng kéo và chúng ta có thể gọi đó là bóng kéo. Trong quá trình di chuyển dữ liệu, hình ảnh được sử dụng để thể hiện dữ liệu đang được kéo.

Bằng cách sử dụng các phương thức đối tượng View.DragShadowBuilder, ta có thể hiển thị hình ảnh của View mà người dùng kéo và sau đó chuyển nó vào hệ thống khi chúng ta bắt đầu kéo bằng  phương thức startDrag(). Là một phần của phản hồi đối với startDrag(), hệ thống sẽ gọi các phương thức mà ta đã xác định trong View.DragShadowBuilder để có được bóng kéo.

Thiết kế thao tác kéo và thả

Sau đây là hướng dẫn từng bước cách bắt đầu sự kiện kéo, cách phản hồi các sự kiện trong quá trình kéo, cách phản hồi sự kiện thả và cách kết thúc thao tác kéo và thả.

Bắt đầu sự kiện kéo

Ta có thể bắt đầu một sự kiện kéo bằng một hành động kéo, thường là một cú nhấn lâu vào đối tượng View. Để đáp lại, ta cần thực hiện những điều sau.

Ta cần tạo ClipData và ClipData.item cho dữ liệu đang được di chuyển. Là một phần của đối tượng ClipData, ta cần gửi siêu dữ liệu được lưu trữ trong đối tượng ClipDescription trong ClipData. Đối với thao tác kéo và thả không biểu thị chuyển động dữ liệu, chúng ta có thể cần sử dụng null thay vì một đối tượng thực tế.

Sau đó, chúng ta cần sử dụng View.DragShadowBuilder(View) để tạo bóng kéo cho các đối tượng View đang được di chuyển. Các View.DragShadowBuilder sẽ tạo ra một bóng kéo mặc định đó là kích thước giống như View được truyền cho nó. Trong trường hợp nếu ta muốn tùy chỉnh bóng kéo, thì ta cần mở rộng chức năng View.DragShadowBuilder dựa trên yêu cầu của ta.

Ví dụ: sau đây là đoạn mã hiển thị cách phản hồi khi nhấn lâu vào đối tượng View bằng cách tạo đối tượng ClipData có chứa thẻ hoặc nhãn của đối tượng View.

@Override
public boolean onLongClick(View v) {

  // Tạo ClipData.Item mới từ thẻ của đối tượng View
  ClipData.Item item = new ClipData.Item((CharSequence) v.getTag());

  // Tạo ClipData mới bằng cách sử dụng thẻ làm nhãn, kiểu MIME văn bản thuần túy và
  // mục đã được tạo. Thao tác này sẽ tạo một đối tượng ClipDescription mới trong
  // ClipData và đặt mục nhập kiểu MIME của nó thành "text/plain"
  String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
  ClipData data = new ClipData(v.getTag().toString(), mimeTypes, item);

  // Khởi tạo trình tạo bóng kéo.
  View.DragShadowBuilder dragshadow = new View.DragShadowBuilder(v);

  // Bắt đầu drag
  v.startDrag(data       // dữ liệu được kéo
    , draggshadow   // kéo đổ bóng
    , v            // dữ liệu cục bộ về thao tác kéo và thả
    , 0          // flags được đặt thành 0 vì hiện tại không sử dụng
  );
  return true;
}

Đây là cách chúng ta có thể phản hồi khi nhấn lâu vào đối tượng View bằng cách tạo đối tượng ClipData trong các ứng dụng android.

Phản hồi sự kiện bắt đầu kéo

Trong quá trình kéo, hệ thống sẽ gửi các sự kiện kéo đến trình nghe sự kiện kéo của các đối tượng View trong bố cục hiện tại. Trình nghe cần phản ứng bằng cách gọi getAction() để nhận loại hành động. Khi bắt đầu kéo, phương thức getAction() trả về ACTION_DRAG_STARTED.

Để phản hồi sự kiện có loại hành động ACTION_DRAG_STARTED, trình nghe sẽ thực hiện những việc sau.

  • Trình nghe sẽ gọi getClipDescription() để lấy chi tiết ClipDescription và nó sử dụng phương thức kiểu MIME trong ClientDescription để quyết định có chấp nhận dữ liệu đang được kéo hay không. Trong trường hợp nếu thao tác kéo và thả không thể hiện bất kỳ chuyển động dữ liệu nào thì không yêu cầu.
  • Nếu trình nghe có thể chấp nhận drop, nó sẽ trả về true. Điều này cho biết rằng hệ thống tiếp tục gửi các sự kiện kéo đến người nghe. Nếu nó không thể chấp nhận thả, nó sẽ trả về false và hệ thống sẽ ngừng gửi các sự kiện kéo cho đến khi gửi ACTION_DRAG_ENDED.

Xử lý sự kiện trong khi kéo

Trong quá trình kéo, người nghe chủ yếu sử dụng các sự kiện kéo như ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED, v.v. để quyết định xem có thay đổi giao diện của Chế độ xem để cho biết rằng nó sắp nhận được một lần thả hay không.

Phản hồi sự kiện thả

Bất cứ khi nào ta thả bóng kéo trên View mà chấp nhận nội dung đang được kéo, thì hệ thống sẽ gửi một sự kiện kéo đến View đó với kiểu hành động ACTION_DROP và trình nghe sẽ thực hiện những việc sau:

  • Trình nghe sẽ gọi getClipData() để lấy đối tượng ClipData ban đầu được cung cấp trong lệnh gọi startDrag() và lưu trữ nó. Nếu thao tác kéo và thả không đại diện cho chuyển động của dữ liệu, điều này có thể không cần thiết.
  • Nó sẽ trả về true hoặc false để cho biết liệu việc thả có được xử lý thành công hay không và nó trả về giá trị từ phương thức getResult() trong  sự kiện ACTION_DRAG_ENDED.

Phản hồi việc kết thúc kéo

Bất cứ khi nào ta thả bóng kéo, hệ thống sẽ gửi một sự kiện kéo tới tất cả các trình nghe sự kiện kéo trong ứng dụng của ta, hành động ACTION_DRAG_ENDED sẽ cho biết rằng thao tác kéo đã kết thúc và trình nghe sẽ thực hiện những việc sau:

  • Trong quá trình hoạt động, trong trường hợp nếu người nghe thay đổi giao diện của đối tượng View, nó phải đặt lại View về giao diện mặc định để cho biết rằng hoạt động đã kết thúc.
  • Trình nghe có thể tùy chọn gọi hàm getResult() để tìm hiểu thêm về hoạt động.

Ví dụ: sau đây là đoạn mã hiển thị cách phản hồi cho các sự kiện kéo trong trình nghe.

// Đây là phương thức mà hệ thống gọi khi nó gửi một sự kiện kéo đến trình nghe.
@Override
public boolean onDrag(View v, DragEvent event) {
  // Tạo một biến để lưu trữ kiểu hành động cho sự kiện đang đến
  int action = event.getAction();
  // Xử lý từng sự kiện mong đợi
  switch (action) {
    case DragEvent.ACTION_DRAG_STARTED:

      // Xác định xem Chế độ xem này có thể chấp nhận dữ liệu được kéo hay không
      if (event.getClipDescription().HasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
        // áp dụng màu xanh lam cho Chế độ xem để biểu thị rằng nó có thể chấp nhận dữ liệu
        v.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
        // Vô hiệu hóa chế độ xem để buộc vẽ lại
        v.invalidate();
        // trả về true để chỉ ra rằng View có thể chấp nhận dữ liệu được kéo.
        return true;
      }
      // Trả về false. Trong thao tác kéo và thả hiện tại, Chế độ xem này sẽ
      // không nhận lại sự kiện cho đến khi ACTION_DRAG_ENDED được gửi.
      return false;

    case DragEvent.ACTION_DRAG_ENTERED:
      // Áp dụng màu VÀNG hoặc bất kỳ màu nào cho Chế độ xem. Trả về true; giá trị trả về bị bỏ qua.
      v.getBackground().setColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_IN);
      // Vô hiệu hóa View để buộc vẽ lại
      v.invalidate();
      return true;


    case DragEvent.ACTION_DRAG_LOCATION:
      // Bỏ qua sự kiện
      return true;
    case DragEvent.ACTION_DRAG_EXITED:
      // Đặt lại sắc thái màu thành xanh lam, nếu bạn đã đặt màu BLUE hoặc bất kỳ màu nào trong ACTION_DRAG_STARTED. Trả về true; giá trị trả về bị bỏ qua.

      v.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
      // Nếu bạn không cung cấp bất kỳ màu nào trong ACTION_DRAG_STARTED thì hãy xóa bộ lọc màu.
      v.getBackground().clearColorFilter();
      // Vô hiệu hóa View để buộc vẽ lại
      v.invalidate();
      return true;
    case DragEvent.ACTION_DROP:
      // Lấy mục chứa dữ liệu kéo
      ClipData.Item item = event.getClipData().GetItemAt(0);
      // Lấy dữ liệu văn bản từ mục.
      String dragData = item.getText().ToString();
      // Hiển thị thông báo chứa dữ liệu được kéo.
      Toast.makeText(this, "Dữ liệu được kéo là " + dragData, Toast.LENGTH_SHORT).show();
      // Tắt mọi sắc thái màu
      v.getBackground().ClearColorFilter();
      // Làm vô hiệu chế độ xem để buộc vẽ lại
      v.invalidate();
      // Trả về true. DragEvent.getResult() sẽ trả về true.
      return true;
    case DragEvent.ACTION_DRAG_ENDED:
      // Tắt mọi hiệu ứng màu
      v.getBackground().ClearColorFilter();
      // Làm mất hiệu lực chế độ xem để buộc vẽ lại
      v.invalidate();
      // Gọi getResult() và hiển thị những gì đã xảy ra.
      if (event.getResult())
        Toast.makeText(this, "Việc thả đã được xử lý.", Toast.LENGTH_SHORT).show();
      else
        Toast.makeText(this, "Việc thả không hoạt động.", Toast.LENGTH_SHORT).show();
      // trả về true; giá trị bị bỏ qua.
      return true;
    // Mặc định đã nhận được một loại hành động không xác định.
    default:
      Log.e("Ví dụ DragDrop", "Loại hành động không xác định được OnDragListener nhận.");
      break;
  }
  return false;
}

Đây là cách ta có thể thực hiện thao tác kéo và thả trong các ứng dụng của mình dựa trên yêu cầu của ta.

Bây giờ chúng ta sẽ xem cách kéo và thả các đối tượng View như button, imageview hoặc  textview, v.v. trong các ứng dụng android chẳng hạn. 

Ví dụ về tính năng kéo và thả trên Android

Sau đây là ví dụ về việc xác định các điều khiển giao diện người dùng như nút, chế độ xem hình ảnh và chế độ xem văn bản trong LinearLayout để triển khai chức năng kéo và thả trong ứng dụng Android.

Tạo một ứng dụng Android mới và đặt tên là DragDropExample.

Bây giờ, hãy mở tệp activity_main.xml từ đường dẫn \res\layout và viết mã như dưới đây:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">
  <LinearLayout
    android:id="@+id/layout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:background="#8ac007"
    android:gravity="center"
    android:orientation="vertical">
    <TextView
      android:id="@+id/lbl"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Draggable Text"
      android:textSize="20sp" android:textColor="#FFFFFF" android:textStyle="bold"/>
    <ImageView
      android:id="@+id/ingvw"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@mipmap/ic_launcher"/>
    <Button
      android:id="@+id/btnDrag"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Draggable Button"/>
  </LinearLayout>
  <LinearLayout
    android:id="@+id/layout2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:background="#ff0000"
    android:gravity="center"
    android:orientation="vertical"/>
  <LinearLayout
    android:id="@+id/layout3"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:background="#0000ff"
    android:gravity="center"
    android:orientation="vertical"/>
</LinearLayout>

Trong đoạn mã trên, ta đã sử dụng LinearLayout với các điều khiển giao diện người dùng bắt buộc để thực hiện thao tác kéo và thả.

Khi ta đã hoàn tất việc tạo bố cục với các điều khiển bắt buộc, ta cần tải tài nguyên bố cục XML từ phương thức callback onCreate() trên activity của ta

Dưới đây là file MainActivity.java.

MainActivity.java

package v1study.com.dragdropv1study1;

import android.content.ClipData;
import android.content.ClipDescription;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class DragDrop extends AppCompatActivity implements View.OnDragListener, View.OnLongClickListener {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.dragdrop);
    //Find all views and set Tag to all draggable views
    TextView txtVw = (TextView) findViewById(R.id.lbl);
    txtVw.setTag("DRAGGABLE TEXTVIEW");
    txtVw.setOnLongClickListener(this);
    ImageView imgVw = (ImageView) findViewById(R.id.ingvw);
    imgVw.setTag("ANDROID ICON");
    imgVw.setOnLongClickListener(this);
    Button btn = (Button) findViewById(R.id.btnDrag);
    btn.setTag("DRAGGABLE BUTTON");
    btn.setOnLongClickListener(this);
    //Set Drag Event Listeners for defined layouts
    findViewById(R.id.layout1).setOnDragListener(this);
    findViewById(R.id.layout2).setOnDragListener(this);
    findViewById(R.id.layout3).setOnDragListener(this);
  }

  @Override
  public boolean onLongClick(View v) {
    // Create a new ClipData.Item from the ImageView object's tag
    ClipData.Item item = new ClipData.Item((CharSequence) 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"
    String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
    ClipData data = new ClipData(v.getTag().toString(), mimeTypes, item);
    // Instantiates the drag shadow builder.
    View.DragShadowBuilder dragshadow = new View.DragShadowBuilder(v);
    // Starts the drag
    v.startDrag(data        // data to be dragged
      , dragshadow   // drag shadow builder
      , v           // local data about the drag and drop operation
      , 0          // flags (not currently used, set to 0)
    );
    return true;
  }

  // This is the method that the system calls when it dispatches a drag event to the listener.
  @Override
  public boolean onDrag(View v, DragEvent event) {
    // Defines a variable to store the action type for the incoming event
    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
        if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
          // if you want to apply color when drag started to your view you can uncomment below lines
          // to give any color tint to the View to indicate that it can accept data.
          // v.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
          // 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.
          return true;
        }
        // Returns false. During the current drag and drop operation, this View will
        // not receive events again until ACTION_DRAG_ENDED is sent.
        return false;

      case DragEvent.ACTION_DRAG_ENTERED:
        // Applies a GRAY or any color tint to the View. Return true; the return value is ignored.
        v.getBackground().setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN);
        // Invalidate the view to force a redraw in the new tint
        v.invalidate();
        return true;

      case DragEvent.ACTION_DRAG_LOCATION:
        // Ignore the event
        return true;

      case DragEvent.ACTION_DRAG_EXITED:
        // Re-sets the color tint to blue. Returns true; the return value is ignored.
        // view.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);
        //It will clear a color filter .
        v.getBackground().clearColorFilter();
        // Invalidate the view to force a redraw in the new tint
        v.invalidate();
        return true;

      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.
        String dragData = item.getText().toString();
        // Displays a message containing the dragged data.
        Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_SHORT).show();
        // Turns off any color tints
        v.getBackground().clearColorFilter();
        // Invalidates the view to force a redraw
        v.invalidate();

        View vw = (View) event.getLocalState();
        ViewGroup owner = (ViewGroup) vw.getParent();
        owner.removeView(vw); //remove the dragged view
        //caste the view into LinearLayout as our drag acceptable layout is LinearLayout
        LinearLayout container = (LinearLayout) v;
        container.addView(vw);//Add the dragged view
        vw.setVisibility(View.VISIBLE);//finally set Visibility to VISIBLE
        // Returns true. DragEvent.getResult() will return true.
        return true;

      case DragEvent.ACTION_DRAG_ENDED:
        // Turns off any color tinting
        v.getBackground().clearColorFilter();
        // Invalidates the view to force a redraw
        v.invalidate();
        // Does a getResult(), and displays what happened.
        if (event.getResult())
          Toast.makeText(this, "The drop was handled.", Toast.LENGTH_SHORT).show();
        else
          Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_SHORT).show();
        // returns true; the value is ignored.
        return true;
      // An unknown action type was received.
      default:
        Toast.makeText(this, "The drop1 didn't work.", Toast.LENGTH_SHORT).show();
//        Log.e("DragDrop Example", "Unknown action type received by OnDragListener.");
        break;
    }
    return false;
  }
}

Trong đoạn mã trên, ta đã đăng ký các đối tượng View và triển khai các sự kiện nhấn lâu cần thiết để triển khai chức năng kéo và thả.

Dưới đây là ảnh thể hiện kết quả giao diện layout:

Bạn nhấn và giữ phím trái chuột một lúc (long press) thì sẽ thực hiện được thao tác kéo và thả.

Chúc các bạn thành công!

» Tiếp: Gửi Email
« Trước: UI Layout trong Android
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!