میتوانید فرآیند کشیدن و رها کردن خود را در نماها با پاسخ دادن به رویدادهایی که ممکن است باعث شروع کشیدن و پاسخ دادن و رها کردن رویدادها شوند، پیادهسازی کنید.
کشیدن را شروع کنید
کاربر کشیدن را با یک حرکت شروع می کند، معمولاً با لمس یا کلیک کردن و نگه داشتن روی موردی که می خواهد بکشد.
برای انجام این کار در یک View
، یک شی ClipData
و شی ClipData.Item
برای داده های در حال جابجایی ایجاد کنید. به عنوان بخشی از ClipData
، فراداده ای را که در یک شی ClipDescription
در ClipData
ذخیره می شود، عرضه کنید. برای یک عملیات کشیدن و رها کردن که حرکت داده را نشان نمی دهد، ممکن است بخواهید از null
به جای یک شی واقعی استفاده کنید.
برای مثال، این قطعه کد نشان میدهد که چگونه میتوان با ایجاد یک شیء ClipData
که حاوی برچسب (یا برچسب) ImageView
است، به یک حرکت لمسی و نگهداشتن در ImageView
پاسخ داد:
کاتلین
// Create a string for the ImageView label. val IMAGEVIEW_TAG = "icon bitmap" ... val imageView = ImageView(context).apply { // Set the bitmap for the ImageView from an icon bitmap defined elsewhere. setImageBitmap(iconBitmap) tag = IMAGEVIEW_TAG setOnLongClickListener { v -> // Create a new ClipData. This is done in two steps to provide // clarity. The convenience method ClipData.newPlainText() can // create a plain text ClipData in one step. // Create a new ClipData.Item from the ImageView object's tag. val item = ClipData.Item(v.tag as? CharSequence) // Create a new ClipData using the tag as a label, the plain text // MIME type, and the already-created item. This creates a new // ClipDescription object within the ClipData and sets its MIME type // to "text/plain". val dragData = ClipData( v.tag as? CharSequence, arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN), item) // Instantiate the drag shadow builder. We use this imageView object // to create the default builder. val myShadow = View.DragShadowBuilder(view: this) // Start the drag. v.startDragAndDrop(dragData, // The data to be dragged. myShadow, // The drag shadow builder. null, // No need to use local data. 0 // Flags. Not currently used, set to 0. ) // Indicate that the long-click is handled. true } }
جاوا
// Create a string for the ImageView label. private static final String IMAGEVIEW_TAG = "icon bitmap"; ... // Create a new ImageView. ImageView imageView = new ImageView(context); // Set the bitmap for the ImageView from an icon bitmap defined elsewhere. imageView.setImageBitmap(iconBitmap); // Set the tag. imageView.setTag(IMAGEVIEW_TAG); // Set a long-click listener for the ImageView using an anonymous listener // object that implements the OnLongClickListener interface. imageView.setOnLongClickListener( v -> { // Create a new ClipData. This is done in two steps to provide clarity. The // convenience method ClipData.newPlainText() can create a plain text // ClipData in one step. // 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 creates a new ClipDescription object // within the ClipData and sets its MIME type to "text/plain". ClipData dragData = new ClipData( (CharSequence) v.getTag(), new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN }, item); // Instantiate the drag shadow builder. We use this imageView object // to create the default builder. View.DragShadowBuilder myShadow = new View.DragShadowBuilder(imageView); // Start the drag. v.startDragAndDrop(dragData, // The data to be dragged. myShadow, // The drag shadow builder. null, // No need to use local data. 0 // Flags. Not currently used, set to 0. ); // Indicate that the long-click is handled. return true; });
به شروع کشیدن پاسخ دهید
در طول عملیات کشیدن، سیستم رویدادهای کشیدن را به شنوندگان رویداد کشیدن اشیاء View
در طرح فعلی ارسال می کند. شنوندگان با فراخوانی DragEvent.getAction()
واکنش نشان می دهند تا نوع عمل را دریافت کنند. در شروع کشیدن، این روش ACTION_DRAG_STARTED
را برمیگرداند.
در پاسخ به رویدادی با نوع کنش ACTION_DRAG_STARTED
، شنونده رویداد کشیدن باید کارهای زیر را انجام دهد:
DragEvent.getClipDescription()
را فراخوانی کنید و از روش های نوع MIME درClipDescription
برگشتی استفاده کنید تا ببینید آیا شنونده می تواند داده های کشیده شده را بپذیرد یا خیر.اگر عملیات کشیدن و رها کردن حرکت داده را نشان ندهد، ممکن است این کار غیر ضروری باشد.
اگر شنونده رویداد کشیدن بتواند یک قطره را بپذیرد، باید به
true
برگردد تا به سیستم بگوید به ارسال رویدادهای کشیدن به شنونده ادامه دهد. اگر شنونده نتواند قطره ای را بپذیرد، شنونده بایدfalse
را برگرداند و سیستم ارسال رویدادهای کشیدن را برای شنونده متوقف می کند تا زمانی که سیستمACTION_DRAG_ENDED
برای پایان دادن به عملیات کشیدن و رها کردن ارسال کند.
برای یک رویداد ACTION_DRAG_STARTED
، روشهای DragEvent
زیر معتبر نیستند: getClipData()
، getX()
، getY()
و getResult()
.
رویدادها را در حین کشیدن مدیریت کنید
در طول عمل کشیدن، شنوندگان رویداد را بکشید که در پاسخ به ACTION_DRAG_STARTED
رویداد کشیدن، به دریافت رویدادهای کشیدن باز true
. انواع رویدادهای کشیدن که شنونده در حین کشیدن دریافت می کند به محل سایه کشیدن و نمایان بودن View
شنونده بستگی دارد. شنوندگان عمدتاً از رویدادهای کشیدن استفاده می کنند تا تصمیم بگیرند که آیا باید ظاهر View
خود را تغییر دهند یا خیر.
در طول عمل کشیدن، DragEvent.getAction()
یکی از سه مقدار را برمی گرداند:
-
ACTION_DRAG_ENTERED
: شنونده این نوع کنش رویداد را زمانی دریافت میکند که نقطه لمس - نقطه روی صفحه در زیر انگشت یا ماوس کاربر - وارد کادر محدودView
شنونده شود. -
ACTION_DRAG_LOCATION
: هنگامی که شنونده یک رویدادACTION_DRAG_ENTERED
را دریافت کرد، هر بار که نقطه لمسی حرکت میکند تا زمانی که یک رویدادACTION_DRAG_EXITED
را دریافت کند، یک رویدادACTION_DRAG_LOCATION
جدید دریافت میکند. متدهایgetX()
وgetY()
مختصات X و Y نقطه تماس را برمی گرداند. -
ACTION_DRAG_EXITED
: این نوع کنش رویداد برای شنونده ای ارسال می شود که قبلاًACTION_DRAG_ENTERED
دریافت کرده است. این رویداد زمانی ارسال میشود که نقطه لمس سایه کشیدن از داخل کادر محدودView
شنونده به خارج از کادر محدود حرکت کند.
شنونده رویداد drag نیازی به واکنش به هیچ یک از این انواع کنش ندارد. اگر شنونده مقداری را به سیستم برگرداند، نادیده گرفته می شود.
در اینجا چند دستورالعمل برای پاسخ به هر یک از این انواع اقدامات وجود دارد:
- در پاسخ به
ACTION_DRAG_ENTERED
یاACTION_DRAG_LOCATION
، شنونده میتواند ظاهرView
را تغییر دهد تا نشان دهد که نما یک هدف بالقوه سقوط است. - یک رویداد با نوع کنش
ACTION_DRAG_LOCATION
حاوی دادههای معتبری برایgetX()
وgetY()
مربوط به مکان نقطه لمس است. شنونده می تواند از این اطلاعات برای تغییر ظاهرView
در نقطه لمس یا تعیین موقعیت دقیقی که کاربر می تواند محتوا را رها کند استفاده کند. - در پاسخ به
ACTION_DRAG_EXITED
، شنونده باید هرگونه تغییر ظاهری را که در پاسخ بهACTION_DRAG_ENTERED
یاACTION_DRAG_LOCATION
اعمال میکند، بازنشانی کند. این به کاربر نشان می دهد کهView
دیگر یک هدف افت قریب الوقوع نیست.
به یک قطره پاسخ دهید
هنگامی که کاربر سایه کشیدن را روی یک View
رها می کند، و View
قبلاً گزارش می دهد که می تواند محتوای در حال کشیدن را بپذیرد، سیستم یک رویداد کشیدن را با نوع عمل ACTION_DROP
به View
ارسال می کند.
شنونده رویداد کشیدن باید کارهای زیر را انجام دهد:
برای دریافت شیء
ClipData
که در ابتدا در فراخوانیstartDragAndDrop()
ارائه شده است،getClipData()
را فراخوانی کنید و داده ها را پردازش کنید. اگر عملیات کشیدن و رها کردن حرکت داده را نشان ندهد، این غیرضروری است.برای نشان دادن اینکه افت با موفقیت پردازش شده است،
true
را برگردانید، یا اگر اینطور نیست،false
. مقدار برگشتی تبدیل به مقدار بازگشتی توسطgetResult()
برای رویدادACTION_DRAG_ENDED
می شود. اگر سیستم یک رویدادACTION_DROP
ارسال نکند، مقدار بازگردانده شده توسطgetResult()
برای یک رویدادACTION_DRAG_ENDED
false
است.
برای یک رویداد ACTION_DROP
، getX()
و getY()
از سیستم مختصات View
که قطره را دریافت می کند استفاده می کنند تا موقعیت X و Y نقطه لمسی را در لحظه ریزش برگرداند.
در حالی که کاربر میتواند سایه کشیدن را روی View
که شنونده رویداد کشیدن آن رویدادهای کشیدن را دریافت نمیکند، مناطق خالی رابط کاربری برنامه شما یا حتی روی مناطق خارج از برنامه شما آزاد کند، Android رویدادی با نوع کنش ACTION_DROP
ارسال نمیکند. و فقط یک رویداد ACTION_DRAG_ENDED
ارسال خواهد کرد.
به انتهای کشیدن پاسخ دهید
بلافاصله پس از اینکه کاربر سایه کشیدن را آزاد کرد، سیستم یک رویداد کشیدن با نوع عمل ACTION_DRAG_ENDED
را برای همه شنوندگان رویداد کشیدن در برنامه شما ارسال میکند. این نشان می دهد که عملیات کشیدن به پایان رسیده است.
هر شنونده رویداد کشیدن باید کارهای زیر را انجام دهد:
- اگر شنونده در طول عملیات ظاهر خود را تغییر دهد، باید به ظاهر پیش فرض خود بازگردد تا نشانه ای بصری به کاربر مبنی بر اینکه عملیات تمام شده است.
- شنونده میتواند به صورت اختیاری
getResult()
برای کسب اطلاعات بیشتر در مورد عملیات فراخوانی کند. اگر شنونده در پاسخ به رویدادی از نوع اکشنACTION_DROP
true
برگرداند، سپسgetResult()
true
boolean را برمیگرداند. در سایر موارد،getResult()
false
boolean را برمی گرداند، از جمله زمانی که سیستم یک رویدادACTION_DROP
را ارسال نمی کند. - برای نشان دادن تکمیل موفقیت آمیز عملیات drop، شنونده باید Boolean
true
به سیستم برگرداند. با عدم بازگشتfalse
، یک نشانه بصری که سایه در حال بازگشت به منبع خود را نشان می دهد ممکن است به کاربر نشان دهد که عملیات ناموفق بوده است.
پاسخ به رویدادهای کشیدن: یک مثال
همه رویدادهای کشیدن توسط روش رویداد کشیدن یا شنونده شما دریافت می شوند. قطعه کد زیر نمونه ای از پاسخ به رویدادهای کشیدن است:
کاتلین
val imageView = ImageView(this) // Set the drag event listener for the View. imageView.setOnDragListener { v, e -> // Handle each of the expected events. when (e.action) { DragEvent.ACTION_DRAG_STARTED -> { // Determine whether this View can accept the dragged data. if (e.clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { // As an example, apply a blue color tint to the View to // indicate that it can accept data. (v as? ImageView)?.setColorFilter(Color.BLUE) // Invalidate the view to force a redraw in the new tint. v.invalidate() // Return true to indicate that the View can accept the dragged // data. true } else { // Return false to indicate that, during the current drag and // drop operation, this View doesn't receive events again until // ACTION_DRAG_ENDED is sent. false } } DragEvent.ACTION_DRAG_ENTERED -> { // Apply a green tint to the View. (v as? ImageView)?.setColorFilter(Color.GREEN) // Invalidate the view to force a redraw in the new tint. v.invalidate() // Return true. The value is ignored. true } DragEvent.ACTION_DRAG_LOCATION -> // Ignore the event. true DragEvent.ACTION_DRAG_EXITED -> { // Reset the color tint to blue. (v as? ImageView)?.setColorFilter(Color.BLUE) // Invalidate the view to force a redraw in the new tint. v.invalidate() // Return true. The value is ignored. true } DragEvent.ACTION_DROP -> { // Get the item containing the dragged data. val item: ClipData.Item = e.clipData.getItemAt(0) // Get the text data from the item. val dragData = item.text // Display a message containing the dragged data. Toast.makeText(this, "Dragged data is $dragData", Toast.LENGTH_LONG).show() // Turn off color tints. (v as? ImageView)?.clearColorFilter() // Invalidate the view to force a redraw. v.invalidate() // Return true. DragEvent.getResult() returns true. true } DragEvent.ACTION_DRAG_ENDED -> { // Turn off color tinting. (v as? ImageView)?.clearColorFilter() // Invalidate the view to force a redraw. v.invalidate() // Do a getResult() and display what happens. when(e.result) { true -> Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG) else -> Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG) }.show() // Return true. The value is ignored. true } else -> { // An unknown action type is received. Log.e("DragDrop Example", "Unknown action type received by View.OnDragListener.") false } } }
جاوا
View imageView = new ImageView(this); // Set the drag event listener for the View. imageView.setOnDragListener( (v, e) -> { // Handle each of the expected events. switch(e.getAction()) { case DragEvent.ACTION_DRAG_STARTED: // Determine whether this View can accept the dragged data. if (e.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { // As an example, apply a blue color tint to the View to // indicate that it can accept data. ((ImageView)v).setColorFilter(Color.BLUE); // Invalidate the view to force a redraw in the new tint. v.invalidate(); // Return true to indicate that the View can accept the dragged // data. return true; } // Return false to indicate that, during the current drag-and-drop // operation, this View doesn't receive events again until // ACTION_DRAG_ENDED is sent. return false; case DragEvent.ACTION_DRAG_ENTERED: // Apply a green tint to the View. ((ImageView)v).setColorFilter(Color.GREEN); // Invalidate the view to force a redraw in the new tint. v.invalidate(); // Return true. The value is ignored. return true; case DragEvent.ACTION_DRAG_LOCATION: // Ignore the event. return true; case DragEvent.ACTION_DRAG_EXITED: // Reset the color tint to blue. ((ImageView)v).setColorFilter(Color.BLUE); // Invalidate the view to force a redraw in the new tint. v.invalidate(); // Return true. The value is ignored. return true; case DragEvent.ACTION_DROP: // Get the item containing the dragged data. ClipData.Item item = e.getClipData().getItemAt(0); // Get the text data from the item. CharSequence dragData = item.getText(); // Display a message containing the dragged data. Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_LONG).show(); // Turn off color tints. ((ImageView)v).clearColorFilter(); // Invalidate the view to force a redraw. v.invalidate(); // Return true. DragEvent.getResult() returns true. return true; case DragEvent.ACTION_DRAG_ENDED: // Turn off color tinting. ((ImageView)v).clearColorFilter(); // Invalidate the view to force a redraw. v.invalidate(); // Do a getResult() and displays what happens. if (e.getResult()) { Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG).show(); } else { Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG).show(); } // Return true. The value is ignored. return true; // An unknown action type is received. default: Log.e("DragDrop Example","Unknown action type received by View.OnDragListener."); break; } return false; });
یک سایه کشیدن را سفارشی کنید
شما می توانید یک myDragShadowBuilder
سفارشی شده را با نادیده گرفتن متدها در View.DragShadowBuilder
تعریف کنید. قطعه کد زیر یک سایه کوچک، مستطیلی و خاکستری برای TextView
ایجاد می کند:
کاتلین
private class MyDragShadowBuilder(view: View) : View.DragShadowBuilder(view) { private val shadow = ColorDrawable(Color.LTGRAY) // Define a callback that sends the drag shadow dimensions and touch point // back to the system. override fun onProvideShadowMetrics(size: Point, touch: Point) { // Set the width of the shadow to half the width of the original // View. val width: Int = view.width / 2 // Set the height of the shadow to half the height of the original // View. val height: Int = view.height / 2 // The drag shadow is a ColorDrawable. Set its dimensions to // be the same as the Canvas that the system provides. As a result, // the drag shadow fills the Canvas. shadow.setBounds(0, 0, width, height) // Set the size parameter's width and height values. These get back // to the system through the size parameter. size.set(width, height) // Set the touch point's position to be in the middle of the drag // shadow. touch.set(width / 2, height / 2) } // Define a callback that draws the drag shadow in a Canvas that the system // constructs from the dimensions passed to onProvideShadowMetrics(). override fun onDrawShadow(canvas: Canvas) { // Draw the ColorDrawable on the Canvas passed in from the system. shadow.draw(canvas) } }
جاوا
private static class MyDragShadowBuilder extends View.DragShadowBuilder { // The drag shadow image, defined as a drawable object. private static Drawable shadow; // Constructor. public MyDragShadowBuilder(View view) { // Store the View parameter. super(view); // Create a draggable image that fills the Canvas provided by the // system. shadow = new ColorDrawable(Color.LTGRAY); } // Define a callback that sends the drag shadow dimensions and touch point // back to the system. @Override public void onProvideShadowMetrics (Point size, Point touch) { // Define local variables. int width, height; // Set the width of the shadow to half the width of the original // View. width = getView().getWidth() / 2; // Set the height of the shadow to half the height of the original // View. height = getView().getHeight() / 2; // The drag shadow is a ColorDrawable. Set its dimensions to // be the same as the Canvas that the system provides. As a result, // the drag shadow fills the Canvas. shadow.setBounds(0, 0, width, height); // Set the size parameter's width and height values. These get back // to the system through the size parameter. size.set(width, height); // Set the touch point's position to be in the middle of the drag // shadow. touch.set(width / 2, height / 2); } // Define a callback that draws the drag shadow in a Canvas that the system // constructs from the dimensions passed to onProvideShadowMetrics(). @Override public void onDrawShadow(Canvas canvas) { // Draw the ColorDrawable on the Canvas passed in from the system. shadow.draw(canvas); } }