2

In my app, I have to move views anywhere on the layout. So I used onTouchListener. On onClick and onLongClick, I have to perform some other tasks. I am able to move the views. But, the listeners are conflicting with each other. When I move the views, then, sometimes, either onClick or onLongClick are invoked. And, I don't want them to be called, when user is moving the view. How can I avoid this? I had referred this link to crosscheck my code. But, couldn't found the mistake. Below is my code:

OnTouch of view

broItemView.setOnTouchListener(new View.OnTouchListener() { private int deltaX; private int deltaY; private float initialTouchX; private float initialTouchY; private boolean isMoved; private int lastTouchX; private int lastTouchY; @Override public boolean onTouch(final View v, final MotionEvent event) { ViewGroup vg = (ViewGroup) v.getParent(); draggedViewIndex = vg.indexOfChild(v); initialTouchX = event.getRawX(); initialTouchY = event.getRawY(); boolean result = v.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { isMoved = false; FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v .getLayoutParams(); deltaX = (int) initialTouchX - params.leftMargin; deltaY = (int) initialTouchY - params.topMargin; new Handler().postDelayed(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { if (!isMoved && event.getAction() != MotionEvent.ACTION_UP) { ViewGroup vg = (ViewGroup) v .getParent(); draggedViewIndex = vg.indexOfChild(v); ClipData data = ClipData.newPlainText( "", ""); DragShadowBuilder shadowBuilder = new View.DragShadowBuilder( v); v.startDrag(data, shadowBuilder, v, 0); v.setVisibility(View.INVISIBLE); Animation slideUp = AnimationUtils .loadAnimation( WMMBFragmentContainerActivity.this, R.anim.slide_up_dialog); crossImage.setVisibility(View.VISIBLE); crossImage.startAnimation(slideUp); } } }); } }, 1000); break; } case MotionEvent.ACTION_MOVE: { Log.e(TAG, "ACTION_MOVE"); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v .getLayoutParams(); params.leftMargin = (int) initialTouchX - deltaX; params.topMargin = (int) initialTouchY - deltaY; v.setLayoutParams(params); isMoved = true; break; } case MotionEvent.ACTION_UP: { Log.e(TAG, "ACTION_UP"); initialTouchX = 0; initialTouchY = 0; deltaX = 0; deltaY = 0; if (!isMoved) { Log.e(TAG, "ACTION_UP if"); showRunningTransactionFragment(mBroList .get(draggedViewIndex)); } else { Log.e(TAG, "ACTION_UP else"); isMoved = false; } break; } default: Log.e(TAG, "default"); return result; } relBroCircle.invalidate(); Log.e(TAG, "defaultqqq "); return true; } }); 

Update 1:

So, as per Viswanath's suggestion below, I modified my code. But, still I am facing problem. When, I move the views in onTouch, sometimes, the code which I wrote in the handler gets executed. I don't want this to be executed when the user moves the view. I want to make it smooth as Facebook messenger ChatHeads. Their, onClick or onLongClick are called only, when user wants to perform them. I am not getting where did I made mistake.

Update 2:

Now, I had removed multiple listeners and tried to do all my desired operations in onTouch. Its working except one problem. Even if I click on the view(I hadn't moved the view till now), ACTION_MOVE gets called and, thus isMoved becomes true. So, in this case, the code inside if block in ACTION_UP never gets executed. To find out whether I did something wrong, I created a dummy project, chose a textview and added onTouchListener and onClickListener to it. Then, performed click operation. There also, I found that ACTION_MOVE was called first prior to onClick then code inside onClick was executed. So, how can I get onClick effect with this? I had updated my code.

2
  • don not set multiple listeners. Better you handle all this things in onTouchListener Commented May 6, 2014 at 12:40
  • Tried as per Viswanath's suggestion earlier, but sometimes, the code inside the handler gets executed while moving the views. I had updated my code already. Commented May 6, 2014 at 12:44

2 Answers 2

1

When you are using onTouch this may occur. Below is the workaround for onclick

Normally we will manually set a flag like

boolean isMoved;

case MotionEvent.ACTION_DOWN: isMoved = false; case MotionEvent.ACTION_MOVE: isMoved = true; case MotionEvent.ACTION_UP: if(!isMoved) YourOnClick(); 
Sign up to request clarification or add additional context in comments.

6 Comments

And, the same I have to do with onLongClick as well?
For onLongClick you should use a timer which starts on ACTION_DOWN and finishes after 2 seconds if the isMoved is false at that time and user didnt perform ACTION_UP trigger OnLongClick
Hi Viswanath, I didn't understood your point of using a timer. Do you mean using a TimerTask or Handler. Ah, I forgot to mention earlier, these views are created at runtime.
Hi @Nitish,That doesn't matter you can set a touch listener for a dynamically created view. You can use a handler, but dont forget to initiate the listener from a runOnUiThread() if you have UI updation
Hi Vishwanath, just updated my code. Did the same, as per your suggestion. Can you please, look at it once?
|
1

Finally, I managed to achieve what I wanted. The solution is based on Viswanath's idea proposed above. Here is my code:

broItemView.setOnTouchListener(new View.OnTouchListener() { private int deltaX; private int deltaY; private float initialTouchX; private float initialTouchY; private boolean isMoved; private int lastTouchX; private int lastTouchY; @Override public boolean onTouch(final View v, final MotionEvent event) { ViewGroup vg = (ViewGroup) v.getParent(); draggedViewIndex = vg.indexOfChild(v); initialTouchX = event.getRawX(); initialTouchY = event.getRawY(); boolean result = v.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { isMoved = false; FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v .getLayoutParams(); deltaX = (int) initialTouchX - params.leftMargin; deltaY = (int) initialTouchY - params.topMargin; lastTouchX = (int) initialTouchX; lastTouchY = (int) initialTouchY; Log.e(TAG, "ACTION_DOWN lasttouchX: " + lastTouchX); Log.e(TAG, "ACTION_DOWN lasttouchY: " + lastTouchY); new Handler().postDelayed(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { if (!isMoved && event.getAction() != MotionEvent.ACTION_UP) { //perform LongClickOperation } } }); } }, 1000); break; } case MotionEvent.ACTION_MOVE: { FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v .getLayoutParams(); params.leftMargin = (int) initialTouchX - deltaX; params.topMargin = (int) initialTouchY - deltaY; v.setLayoutParams(params); break; } case MotionEvent.ACTION_UP: { if ((lastTouchX == (int) initialTouchX) && (lastTouchY == (int) initialTouchY)) { isMoved = false; } else if ((lastTouchX > (int) initialTouchX)) { if (((lastTouchX - (int) initialTouchX) <= 10)) { isMoved = false; } else { isMoved = true; } } else if ((lastTouchX < (int) initialTouchX)) { if ((((int) initialTouchX - lastTouchX) <= 10)) { isMoved = false; } else { isMoved = true; } } else if ((lastTouchY > (int) initialTouchY)) { if (((lastTouchY - (int) initialTouchY) <= 10)) { isMoved = false; } else { isMoved = true; } } else if ((lastTouchY < (int) initialTouchY)) { if ((((int) initialTouchY - lastTouchY) <= 10)) { isMoved = false; } else { isMoved = true; } } else { isMoved = true; } if (!isMoved) { //perform onClick operation } else { isMoved = false; } initialTouchX = 0; initialTouchY = 0; deltaX = 0; deltaY = 0; break; } default: return result; } relBroCircle.invalidate(); return true; } }); 

In the ACTION_UP, I had to add additional checks to determine whether view actually moved or not because, I found that, ACTION_MOVE still called up even when user has simply clicked on the view. I have checked this separately, as I have mentioned Update 2 of my post. And, when I tested my code on a 10" Tablet, then, my onclick code never executed. So, to avoid this problem, I had to write those bunch of codes. But, I don't think its an elegant solution and seems hack to me. If anyone has better solution, then please advise so that I can update my code.

2 Comments

Did you ever find a better solution to this problem?
@MichaelAlanHuff Nops, instead used Android DragDrop API

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.