Question

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.

Was it helpful?

Solution 2

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.

OTHER TIPS

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();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top