I'm creating drag and drop text view and I need this text view also can be clicked. My code as per below :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_grey"
android:orientation="vertical" >
<GridView
android:id="@+id/gridview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@color/bg_grey"
android:clickable="true"
android:columnWidth="100dp"
android:gravity="center"
android:horizontalSpacing="10dp"
android:numColumns="2"
android:stretchMode="columnWidth"
android:verticalSpacing="10dp" />
<TextView
android:id="@+id/drag_drop_button"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginLeft="255dp"
android:layout_marginTop="155dp"
android:background="@drawable/circle_button"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:gravity="center"
android:text="@string/brand"
android:textColor="@color/sign_blue"
android:textSize="15sp"
android:textStyle="bold" />
</FrameLayout>
class ButtonDragListener implements OnDragListener {
Drawable normalShape = getResources().getDrawable(
R.drawable.circle_button);
@Override
public boolean onDrag(View v, DragEvent event) {
int action = event.getAction();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
// do nothing
break;
case DragEvent.ACTION_DRAG_ENTERED:
// do nothing
break;
case DragEvent.ACTION_DRAG_EXITED:
// do nothing
break;
case DragEvent.ACTION_DROP:
// Dropped, reassign View to ViewGroup
View view = (View) event.getLocalState();
view.setX(event.getX());
view.setY(event.getY());
ViewGroup owner = (ViewGroup) view.getParent();
owner.removeView(view);
FrameLayout container = (FrameLayout) v;
container.addView(view);
view.setVisibility(View.VISIBLE);
break;
case DragEvent.ACTION_DRAG_ENDED:
// do nothing
break;
default:
break;
}
return true;
}
}
private final class ButtonTouchListener implements OnTouchListener {
private static final int MAX_CLICK_DURATION = 200;
private long startClickTime;
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
startClickTime = Calendar.getInstance().getTimeInMillis();
ClipData data = ClipData.newPlainText("", "");
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(
view);
view.startDrag(data, shadowBuilder, view, 0);
view.setVisibility(View.VISIBLE);
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
long clickDuration = Calendar.getInstance().getTimeInMillis()
- startClickTime;
if (clickDuration < MAX_CLICK_DURATION){
view.performClick();
}
}
return true;
}
}
private final class ButtonOnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
Log.e(TAG, "ALOHAAAAA");
}
}
TextView dragDrop = (TextView) view.findViewById(R.id.drag_drop_button);
dragDrop.setAlpha(0.7f);
dragDrop.setOnClickListener(new ButtonOnClickListener());
dragDrop.setOnTouchListener(new ButtonTouchListener());
frameLayout = (FrameLayout) view.findViewById(R.id.root);
frameLayout.setOnDragListener(new ButtonDragListener());
But MotionEvent.ACTION_UP
never be called, I'm also trying to move view.performClick()
inside MotionEvent_ACTION_DOWN
, the click listener calling fine but whenever I'm dragging the text view the click listener also run.
I also have setOnTouchListener
and setOnClickListener
directly from textView but the result is same.
What I need here is smooth drag and drop and also click function. Kindly advise what I'm doing wrong here. Thank you and really appreciate for any kind help.
I have the same problem as you, your solution is not quite helping me, so I came up with this solution, it's based on your previous solution.
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (Math.abs(event.getX() - mX) < MAX_X_MOVE || Math.abs(event.getY() - mY) < MAX_Y_MOVE) {
v.performClick();
} else {
ClipData clipData = ClipData.newPlainText("", "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
v.startDrag(clipData, shadowBuilder, v, 0);
v.setVisibility(View.INVISIBLE);
mX = event.getX();
mY = event.getY();
}
return true;
}
return false;
}
This is a kind of a problem where although there is provided solutions, but also you have to think of how these solutions will fit your needs. For my case I used the ideas above and came up with my own solution that fits my needs.
/**
* Max value of X to move before we declare that we are going to do
* drag.
*/
private static final float MAX_X_MOVE = 120;
/**
* Max value of Y to move before we declare that we are going to do
* drag.
*/
private static final float MAX_Y_MOVE = 120;
private final static int NONE = 0;
private final static int DRAG = 1;
private int m_mode = NONE;
icon.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
m_mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(event.getX()) > MAX_X_MOVE ||
Math.abs(event.getY()) > MAX_Y_MOVE) {
m_mode = DRAG;
_startDragOnFling();
}
break;
case MotionEvent.ACTION_UP:
if(m_mode == DRAG){
break;
}
v.performClick();
break;
}
return true;
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With