آموزش drag/drop در اندروید

آموزش drag/drop در اندروید این امکان را برای کاربران فراهم می کند که در layout فعلی، با استفاده از کشیدن (drag) و رها کردن (drop) ، حالت گرافیکی داده را از یک View به View  دیگر منتقل کنند. (به زبان ساده فریم ورک drag/drop اندروید به کاربران اجازه می دهد که داده ها را از یک نما به نمای دیگری در طرح حاضر جابجا کنند.)
فریم ورک شامل سه مولفه اصلی زیر برای پشتیبانی از قابلیت drag و drop است.

  • کلاس Drag event
  • Drag listeners
  • متد ها و کلاس های کمکی

فرایند کشیدن (Drag) و رها کردن (Drop) در آموزش drag/drop در اندروید:

4 وضعیت در فرایند کشیدن (drag) و رها کردن (drop) وجود دارد :

Started

این رویداد (event ) زمانی اتفاق می افتد که شما شروع به کشیدن (dragging) یک آیتم در صفحه می کنید و اپلیکیشن شما متد ()startDrag را برای اعلام آغاز عملیات drag به سیستم، فراخوانی می کند.
آرگومان های موجود در متد ()startDrag ، داده هایی که باید drag شود، metadata مربوط به این داده ها، و یک callback برای ترسیم مسیر drag  را فراخوانی می کنند.

سیستم ابتدا با فراخوانی اپلیکیشن شما برای گرفتن مسیر drag  به رویداد پاسخ می دهد، سپس مسیر drag را بر روی دستگاه نمایش می دهد.

پس از آن ، سیستم یک رویداد drag با نوع اکشن ACTION_DRAG_STARTED را به شنونده رویداد (event listener) ثبت شده برای تمام اشیا View در Layout فعلی ارسال می کند.

برای ادامه دریافت رویداد های drag ، از جمله یک رویداد امکان پذیرِ drop ، شنونده باید مقدار true را برگرداند. اگر شنونده رویداد drag ، مقدار false را برگرداند، دیگر رویداد های drag عملیات فعلی را دریافت نمی کنند، تا زمانی که سیستم یک رویداد drag را با نوع اکشن ACTION_DRAG_ENDED انتخاب کنند.

Continuing :

 کاربر به drag کردن ادامه می دهد. سیستم ، اکشن ACTION_DRAG_ENTERED و به دنبال آن اکشن ACTION_DRAG_LOCATION را به شنونده رویداد (event listener ) رجیستر شده برای view ای که نقطه drag کردن از آن آغاز می شود، ارسال می کند.
شنونده می تواند انتخاب کند که ظاهر اشیا view خود را در پاسخ به رویداد تغییر دهد یا می تواند با هایلایت کردن view مربوطه، به رویداد واکنش نشان دهد.
شنونده رویداد ، drag یک اکشن ACTION_DRAG_EXITED را بعد از این که کاربر  مسیر drag را از محدوده View خارج کرد، دریافت می کند.

Dropped :

کاربر آیتم کشیده شده را رها می کند. سیستم به شنونده شی view یک رویداد drag را با نوع اکشنACTION_DROP می فرستد.

Ended : 

دقیقا پس از نوع اکشن ACTION_DROP، سیستم برای نشان دادن پایان عملیات drag، یک رویداد drag را با نوع عمل ACTION_DRAG_ENDED ارسال می کند .

کلاس DragEvent

 نشان دهنده رویدادی است که به وسیله سیستم در زمان های مختلف در طول عملیات drag و drop ارسال می شود.
این کلاس دارای تعدادی ثابت و متد مهم است که در طول عملیات drag و drop مورد استفاده قرار می گیرند.

ثوابت

در بخش زیر تمام ثابت های صحیحی که به عنوان بخشی از کلاس DragEvent موجود هستند را می بینید :

شماره ثوابت و توضیحات
1 ACTION_DRAG_STARTED

سیگنال های مبنی بر  آغاز عملیات drag و drop

2 ACTION_DRAG_ENTERED

سیگنالی به یک View مبنی بر این که، نقطه drag کردن وارد محدوده view شده است. 

3 ACTION_DRAG_LOCATION

بعد از اکشن ACTION_DRAG_ENTERED به یک view فرستاده می شود، اگر مسیر drag کردن هنوز در محدوده اشیا view باشد.

4 ACTION_DRAG_EXITED

سیگنالی مبنی بر اینکه کاربر مسیر drag را به خارج از محدوده View منتقل کرده است.

5 ACTION_DROP 

سیگنالی مبنی بر اینکه کاربر مسیر drag کردن را رها کرده است به یک view فرستاده می شود و نقطه drag در محدوده View قرار دارد .

6 ACTION_DRAG_ENDED

سیگنالی به یک View مبنی بر اینکه عملیات drag و drop به پایان رسیده اند فرستاده می شود.

متد ها

در بخش زیر از آموزش drag/drop در اندروید ، مهمترین و کارآمد ترین متد های موجود در کلاس DragEvent ارائه شده اند :

شماره ثوابت و توضیحات
1 ()int getAction

مقدار action رویداد (event) مربوطه را بررسی می کند.

2 ()ClipData getClipData

شی ClipData ارسال شده به سیستم را به عنوان بخشی از فراخوانی به ()startDrag برمی گرداند.

3 ()ClipDescription getClipDescription شی ClipDescription موجود در ClipData را برمی گرداند
4 ()boolean getResult : نشانه ای از نتیجه عملیات drag و drop را برمی گرداند.
5 ()float getX  : مختصات X نقطه drag کردن را می گیرد.
6 ()float getY : مختصات Y نقطه drag کردن را می گیرد.
7 ()String toString : نمایش رشته ای شی DragEvent را بر می گرداند.

گوش کردن به Drag Event

اگر شما بخواهید که هر کدام از View های شما در داخل یک Layout به رویداد drag پاسخ بدهد، آنگاه View شما متد های callback،View.OnDragListener یا (onDragEvent (DragEvent را پیاده سازی می کند.
زمانی که سیستم متد یا شنونده را فراخوانی می کند، یک شی DragEvent که در بالا توصیف شد را به آن ها بر می گرداند. شما می توانید هر دو مورد شنونده و متد callback  را برای شی View داشته باشید. در این صورت سیستم ابتدا شنود کننده را فراخوانی می کند و سپس تا زمانی که شنونده مقدار true را برگرداند، callback تعریف شده را فراخوانی می کند.

ترکیب متد(onDragEvent(DragEvent و View.OnDragListener شبیه به ترکیبی از ()onTouchEvent و View.OnTouchListenerبه کار رفته در مورد رویداد های لمسی در نسخه های قدیمی اندروید است.

آغاز کردن یک رویداد Drag 

شما کار را با ایجاد یک ClipData و ClipData.Item برای داده های منتقل شده آغاز می کنید.
این آیتم به عنوان بخشی از شی ClipData، متا دیتا  را عرضه می کند که در شی ClipDescription درون  ClipData ذخیره می شود. برای عملیات drag و drop ای که شامل انتقال داده نیست، می توانید به جای شی واقعی از null استفاده کنید.

سپس شما می توانید یاView.DragShadowBuilder را برای ایجاد یک مسیر drag کردن برای درگ کردن View آغاز کنید و یا به سادگی از (View.DragShadowBuilder(View برای ایجاد مسیر پیش فرض drag کردن استفاده کنید که به همان سایز آرگومان های View ارسالی به آن ، به مرکزیت نقطه لمس کردن در مسیر drag است.

مثال زیر عملکرد یک عملیات ساده drag و drop را با استفاده از شنونده رویداد View.setOnLongClickListener  همراه با View.OnDragEventListener نشان می دهد.

مرحله توضیحات
1 از  Android studio IDE برای ایجاد یک اپلیکیشن اندرویدی استفاده کنید و آن را با نام My Application تحت پکیج com.example.saira_000.myapplication بنامید.
2 فایل src/MainActivity.java را تغییر دهید و کدهایی را برای تعریف شنوندگان رویداد و همچنین متد های call back مربوط به تصویر لوگوی به کار رفته در مثال را اضافه کنید.
3 تصویر logo.png را در پوشه res/drawable-* کپی کنید. در صورتی که می خواهید تصاویر را برای دستگاه های مختلف به کار ببرید، می توانید از تصاویری با وضوح مختلف استفاده کنید.
4  فایل صفحه بندی XML res/layout/activity_main.xml را برای تعریف view پیش فرض از تصویر لوگو تغییر دهید.
5 برای راه اندازی امولاتور اندروید اپلیکیشن را اجرا کنید و نتایج تغییرات اعمال شده در اپلیکیشن را بررسی کنید.

در بخش زیر محتوای تعییر یافته­ ی فایل اکتیویتی اصلی  src/com.example.dragndropdemo/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>

اکنون می خواهیم اپلیکیشن DragNDropDemo  را اجرا کنیم. من فرض می­کنم که شما AVD خود را در زمان نصب محیط ایجاد کرده باشید. برای اجرای اپلیکیشن از Android Studio ، یکی از فایل های اکتیویتی پروژه ­ی خود را باز کنید و بر روی آیکون RunEclipse Run Icon از نوار ابزار کلیک کنید.Android Studio ، اپلیکیش را بر روی AVD شما نصب می کند و آن را آغاز می کند، و در صورتی که همه­ ی موارد مربوط به تنظیمات و برنامه شما درست باشد، صفحه امولاتور لوگوی انتخابی شما را نشان می دهد و شما می توانید آن را درگ و دراپ کنید.

حالا به مدت طولانی بر روی لوگوی اندروید کلیک کنید، می بینید که لوگو بعد از 1 ثانیه کلیک کردن کمی از جای خود جابجا می شود. اینجا زمانی است که شما باید شروع به کشیدن (drag کردن) تصویر کنید. شما می توانید آن را بر روی صفحه بکشید و در مکانی جدید رها کنید ( دراپ کنید) .