前言

您是否曾经在任何应用程序中使用过拖放功能?它可能是一些 Web 应用程序或一些 Android 应用程序。以一种或另一种方式,我们都使用了拖放功能。例如:在电子邮件中发送附件时,我们可以将所需文件拖到 Gmail 的撰写编辑器中。同样在 Android 中,我们也都使用过拖放功能。例如:在我们的启动器屏幕中,每个应用程序都出现在我们的移动设备中,我们可以轻松地将应用程序图标从一个地方拖放到另一个地方。

因此,在本文中,我们将学习 android 中拖放的各个方面。在这篇文章中,我们将看到:

  • 拖放过程概述
  • 拖放过程
  • 拖动事件
  • 拖放示例
  • 结论

那么,让我们开始吧。

拖放过程概述

Android中的拖放框架允许您将一个视图拖放到另一个视图,即在一个Activity中,如果您有两个或多个视图,那么您可以使用拖放框架将一个视图表单视图的数据移动到另一个视图安卓。例如:如果你的 Android Activity 有两个 TextView,一个 TextView 在一半,另一个在屏幕的另一半,那么第一个 TextView 的数据可以拖放到另一个 TextView 上。

此片段旨在将数据从一个视图共享到另一个视图,但此片段也可用于将一个 UI 元素与另一个交换或拖放。

为了在您的应用程序中启动拖放过程,您必须在 Android 屏幕上做出一些手势。通过这样做,您的 Android 设备将知道拖放过程将会发生。因此,应用程序将提醒或简单地向系统发送消息,表明某些拖放过程将发生。之后,随着您的手指在屏幕上移动,系统会向拖动事件侦听器对象发送拖动事件,然后调用与拖动事件相关的方法。

您的应用程序使用startDrag()方法告诉系统正在调用拖动事件。此方法还将数据从一个视图发送到其他视图。

拖放过程

在拖放过程中,有四个与之相关的步骤。他们是:

  1. 开始:当用户发送一些手势以指示拖放过程将要发生时,将调用此步骤。您的应用程序将调用startDrag()方法来告诉系统一些拖放过程将要发生。这个startDrag()方法将给出视图的数据以及视图的 UI 元素。之后,系统将事件类型发送到拖动事件监听器,即系统识别将执行哪种类型的拖动并将数据发送到拖动事件监听器。如果您想接收带有相应放置事件的拖动事件,则拖动事件侦听器必须返回true价值。如果是这样,拖动事件侦听器将向系统注册。如果拖动事件监听器返回false,则拖动事件监听器告诉系统它不再对任何类型的拖动事件感兴趣,之后系统将不允许应用程序执行拖动事件。
  2. 继续:如果拖动事件侦听器返回真值,则应用程序可以启动拖动事件。在继续状态下,拖动事件正在执行,即它已经开始但尚未完成。例如:如果要将一个名为 tv1 的 TextView 从位置 1 拖动到位置 2,则位置 1 和位置 2 之间的中间状态处于continue状态。
  3. Dropped:如果用户将某个视图从一个视图拖到另一个视图,则用户可以将视图拖放到屏幕上的任何位置。但这里要注意的是,您可以将拖动的元素拖放到屏幕的任何部分,但只有当您将视图拖放到与拖动事件关联的视图中时,才会调用 drop 事件。例如:如果您有两个视图,即视图 1 和视图 2。View1 有一个可拖动项目,并且拖动事件仅与 view1 相关联,那么如果您将项目放在 view1 中,则将调用放置状态,但如果您将项目放置在 view2 中,则不会调用放置状态因为拖动事件与view2没有关联。
  4. 结束:当用户在拖动后放下项目并调用与该放置状态相关的所有事件时,系统会向应用程序发送一个指示,表明放置操作已结束并调用所有必需的函数。因此,系统指示拖放事件已进入结束状态。

拖动事件

当调用startDrag()函数时,系统会识别当前发生的拖动事件的类型并将拖动事件返回给应用程序。它以DragEvent对象的形式发出拖动事件。getAction()用于识别正在发生的拖动事件类型。拖动事件有六个可能的值。他们是:

  1. ACTION_DRAG_STARTED:这个事件动作类型在startDrag()被调用之后被接收。
  2. ACTION_DRAG_ENTERED:当您进入拖动事件侦听器时收到。如果侦听器返回true,则拖动事件将继续,否则将暂停拖动事件。
  3. ACTION_DRAG_LOCATION:这是在拖动发生时收到的,即用户将项目从一个地方拖动到另一个地方。
  4. ACTION_DRAG_EXITED:当您收到ACTION_DRAG_ENTERED和ACTION_DRAG_LOCATION的值并且用户已将可拖动项目移到边界框外时收到此消息。
  5. ACTION_DROP:当用户放下可拖动项目时收到,之后系统开始调用与拖动事件对应的放置事件。
  6. ACTION_DRAG_ENDED:当拖动动作结束并且系统想要通知应用程序拖放或仅拖动操作已经发生并且与事件关联的函数被调用时接收到。

拖放示例

现在我们完成了理论部分。所以,让我们在我们的应用程序中实现拖放功能。
在这个例子中,我们将一个文本视图从一个地方拖放到另一个边界区域,这个边界区域是我案例中屏幕的一半。

因此,首先为应用程序添加布局文件。将以下代码添加到main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <LinearLayout
            android:layout_width="match_parent"
            android:id="@+id/ll_pinklayout"
            android:orientation="vertical"
            android:layout_height="350dp"
            android:background="#FF8989">
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/tv_dropdrop"
                android:text="Drag Text"
                android:textSize="24sp"
                android:layout_margin="16dp"
                android:textColor="#000000"/>
    </LinearLayout>
</RelativeLayout>

所以,我们完成了布局部分。这将在该 LinearLayout 中添加一个 350dp 高度的 LinearLayout 和一个 TextView。

现在,继续我们的MainActivity.kt部分。以下是代码:

package com.sumit.dragdrop

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.DragEvent
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity: AppCompatActivity(), View.OnTouchListener, View.OnDragListener {
    private val TAG = MainActivity::class.java.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setListeners()
    }
    private fun setListeners() {
        tv_dropdrop.setOnTouchListener(this)
        ll_pinklayout.setOnDragListener(this)
    }
    override fun onDrag(view:View, dragEvent: DragEvent):Boolean {
        Log.d(TAG, "onDrag: view->$view\n DragEvent$dragEvent")
        when (dragEvent.action) {
            DragEvent.ACTION_DRAG_ENDED -> {
                Log.d(TAG, "onDrag: ACTION_DRAG_ENDED ")
                return true
            }
            DragEvent.ACTION_DRAG_EXITED -> {
                Log.d(TAG, "onDrag: ACTION_DRAG_EXITED")
                return true
            }
            DragEvent.ACTION_DRAG_ENTERED -> {
                Log.d(TAG, "onDrag: ACTION_DRAG_ENTERED")
                return true
            }
            DragEvent.ACTION_DRAG_STARTED -> {
                Log.d(TAG, "onDrag: ACTION_DRAG_STARTED")
                return true
            }
            DragEvent.ACTION_DROP -> {
                Log.d(TAG, "onDrag: ACTION_DROP")
                val tvState = dragEvent.localState as View
                Log.d(TAG, "onDrag:viewX" + dragEvent.x + "viewY" + dragEvent.y)
                Log.d(TAG, "onDrag: Owner->" + tvState.parent)
                val tvParent = tvState.parent as ViewGroup
                tvParent.removeView(tvState)
                val container = view as LinearLayout
                container.addView(tvState)
                tvParent.removeView(tvState)
                tvState.x = dragEvent.x
                tvState.y = dragEvent.y
                view.addView(tvState)
                view.setVisibility(View.VISIBLE)
                return true
            }
            DragEvent.ACTION_DRAG_LOCATION -> {
                Log.d(TAG, "onDrag: ACTION_DRAG_LOCATION")
                return true
            }
            else -> return false
        }
    }
    override fun onTouch(view:View, motionEvent: MotionEvent):Boolean {
        Log.d(TAG, "onTouch: view->view$view\n MotionEvent$motionEvent")
        return if (motionEvent.action === MotionEvent.ACTION_DOWN) {
            val dragShadowBuilder = View.DragShadowBuilder(view)
            view.startDrag(null, dragShadowBuilder, view, 0)
            true
        } else {
            false
        }
    }
}

在上面的示例中,我们处理了与拖动事件相关的每个事件。

onTouch()函数在这里用于告诉系统某些拖动操作将由触摸手势处理。通过这样做,每当我们触摸 TextView 时,都会调用 startDrag() 函数,并调用与拖动事件相关的事件,即 ACTION_DRAG_ENDED、ACTION_DRAG_EXITED、ACTION_DROP 和其他事件,并相应地执行操作。

只需运行应用程序并尝试拖放 TextView。另外,尝试将 TextView 放在无界区域中。

结论

在这篇文章中,我们学习了如何在我们的 Android 应用程序中使用拖放功能。我们学习了许多与拖动事件相关的事件。最后,我们做了一个拖放功能的例子。因此,您可以在 Android 中使用上述概念在拖放上构建更多应用程序。