Android drag and drop on dynamically created views - java

could really do with some help.
I have a random number of buttons being created on the fly and each button is setOnTouchListener, which looks to be working fine. The buttons are added to Linearlayout (Top) and there is another empty Linearlayout (Bottom).
I want to be able to do the following:
Drag & Drop buttons between Linearlayout (Top) & Linearlayout (Bottom)
public class GetString extends Activity{
myDragEventListener mDragListen = new myDragEventListener();
public void getString(Context context, String[] temp, int no, LinearLayout words1, LinearLayout words2) {
Button bt;
int i = 0;
for (i = 0; i < no; i++) {
bt = new Button(context);
bt.setId(i);
bt.setText(temp[i]);
bt.setOnTouchListener(new MyTouchListener());
words1.addView(bt);
bt.setOnDragListener(mDragListen);
}
}
protected class myDragEventListener implements View.OnDragListener {
public boolean onDrag(View v, DragEvent event) {
// Defines a variable to store the action type for the incoming event
final int action = event.getAction();
// Handles each of the expected events
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
return false;
case DragEvent.ACTION_DRAG_ENTERED:
v.invalidate();
return true;
case DragEvent.ACTION_DRAG_LOCATION:
return true;
case DragEvent.ACTION_DRAG_EXITED:
v.invalidate();
return true;
case DragEvent.ACTION_DROP:
return true;
case DragEvent.ACTION_DRAG_ENDED:
return true;
// An unknown action type was received.
default:
Log.e("DragDrop Example", "Unknown action type received by OnDragListener.");
break;
}
return false;
}
}
public class MyTouchListener implements View.OnTouchListener {
#Override
public boolean onTouch(View v, MotionEvent arg1) {
ClipData data = ClipData.newPlainText("", "");
View.DragShadowBuilder shadow = new View.DragShadowBuilder(v);
v.startDrag(data, shadow, null, 0);
return false;
}
}
}

Related

ViewPager Disable Swiping

I have a ViewPager which has 3 fragments. Fragment A on the left, B in the middle and C on the right. Fragment C has a ListView which fills the whole width of the screen. I implemented a swipe listener on my ListView items using the following code:
SWIPE DETECTOR :
public class SwipeDetector implements View.OnTouchListener {
public static enum Action {
LR, // Left to Right
RL, // Right to Left
TB, // Top to bottom
BT, // Bottom to Top
None // when no action was detected
}
private static final String logTag = "SwipeDetector";
private static final int MIN_DISTANCE = 100;
private static final int VERTICAL_MIN_DISTANCE = 80;
private static final int HORIZONTAL_MIN_DISTANCE = 80;
private float downX, downY, upX, upY;
private Action mSwipeDetected = Action.None;
public boolean swipeDetected() {
return mSwipeDetected != Action.None;
}
public Action getAction() {
return mSwipeDetected;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
downX = event.getX();
downY = event.getY();
mSwipeDetected = Action.None;
return false; // allow other events like Click to be processed
}
case MotionEvent.ACTION_MOVE: {
upX = event.getX();
upY = event.getY();
float deltaX = downX - upX;
float deltaY = downY - upY;
// horizontal swipe detection
if (Math.abs(deltaX) > HORIZONTAL_MIN_DISTANCE) {
// left or right
if (deltaX < 0) {
// Log.i(logTag, "Swipe Left to Right");
mSwipeDetected = Action.LR;
return true;
}
if (deltaX > 0) {
// Log.i(logTag, "Swipe Right to Left");
mSwipeDetected = Action.RL;
return true;
}
} else
// vertical swipe detection
if (Math.abs(deltaY) > VERTICAL_MIN_DISTANCE) {
// top or down
if (deltaY < 0) {
Log.i(logTag, "Swipe Top to Bottom");
mSwipeDetected = Action.TB;
return false;
}
if (deltaY > 0) {
Log.i(logTag, "Swipe Bottom to Top");
mSwipeDetected = Action.BT;
return false;
}
}
return true;
}
}
return false;
}}
I use it in the following way :
listView.setOnTouchListener(swipeDetector);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
final int position, long id) {
Log.d("CLICKED", "CLICKED");
if (swipeDetector.swipeDetected()) {
Log.d("SWIPING", "SWIPING");
Log.d("ACTION", swipeDetector.getAction().toString());
final Button del = (Button) view.findViewById(R.id.delete_button);
if (swipeDetector.getAction() == SwipeDetector.Action.LR) {
Log.d("LEFT TO RIGHT", "Left to right");
This works perfectly fine with Activities. However, the problem now is that when I swipe, it assumes I am swiping in the ViewPager and takes me back to the middle fragment. Is there a way to disable the ViewPager swiping on this ListView or change the focus so that this works?

drag and drop (checkmark if I drag the right image) android

If I drag a countryTypical image and drop in imageCountry[n9] the application set the checkmark image on the right countryTypical image.
It happens if I drag the right countryTypical image and also if I drag the mistaken countryTypical image. I would like to correct the error.
Many thanks...
String[] countryNames = {
"Italy", "France", "Spain", "Germany", "Belgium", "Portugal", "Switzerland", "Great Britain"
};
int[] countryImages = {
R.drawable.italy,
R.drawable.france,
R.drawable.spain,...
};
int[] countryFlags = {
R.drawable.italian_flag,
R.drawable.french_flag,
R.drawable.spanish_flag,....
};
In my OnCreate:
countryTypical1.setImageResource(countryFlags[n1]);
countryTypical2.setImageResource(countryFlags[n2]);
countryTypical3.setImageResource(countryFlags[n3]);
countryTypical4.setImageResource(countryFlags[n4]);
countryTypical5.setImageResource(countryFlags[n5]);
countryTypical6.setImageResource(countryFlags[n6]);
countryTypical7.setImageResource(countryFlags[n7]);
countryTypical8.setImageResource(countryFlags[n8]);
countryTypical1.setOnTouchListener(dragClick);
countryTypical2.setOnTouchListener(dragClick);
countryTypical3.setOnTouchListener(dragClick);
countryTypical4.setOnTouchListener(dragClick);
countryTypical5.setOnTouchListener(dragClick);
countryTypical6.setOnTouchListener(dragClick);
countryTypical7.setOnTouchListener(dragClick);
countryTypical8.setOnTouchListener(dragClick);
imageCountry.setOnDragListener(DropListener);
View.OnTouchListener dragClick = new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
DragShadow dragShadow = new DragShadow(view);
ClipData dragData = ClipData.newPlainText("", "");
view.startDrag(dragData, dragShadow, view,0);
return true;
}
return true;
}
};
private class DragShadow extends View.DragShadowBuilder{
public DragShadow(View view) {
super(view);
}
#Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
View view = getView();
int height = view.getHeight();
int width = view.getWidth();
shadowSize.set(width, height);
shadowTouchPoint.set(width, height);
}
#Override
public void onDrawShadow(Canvas canvas) {
getView().draw(canvas);
}
}
View.OnDragListener DropListener = new View.OnDragListener() {
#Override
public boolean onDrag(View view, DragEvent event) {
assignDropTrue(countryTypical1, event, n1);
assignDropTrue(countryTypical2, event, n2);
assignDropTrue(countryTypical3, event, n3);
assignDropTrue(countryTypical4, event, n4);
assignDropTrue(countryTypical5, event, n5);
assignDropTrue(countryTypical6, event, n6);
assignDropTrue(countryTypical7, event, n7);
assignDropTrue(countryTypical8, event, n8);
return true;
}
};
private void assignDropTrue(ImageView countryTypical, DragEvent event, int n1){
if (n9 == n1){
int dragEvent = event.getAction();
switch (dragEvent) {
case DragEvent.ACTION_DROP:
countryTypical.setImageResource(R.drawable.vcheckmark);
}}
}
I wrote a sample just to give you an idea behind the logic I propose (a.k.a it's not tested)
View.OnDragListener DropListener = new View.OnDragListener() {
#Override
public boolean onDrag(View view, DragEvent event) {
int action = event.getAction();
View draggedView = (View) event.getLocalState();
//view is the country where the flag was dropped, draggedView is the flag you dropped
//below we check if the correct flag is dropped in the correct country
switch (event.getAction()){
case DragEvent.ACTION_DROP:
for (int i; i< countryImages.length(); i++){
if (draggedView.getId() == countryFlags[i] && view.getId()== countryImages[i])
draggedView.setImageResource(R.drawable.vcheckmark);
}
break;
default:
break;
}
return true;
}
};

Drag and drop game in Android

I have a drag and drop game. The user should choose the correct shape to drag and drop onto the box. I followed this tutorial here When the user has dragged the wrong object, it won't be displaying WRONG ANSWER yet. The warning should displayed when he drops it onto the basket object. I have tried this:
TextView objBasket, tx, timer;
int trial = 0;
TextView obj[] = new TextView[4];
int[] images = {
R.drawable.stage4_object1, // Correct Answer a
R.drawable.stage4_object2,
R.drawable.stage4_object3,
R.drawable.stage4_object4
};
List<TextView> iv = new ArrayList<TextView>();
String[] tagList = {"a","b","c","d"};
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.stage4_1);
// I created a custom countdown timer c/o Say
counter = new MyCount(30000,1000);
counter.start();
initControls();
getCorrectObject();
}
private void getCorrectObject() {
// TODO Auto-generated method stub
List<Integer> objects = new ArrayList<Integer>();
for(int arr : images){
objects.add(arr);
}
// Shuffle the collection
Collections.shuffle(objects);
iv.add((TextView) findViewById(R.id.txtStage4_object1));
iv.add((TextView) findViewById(R.id.txtStage4_object2));
iv.add((TextView) findViewById(R.id.txtStage4_object3));
iv.add((TextView) findViewById(R.id.txtStage4_object4));
Collections.shuffle(iv);
iv.get(0).setBackgroundResource(images[0]);
iv.get(1).setBackgroundResource(images[1]);
iv.get(2).setBackgroundResource(images[2]);
iv.get(3).setBackgroundResource(images[3]);
iv.get(0).setOnTouchListener(new ChoiceTouchListener());
iv.get(1).setOnTouchListener(new ChoiceTouchListener());
iv.get(2).setOnTouchListener(new ChoiceTouchListener());
iv.get(3).setOnTouchListener(new ChoiceTouchListener());
for (int i = 0; i < tagList.length; i++) {
iv.get(i).setTag(tagList[i]);
}
}
#SuppressLint("NewApi")
private class ChoiceTouchListener implements OnTouchListener {
#SuppressLint("NewApi")
#Override
public boolean onTouch(View v, MotionEvent motionEvent) {
// TODO Auto-generated method stub
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
//setup drag
ClipData data = ClipData.newPlainText("", "");
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
//start dragging the item touched
v.startDrag(data, shadowBuilder, v, 0);
return true;
}
else {
return false;
}
}
}
#SuppressLint("NewApi")
private class ChoiceDragListener implements OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
//no action necessary
break;
case DragEvent.ACTION_DRAG_ENTERED:
//no action necessary
break;
case DragEvent.ACTION_DRAG_EXITED:
//no action necessary
break;
case DragEvent.ACTION_DROP:
//handle the dragged view being dropped over a drop view
View view = (View) event.getLocalState();
//stop displaying the view where it was before it was dragged
view.setVisibility(View.INVISIBLE);
//view dragged item is being dropped on
TextView dropTarget = (TextView) v;
//view being dragged and dropped
TextView dropped = (TextView) view;
for (int i = 0; i < iv.size(); i++) {
final int k = i;
String tmp = "a";
if (tmp.equals(iv.get(k).getTag())) {
Log.i("result","CORRECT ANSWER: "+ tmp);
goToNextQuestion();
} else {
Log.i("result","WRONG ANSWER: "+ iv.get(k).getTag());
trial++;
guessedWrong();
playWrongSound();
}
}
break;
case DragEvent.ACTION_DRAG_ENDED:
//no action necessary
break;
default:
break;
}
return true;
}
}
But it won't go to the next question whenever I choose the correct object. What am I missing in here? I really need help to finish this game. Thanks in advance.
I think that I see two problems with your program.
You created your ChoiceDragListener class but you didn't register it with any of those items you wish to drag around. This appears to be missing from your getCorrectObject method.
iv.get(0).setOnDragListener(new ChoiceDragListener ());
iv.get(1).setOnDragListener(new ChoiceDragListener ());
iv.get(2).setOnDragListener(new ChoiceDragListener ());
iv.get(3).setOnDragListener(new ChoiceDragListener ());
The other problem that I see is with your ACTION_DROP case from your switch statement. First you get the TextView that the user has dropped but then you begin to cycle through all of your text views anyways. The code inside the ACTION_DROP case should look more like:
View view = (View) event.getLocalState();
view.setVisibility(View.INVISIBLE);
TextView dropTarget = (TextView) v;
TextView dropped = (TextView) view;
String temp = "a";
if(temp.equals(view.getTag()){
goToNextQuestion();
}else{
guessedWrong()
}

onOptionsItemSelected(MenuItem item) strange behavior

I've created a menu with a intents to access different activities, but I have a strange behavior, it always goes through all the cases of the switch statement after the statement selected , I've reviewed the value of the variable item and is correct, any ideas what could be wrong?
the snippet of code that represents the menu is:
public static final int wiifidi = 0;
public static final int cuentaint = 1;
public static final int cajerosint = 2;
public static final int indicadoresint = 3;
public static final int promoint = 5;
public static final int contactoint = 4;
....
....
....
#Override
//add the items to the menu on the class
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0,wiifidi, 0, R.string.menu_wifi);
menu.add(0,cuentaint, 0, R.string.menu_cuenta);
menu.add(0,cajerosint,0,R.string.menu_cajeros);
menu.add(0,indicadoresint,0,R.string.menu_indicadores);
menu.add(0,contactoint,0,R.string.menu_contacto);
menu.add(0,promoint,0,R.string.menu_promo);
return result;
}
#Override
//handle everything that happens when an item of menu is selected
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(getApplicationContext(), "el item es " +item.getItemId(), Toast.LENGTH_LONG).show();
switch (item.getItemId()) {
case wiifidi:
wifistatus();
case cuentaint:{
consulta();
}
case cajerosint:{
cajero();
}
case indicadoresint:{
indicador();
}
case contactoint:{
contacto();
}
case promoint:{
promocion();
}
}
return super.onOptionsItemSelected(item);
}
Remember to break out of your switches.
switch (item.getItemId())
{
case wiifidi:
wifistatus();
break;
case cuentaint:
consulta();
break;
case cajerosint:
cajero();
break;
case indicadoresint:
indicador();
break;
case contactoint:
contacto();
break;
case promoint:
promocion();
break;
}
Specify break
case wiifidi:
wifistatus();
break;

Detecting a long press in Android

I am currently using onTouchEvent(MotionEvent event) { } to detect when the user presses my glSurfaceView is there a way to detect when a long click is made.
I'm guessing if I can't find much in the dev docs then it will be some sort of work around method. Something like registering ACTION_DOWN and seeing how long it is before ACTION_UP.
How do you detect long presses on Android using opengl-es?
GestureDetector is the best solution.
Here is an interesting alternative. In onTouchEvent on every ACTION_DOWN schedule a Runnable to run in 1 second. On every ACTION_UP or ACTION_MOVE, cancel scheduled Runnable. If cancelation happens less than 1s from ACTION_DOWN event, Runnable won't run.
final Handler handler = new Handler();
Runnable mLongPressed = new Runnable() {
public void run() {
Log.i("", "Long press!");
}
};
#Override
public boolean onTouchEvent(MotionEvent event, MapView mapView){
if(event.getAction() == MotionEvent.ACTION_DOWN)
handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout());
if((event.getAction() == MotionEvent.ACTION_MOVE)||(event.getAction() == MotionEvent.ACTION_UP))
handler.removeCallbacks(mLongPressed);
return super.onTouchEvent(event, mapView);
}
Try this:
final GestureDetector gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent e) {
Log.e("", "Longpress detected");
}
});
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
};
I have a code which detects a click, a long click and movement.
It is fairly a combination of the answer given above and the changes i made from peeping into every documentation page.
//Declare this flag globally
boolean goneFlag = false;
//Put this into the class
final Handler handler = new Handler();
Runnable mLongPressed = new Runnable() {
public void run() {
goneFlag = true;
//Code for long click
}
};
//onTouch code
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
handler.postDelayed(mLongPressed, 1000);
//This is where my code for movement is initialized to get original location.
break;
case MotionEvent.ACTION_UP:
handler.removeCallbacks(mLongPressed);
if(Math.abs(event.getRawX() - initialTouchX) <= 2 && !goneFlag) {
//Code for single click
return false;
}
break;
case MotionEvent.ACTION_MOVE:
handler.removeCallbacks(mLongPressed);
//Code for movement here. This may include using a window manager to update the view
break;
}
return true;
}
I confirm it's working as I have used it in my own application.
I have created a snippet - inspired by the actual View source - that reliably detects long clicks/presses with a custom delay. But it's in Kotlin:
val LONG_PRESS_DELAY = 500
val handler = Handler()
var boundaries: Rect? = null
var onTap = Runnable {
handler.postDelayed(onLongPress, LONG_PRESS_DELAY - ViewConfiguration.getTapTimeout().toLong())
}
var onLongPress = Runnable {
// Long Press
}
override fun onTouch(view: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
boundaries = Rect(view.left, view.top, view.right, view.bottom)
handler.postDelayed(onTap, ViewConfiguration.getTapTimeout().toLong())
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
handler.removeCallbacks(onLongPress)
handler.removeCallbacks(onTap)
}
MotionEvent.ACTION_MOVE -> {
if (!boundaries!!.contains(view.left + event.x.toInt(), view.top + event.y.toInt())) {
handler.removeCallbacks(onLongPress)
handler.removeCallbacks(onTap)
}
}
}
return true
}
When you mean user presses, do you mean a click? A click is when the user presses down and then immediately lifts up finger. Therefore it is encompassing two onTouch Events. You should save the use of onTouchEvent for stuff that happens on the initial touch or the after release.
Thus, you should be using onClickListener if it is a click.
Your answer is analogous: Use onLongClickListener.
The solution by MSquare works only if you hold a specific pixel, but that's an unreasonable expectation for an end user unless they use a mouse (which they don't, they use fingers).
So I added a bit of a threshold for the distance between the DOWN and the UP action in case there was a MOVE action inbetween.
final Handler longPressHandler = new Handler();
Runnable longPressedRunnable = new Runnable() {
public void run() {
Log.e(TAG, "Long press detected in long press Handler!");
isLongPressHandlerActivated = true;
}
};
private boolean isLongPressHandlerActivated = false;
private boolean isActionMoveEventStored = false;
private float lastActionMoveEventBeforeUpX;
private float lastActionMoveEventBeforeUpY;
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
longPressHandler.postDelayed(longPressedRunnable, 1000);
}
if(event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
if(!isActionMoveEventStored) {
isActionMoveEventStored = true;
lastActionMoveEventBeforeUpX = event.getX();
lastActionMoveEventBeforeUpY = event.getY();
} else {
float currentX = event.getX();
float currentY = event.getY();
float firstX = lastActionMoveEventBeforeUpX;
float firstY = lastActionMoveEventBeforeUpY;
double distance = Math.sqrt(
(currentY - firstY) * (currentY - firstY) + ((currentX - firstX) * (currentX - firstX)));
if(distance > 20) {
longPressHandler.removeCallbacks(longPressedRunnable);
}
}
}
if(event.getAction() == MotionEvent.ACTION_UP) {
isActionMoveEventStored = false;
longPressHandler.removeCallbacks(longPressedRunnable);
if(isLongPressHandlerActivated) {
Log.d(TAG, "Long Press detected; halting propagation of motion event");
isLongPressHandlerActivated = false;
return false;
}
}
return super.dispatchTouchEvent(event);
}
The idea is creating a Runnable for execute long click in a future, but this execution can be canceled because of a click, or move.
You also need to know, when long click was consumed, and when it is canceled because finger moved too much. We use initialTouchX & initialTouchY for checking if the user exit a square area of 10 pixels, 5 each side.
Here is my complete code for delegating Click & LongClick from Cell in ListView to Activity with OnTouchListener:
ClickDelegate delegate;
boolean goneFlag = false;
float initialTouchX;
float initialTouchY;
final Handler handler = new Handler();
Runnable mLongPressed = new Runnable() {
public void run() {
Log.i("TOUCH_EVENT", "Long press!");
if (delegate != null) {
goneFlag = delegate.onItemLongClick(index);
} else {
goneFlag = true;
}
}
};
#OnTouch({R.id.layout})
public boolean onTouch (View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout());
initialTouchX = motionEvent.getRawX();
initialTouchY = motionEvent.getRawY();
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
if (Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
handler.removeCallbacks(mLongPressed);
return true;
}
return false;
case MotionEvent.ACTION_UP:
handler.removeCallbacks(mLongPressed);
if (goneFlag || Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
goneFlag = false;
return true;
}
break;
}
Log.i("TOUCH_EVENT", "Short press!");
if (delegate != null) {
if (delegate.onItemClick(index)) {
return false;
}
}
return false;
}
ClickDelegateis an interface for sending click events to the handler class like an Activity
public interface ClickDelegate {
boolean onItemClick(int position);
boolean onItemLongClick(int position);
}
And all what you need is to implement it in your Activity or parent Viewif you need to delegate the behavior:
public class MyActivity extends Activity implements ClickDelegate {
//code...
//in some place of you code like onCreate,
//you need to set the delegate like this:
SomeArrayAdapter.delegate = this;
//or:
SomeViewHolder.delegate = this;
//or:
SomeCustomView.delegate = this;
#Override
public boolean onItemClick(int position) {
Object obj = list.get(position);
if (obj) {
return true; //if you handle click
} else {
return false; //if not, it could be another event
}
}
#Override
public boolean onItemLongClick(int position) {
Object obj = list.get(position);
if (obj) {
return true; //if you handle long click
} else {
return false; //if not, it's a click
}
}
}
setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
longClick = false;
x1 = event.getX();
break;
case MotionEvent.ACTION_MOVE:
if (event.getEventTime() - event.getDownTime() > 500 && Math.abs(event.getX() - x1) < MIN_DISTANCE) {
longClick = true;
}
break;
case MotionEvent.ACTION_UP:
if (longClick) {
Toast.makeText(activity, "Long preess", Toast.LENGTH_SHORT).show();
}
}
return true;
}
});
Here is an approach, based on MSquare's nice idea for detecting a long press of a button, that has an additional feature: not only is an operation performed in response to a long press, but the operation is repeated until a MotionEvent.ACTION_UP message is received. In this case, the long-press and short-press actions are the same, but they could be different.
Note that, as others have reported, removing the callback in response to a MotionEvent.ACTION_MOVE message prevented the callback from ever getting executed since I could not keep my finger still enough. I got around that problem by ignoring that message.
private void setIncrementButton() {
final Button btn = (Button) findViewById(R.id.btn);
final Runnable repeater = new Runnable() {
#Override
public void run() {
increment();
final int milliseconds = 100;
btn.postDelayed(this, milliseconds);
}
};
btn.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
increment();
v.postDelayed(repeater, ViewConfiguration.getLongPressTimeout());
} else if (e.getAction() == MotionEvent.ACTION_UP) {
v.removeCallbacks(repeater);
}
return true;
}
});
}
private void increment() {
Log.v("Long Press Example", "TODO: implement increment operation");
}
option: custom detector class
abstract public class
Long_hold
extends View.OnTouchListener
{
public#Override boolean
onTouch(View view, MotionEvent touch)
{
switch(touch.getAction())
{
case ACTION_DOWN: down(touch); return true;
case ACTION_MOVE: move(touch);
}
return true;
}
private long
time_0;
private float
x_0, y_0;
private void
down(MotionEvent touch)
{
time_0= touch.getEventTime();
x_0= touch.getX();
y_0= touch.getY();
}
private void
move(MotionEvent touch)
{
if(held_too_short(touch) {return;}
if(moved_too_much(touch)) {return;}
long_press(touch);
}
abstract protected void
long_hold(MotionEvent touch);
}
use
private double
moved_too_much(MotionEvent touch)
{
return Math.hypot(
x_0 -touch.getX(),
y_0 -touch.getY()) >TOLERANCE;
}
private double
held_too_short(MotionEvent touch)
{
return touch.getEventTime()-time_0 <DOWN_PERIOD;
}
where
TOLERANCE is the maximum tolerated movement
DOWN_PERIOD is the time one has to press
import
static android.view.MotionEvent.ACTION_MOVE;
static android.view.MotionEvent.ACTION_DOWN;
in code
setOnTouchListener(new Long_hold()
{
protected#Override boolean
long_hold(MotionEvent touch)
{
/*your code on long hold*/
}
});
I found one solution and it does not require to define runnable or other things and it's working fine.
var lastTouchTime: Long = 0
// ( ViewConfiguration.#.DEFAULT_LONG_PRESS_TIMEOUT =500)
val longPressTime = 500
var lastTouchX = 0f
var lastTouchY = 0f
view.setOnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
lastTouchTime = SystemClock.elapsedRealtime()
lastTouchX = event.x
lastTouchY = event.y
return#setOnTouchListener true
}
MotionEvent.ACTION_UP -> {
if (SystemClock.elapsedRealtime() - lastTouchTime > longPressTime
&& Math.abs(event.x - lastTouchX) < 3
&& Math.abs(event.y - lastTouchY) < 3) {
Log.d(TAG, "Long press")
}
return#setOnTouchListener true
}
else -> {
return#setOnTouchListener false
}
}
}

Categories