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。

