2

I am making a game. I use GridView to display game blocks of various shapes ( tetris-like). So I have for example L block, T block etc.

When player touch and press on a block, he can move it anywhere on the screen. Each block is actualy a GridView, which has few cells colored and few invisible (depends on the block shape).

ms paint image

The problem occurs, when the blocks are near to each other, and you want to move a block which is "under" other blocks. Because of the gridView shape, touching at 1 or 2 position of the green block will not trigger green block touch listener, but blue (since the blue block was placed after the green on the layout, and you are touching inside its boundaries). So the player will start moving the blue block, but of course he wanted to move the green.

How can I fix this problem?

Edit:

I tried to set touch listener to every Blocks gridView child (don't mind empty cells at this moment), and pass the information to the methods I made. However, ACTION_DOWN works, but once I move my finger, the ACTION_UP is triggered (without any ACTION_MOVE).

void setBlockTouchListener(final Block block)
{
    for (int i = 0; i < block.getChildCount(); i++)
    {
        block.getChildAt(i).setOnTouchListener(new View.OnTouchListener()
        {
            @Override
            public boolean onTouch(View v, MotionEvent event)
            {
                final int X = (int) event.getRawX();
                final int Y = (int) event.getRawY();
                switch (event.getAction() & MotionEvent.ACTION_MASK)
                {
                    case MotionEvent.ACTION_DOWN:
                        Log.d("TAG", "child down");
                        onDown(block, X, Y, MotionEvent.ACTION_DOWN);
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        Log.d("TAG", "child cancel");
                        onCancel(block, X, Y, MotionEvent.ACTION_CANCEL);
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.d("TAG", "child move");
                        onMove(block, X, Y, MotionEvent.ACTION_MOVE);
                        break;
                }

                return true;
            }
        });
    }
}

Logcat (when I try to touch and move the block):

child_down
child_cancel 
blackJack
  • 380
  • 3
  • 21

1 Answers1

1

Try calling requestDisallowInterceptTouchEvent after

case MotionEvent.ACTION_DOWN

Another view may be stealing the touch events. This will disallow the theft.

Block block = ((Block) v.getParent());

switch (event.getAction() & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
        block.requestDisallowInterceptTouchEvent(true);
        onDown(block, X, Y, MotionEvent.ACTION_DOWN);
        break;
        }
Cheticamp
  • 61,413
  • 10
  • 78
  • 131
  • Okay so I found out this step you described is not neccessary for me, but `block.getLayoutParams().width = somevalue` (that's what I do in onDown) is the line which "consumes" the touch event and triggers `ACTION_UP`. Would you please have any suggestion how to listen even after changing layout width & height? By the way, I call `requestLayout()` too after changing params to apply them. – blackJack Nov 09 '18 at 12:02
  • @BlackJack If you want to change the size of the block, you might try using [Property Animation](https://developer.android.com/guide/topics/graphics/prop-animation) instead of the layout params. ` block.animate().scaleX(1.1f).scaleY(1.1f).start()' will grow the view by 10% and should not cause a cancel. I think once this is solved, my answer will be useful. – Cheticamp Nov 09 '18 at 13:50
  • Thanks, I implemented the animation instead of params, and now it is possible to drag and move the blocks on the layout as before. But only one thing is still not working, and that's touchListener on cells under empty cells (figure 1), even I set listener only to cells which are actually displayed (empty cells have visibility `GONE` and no touchListener). When i touch that cell, no block gets touch focus. Other cells works fine. And to not forget, in the end I need the `requestDisallowInterceptTouchEven` as you said, so it is in code too. PS: I really want to give you bounty, just... – blackJack Nov 09 '18 at 16:18
  • @BlackJack Even though the cell is `GONE`, the `Block` which is the `ViewGroup` holding the cell is taking the touch event. Add an override in `Block` for `onTouchEvent()` and always return `false`. This will permit the underlying cell to get control. Take a look [here](https://stackoverflow.com/a/46862320/6287910) for more info. – Cheticamp Nov 09 '18 at 16:36
  • Tried it now, but still no change. I'll take a look at the example you posted. – blackJack Nov 09 '18 at 16:45
  • All blocks views are in a RelativeLayout, returning false in onTouch of a block would pass the event to the RelativeLayout if I am not wrong. – blackJack Nov 09 '18 at 17:02
  • @BlackJack Return `false` in `onTouchEvent()` not `onTouch()`. The `Blocks` are stacked. If the top block refuses the touch (`onTouchEvent()` returns `false`), then the event moves down to the underlying view which, in this case, would be the color cell under the `GONE` cell. It would then have a bite at the apple and will be able to capture the touch stream to move its `Block`. – Cheticamp Nov 09 '18 at 17:15
  • You are right. I overrided `onTouchEvent` and now passing the event seems to go correctly, empty cells now refuses the touch which goes under. Thanks for your help and patience. – blackJack Nov 09 '18 at 17:28