Android拖放
在本教程中,我们将在应用程序中实现Android拖放功能。
Android框架具有用于在应用程序中实现拖放功能的内置机制。
Android拖放
要拖动视图,我们需要先向onTouchListener或者onLongClickListener注册。
我们还需要向该视图添加一个侦听器,以将拖放的视图放置到该视图。
我们将在其上注册一个" onDragListener"。onDragListener接口包含方法onDrag,只要有任何DragEvent事件就会被调用。
以下是在拖放操作期间触发的事件。
ACTION_DRAG_STARTED:一旦用户触摸/单击要拖动的视图,就会在onTouch/onLongClick方法内部调用startDrag方法,从而指示拖动已经开始。
最终在" onDrag"方法内调用" ACTION_DRAG_STARTED"。ACTION_DRAG_ENTERED
:当拖动的视图进入拖放视图的边界时,将触发此事件。ACTION_DRAG_LOCATION
:此事件在触发" ACTION_DRAG_ENTERED"事件后触发,并用于通过" getX()"和" getY()"方法获取拖动视图的当前位置。ACTION_DRAG_EXITED
:当拖动的视图离开放置的视图的边界时触发。ACTION_DROP:当用户释放拖动的视图时触发。
ACTION_DRAG_ENDED
:得出的结论是,拖放操作已结束。
注意:在拖放过程中,被拖动的视图是原始视图的阴影。
原始视图保持原样,并且保持不变。
相反,它的实例是使用DragShadow
类创建的。
因此,我们上面提到的拖动视图实际上是一个拖动阴影。
要将数据从拖动的视图传递到放置的视图,我们使用ClipData。
让我们进入本教程的业务端,在此我们将在应用程序中开发拖放功能。
Android拖放代码
下面给出了" activity_main.xml"布局文件的代码。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.theitroad.draganddrop.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Are you happy with the way drag and drop functionality is taught in this tutorial?" android:textColor="#FFF" android:gravity="center" android:layout_margin="16dp" android:textSize="20sp" android:layout_above="@+id/btnNo" android:layout_centerHorizontal="true" <Button android:id="@+id/btnNo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_margin="8dp" android:tag="NO" android:text="NO" <Button android:id="@+id/btnYes" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_centerVertical="true" android:layout_margin="8dp" android:tag="YES" android:text="YES" <ImageView android:id="@+id/imgDestination" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_above="@+id/textView" android:src="@drawable/circle_border" android:tag="Destination" <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="DROP ABOVE" android:layout_margin="16dp" android:textColor="#FFF" </RelativeLayout>
布局包含两个按钮,这些按钮将用于在ImageView内部拖动。
ImageView显示位于图形" circle_border.xml"中的可绘制形状。
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="https://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@android:color/transparent" <stroke android:width="2dp" android:color="#fff" <size android:width="100dp" android:height="100dp" </shape>
下面给出了MainActivity.java类的代码。
package com.theitroad.draganddrop; import android.content.ClipData; import android.content.ClipDescription; import android.graphics.Color; import android.os.Build; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.DragEvent; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements View.OnTouchListener, View.OnDragListener { Button btnYes, btnNo; ImageView imgDestination; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnYes = findViewById(R.id.btnYes); btnNo = findViewById(R.id.btnNo); imgDestination = findViewById(R.id.imgDestination); btnYes.setOnTouchListener(this); btnNo.setOnTouchListener(this); imgDestination.setOnDragListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { View.DragShadowBuilder mShadow = new View.DragShadowBuilder(v); ClipData.Item item = new ClipData.Item(v.getTag().toString()); String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN}; ClipData data = new ClipData(v.getTag().toString(), mimeTypes, item); switch (v.getId()) { case R.id.btnYes: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { v.startDragAndDrop(data, mShadow, null, 0); } else { v.startDrag(data, mShadow, null, 0); } break; case R.id.btnNo: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { v.startDragAndDrop(data, mShadow, null, 0); } else { v.startDrag(data, mShadow, null, 0); } break; } return false; } @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: ((ImageView) v).setColorFilter(Color.YELLOW); v.invalidate(); return true; case DragEvent.ACTION_DRAG_ENTERED: String clipData = event.getClipDescription().getLabel().toString(); switch (clipData) { case "YES": ((ImageView) v).setColorFilter(ContextCompat.getColor(MainActivity.this, R.color.green), android.graphics.PorterDuff.Mode.MULTIPLY); break; case "NO": ((ImageView) v).setColorFilter(ContextCompat.getColor(MainActivity.this, R.color.colorAccent), android.graphics.PorterDuff.Mode.MULTIPLY); break; } v.invalidate(); return true; case DragEvent.ACTION_DRAG_LOCATION: return true; case DragEvent.ACTION_DRAG_EXITED: ((ImageView) v).clearColorFilter(); ((ImageView) v).setColorFilter(Color.YELLOW); v.invalidate(); return true; case DragEvent.ACTION_DROP: clipData = event.getClipDescription().getLabel().toString(); Toast.makeText(getApplicationContext(),clipData, Toast.LENGTH_SHORT).show(); v.invalidate(); return true; case DragEvent.ACTION_DRAG_ENDED: ((ImageView) v).clearColorFilter(); if (event.getResult()) { Toast.makeText(MainActivity.this, "Awesome!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "Aw Snap! Try dropping it again", Toast.LENGTH_SHORT).show(); } return true; default: return false; } } }
让我们分析一下我们是如何产生以上代码的。
如前所述,我们已经在onTouchListener上注册了要拖动(按钮)的视图,而ImageView是应将其拖放到的视图。
因此,onDragListener和随后的DragEvent被注册到其上。" onTouch"方法是我们传递" ClipData"并创建" DragShadowBuilder"视图的实例的地方(最终将其拖到第一位)。
随着Android Nougat(API 24)的引入,不赞成使用startDrag()方法。
因此,我们对API> = 24使用startDragAndDrop()
方法。startDrag
/startDragAndDrop
方法需要ClipData
以及DragShadow
实例。当拖动开始时,我们使用setColorFilter方法在onDrag方法中的ACTION_DRAG_STARTED案例中的拖放视图上添加背景色。
每个开关案例都"返回true"。
返回false表示不想为该特定DragEvent触发onDrag方法。在" ACTION_DRAG_ENTERED"情况下,我们基于哪个Button进入放置视图范围来更改放置视图的背景色。
按钮的类型由ClipData确定。要检索ClipData,我们将以下方法链接在一起:" event.getClipDescription().getLabel().toString()"。
" event.getResult()"返回一个布尔值,该布尔值确定DragShadow是否被放到了应该放置的范围之内。
v.invalidate()用于强制重绘ImageView。