Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Word Search Game: How to Select Multiple Items in a GridView By Dragging? (Android)

HERE ARE THE PROGRAM DETAILS: So I am creating a word search game, like where there are a bunch of letters arranged in a square, and you have to find and select the words that either go vertically, horizontally, or diagonally. I am using an array of Strings for the board, and using an ArrayAdapter to store this string array in a gridview, which is base element of the layout of my main activity.

HERE'S THE QUESTION: How do I make it so that users can drag their finger over their selection, selecting all of the letters in the word without having to lift their finger off the screen multiple times? And i want the highlight over the selected word to either stay on the screen i the player selected a word, and I want the highlight over the letters to go away when the user lifts his finger from the screen if he did not correctly select a word.

like image 469
evnobev Avatar asked Aug 01 '13 00:08

evnobev


1 Answers

I'm late to this question but coming across a similar problem recently, I decided to create my own custom board view library to address this problem. It adds more flexibility to what you can do and it leads to a more straightforward implementation.

Here are some snippets of code showing how it works. First, it is easy to draw the board grid on a custom view's onDraw() method:

@Override
protected void onDraw(Canvas canvas) {
    for (int i = 0; i <= numRows; i++) {
        //For custom grid sizes, y can't be equal the board size or the line drawn won't show
        int y = Math.min(boardHeight - 1, i * tileSize);
        canvas.drawLine(0, y, boardWidth, y, boardPaint);
    }

    for (int i = 0; i <= numCols; i++) {
        //For custom grid sizes, x can't be equal the board size or the line drawn won't show
        int x = Math.min(boardWidth - 1, i * tileSize);
        canvas.drawLine(x, 0, x, boardHeight, boardPaint);
    }
}

If you have a String[][] array of strings, here is how you could set the letters, assuming the views for each tile have already been placed on the board:

public void setLetterBoard(String[][] letterBoard) {
    if (letterBoard.length != getNumRows()
            || letterBoard[0].length != getNumCols()) {
        setBoardSize(letterBoard.length, letterBoard[0].length);
    }

    int row, col;
    for (int i=0; i < getChildCount(); i++) {
        row = i / getNumCols();
        col = i % getNumCols();

        LetterTileView child = (LetterTileView) getChildAt(i);
        child.setLetter(letterBoard[row][col]);
    }
}

Then you need more complex work to actually handle the touch and dragging over the board tiles to select the words:

@Override
public boolean onTouchEvent(MotionEvent event) {
    float X = event.getX();
    float Y = event.getY();
    int row = (int) (Y / getTileSize());
    int col = (int) (X / getTileSize());

    View child = getChildAt(row, col);

    //Exit on invalid touches
    if (event.getActionMasked() != MotionEvent.ACTION_UP
            && (row >= getNumRows()
            || col >= getNumCols()
            || child == null)) {
        return true;
    }

    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            Tile currentTile = new Tile(row, col, child);
            if (currentSelectedWord == null) {
                currentSelectedWord = new SelectedWord(currentTile);
            } else if (!currentTile.equals(currentSelectedWord.lastTile)
                    && currentSelectedWord.isTileValid(currentTile)) {
                if (!currentSelectedWord.isTileAllowed(currentTile)) {
                    //Clear the status of the old selection
                    updateTiles(currentSelectedWord.selectedTiles, false, false);
                    //If the current tile is valid but not allowed for the current word selection,
                    //start a new selection that matches the tile
                    currentSelectedWord = new SelectedWord(currentSelectedWord.getInitialTile());
                }
                List<Tile> tiles = getTilesBetween(currentSelectedWord.lastTile, currentTile);
                if (tiles.size() > 0) {
                    currentSelectedWord.addTiles(tiles);
                }
            }
            updateTiles(currentSelectedWord.selectedTiles, true, false);
            break;
        case MotionEvent.ACTION_UP:
            if (currentSelectedWord != null) {
                boolean isValidSelection = (listener != null && listener.onWordSelected(currentSelectedWord.toBoardWord()));
                updateTiles(currentSelectedWord.selectedTiles, false, isValidSelection);
                if (isValidSelection) {
                    selectedWords.add(currentSelectedWord);
                }
                currentSelectedWord = null;
            }
            break;
        default:
            return false;
    }
    return true;
}

The methods isTileValid() and isTileAllowed() make sure the tile the user is trying to select is in an allowed direction and has not been previously selected. Finally, the getTilesBetween() return all the valid tiles between the first tile the user touched and the one he/she is currently touching.

Hope it helps!

like image 168
Ricardo Avatar answered Oct 20 '22 07:10

Ricardo