-2

I have nine buttons in 3 X 3 grid for tic-tac-toe app. Here is the code from board_layout.xml for first three buttons -

<TableLayout
    android:id="@+id/tableLayout1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="20sp" >

    <TableRow
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center" >

        <Button
            android:id="@+id/cellOne"
            android:layout_width="90sp"
            android:layout_height="98sp"
            android:text=""
            android:textSize="70sp"
            />

        <Button
            android:id="@+id/cellTwo"
            android:layout_width="90sp"
            android:layout_height="98sp"
            android:text=""
            android:textSize="70sp"
            />

        <Button
            android:id="@+id/cellThree"
            android:layout_width="90sp"
            android:layout_height="98sp"
            android:text=""
            android:textSize="70sp"
            />

    </TableRow>

<!and so on for other 6>

Now in my MainActivity.java I have a function setboard() that sets this layout on this screen and initializes the buttons as follows:

private String p1name = "P1";
private String p2name = "P2";

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

public void setName(View view) {
            //Function called upon click on some button
            // Other code irrelevant to the question
            setBoard();
    }

}

public void setBoard() {
    ////////////
    LayoutInflater inflater = this.getLayoutInflater();
    View boardView = inflater.inflate(R.layout.board_layout, null);
    ////////////

    boardCells = new Button[3][3];
    boardCells[0][0] = (Button) boardView.findViewById(R.id.cellOne);
    boardCells[0][1] = (Button) boardView.findViewById(R.id.cellTwo);
    boardCells[0][2] = (Button) boardView.findViewById(R.id.cellThree);
    // Code for other 6 buttons

    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++) {
            boardCells[i][j].setOnClickListener(new MyClickListener(i, j));
            boardCells[i][j].setText("");
            boardCells[i][j].setEnabled(false);
        }

    setContentView(R.layout.board_layout);

    TextView tv1 = (TextView) findViewById(R.id.p1Name_board);
    tv1.setText(p1name + ":");

    tv1 = (TextView) findViewById(R.id.p2Name_board);
    tv1.setText(p2name + ":");

    turnDisp = (TextView) findViewById(R.id.turnDispString);
    turnDisp.setText("Turn of " + p1name);
}

Here is the code for MyClickListener class (inner class of MainActivity class):

class MyClickListener implements View.OnClickListener {
    int x;
    int y;


    public MyClickListener(int x, int y) {
        this.x = x;
        this.y = y;
        //This Log.d works fine
        Log.d("TAG1", Float.toString(x) + " " + Float.toString(y));
    }


    public void onClick(View view) {
        //This Log.d doesn't work
        Log.d("TAG2", Float.toString(this.x) + " Hi ");

        //This Toast also doesn't work
        Toast.makeText(getApplicationContext(), "Pressed", Toast.LENGTH_LONG).show();

        //Plus Other code irrelevant to the question
    }

Now the issue is that Log.d in constructor called while setting buttons is working fine and prints to Logcat but the onClick function doesn't works when the button is clicked. Neither the Log.d nor the toast is shown for onClick function.

Any help would be appreciated.

Krist
  • 477
  • 4
  • 17
  • 1
    Arrays are zero indexed, by the way – OneCricketeer Feb 25 '17 at 18:06
  • 1
    What's the rest of the `inflate()` call? Are you inflating the board directly into the existing layout? If not, where are you adding the inflated board to the existing layout? Are you sure the `View`s you're setting the listeners on are actually the on-screen board? – Mike M. Feb 25 '17 at 18:06
  • Try using `@Override public void onClick` to force the compiler to use that method – OneCricketeer Feb 25 '17 at 18:07
  • 1
    @cricket_007 I know that it is just for ease. And I don;t know much Java so please elaborate on Override. – Krist Feb 25 '17 at 18:14
  • @MikeM. I have no clue what you are talking about. This is my first android app and the layout was not loading and I searched on SO for possible solution and this is what I got so I included that, and it worked. – Krist Feb 25 '17 at 18:17
  • I don't understand why I got downvotes ? :( – Krist Feb 25 '17 at 18:17
  • @Chetan The code shown is only for 1 button; Do you want me to add the code for all the buttons. I thought that will unnecessarily increase code size so I didn't include it here. – Krist Feb 25 '17 at 18:19
  • You have downvotes because you have incomplete codes in the question that doest compile. Override will say, "hey Java, I'm implementing this interface and overriding this method, please use it instead of the default definition" – OneCricketeer Feb 25 '17 at 18:21
  • 1
    Regarding what Mike says, `View boardView = inflater.inflate`. That line is not complete.... And how does `boardView` get used later in that code? How are you showing those buttons? – OneCricketeer Feb 25 '17 at 18:23
  • @cricket_007 Sorry for the typo. I updated the code. – Krist Feb 25 '17 at 18:26
  • 1
    We need to see entire setBoard() method plus the entire method calling it which I'll assume is an onCreate(), don't leave out parts, you might be missign something – Nick Cardoso Feb 25 '17 at 18:31
  • @NickCardoso - Added the code you asked. – Krist Feb 25 '17 at 18:44

2 Answers2

1

Short answer:

Your views aren't in the hierarchy

Long Answer / Solution (the right way to fix it):

You're seeing this problem because the views with your onClickListeners on aren't actually attached to the view hierarchy.

This could be easily avoided by never using a null in an inflater like you have here:

inflater.inflate(R.layout.board_layout, null);

It skips a useful block of code in the inflater anyway and there is already a provision for waiting to attach where you need to (i.e. in adapters):

inflater.inflate(R.layout.board_layout, parent, false);

When you pass null as the second parameter, the inflated views aren't attached to the hierarchy unless you manually do so.

However, in your case there is no need to inflate the layout. You're in an activity so setContentView() inflates and replaces the layout (but should never be called after onCreate).

Just change the layout being used in onCreate() and then remove the inflater.inflate and call findViewById directly on the context:

LayoutInflater inflater = this.getLayoutInflater(); View boardView = inflater.inflate(R.layout.board_layout, null);

boardCells[0][0] = (Button) findViewById(R.id.cellOne);

Also remove that second call to setContentView:

setContentView(R.layout.board_layout);

That second call was adding a new inflated set to the screen (not using the ones you attached a listener to)

There are also some other minor mistakes here. Java always uses Zero-based arrays (So the elements are [0],[1],[2]... not [1],[2]...). You're wasting memory with your board allocation - you have a 3x3 grid in your layout but a 4x4 array

boardCells = new Button[4][4];
boardCells[1][1] = (Button) boardView.findViewById(R.id.cellOne);

for (int i = 1; i <= 3; i++)
    for (int j = 1; j <= 3; j++)

When you use the loops above you're only using some items in the array (x's below). The zeros are empty wasted elements

0 | 0 | 0 | 0
- - - - - - -
0 | x | x | x
- - - - - - -
0 | x | x | x
- - - - - - -
0 | x | x | x

Another note (just to save you effort). Anytime you concatenate anything with a string, it will be cast to a string for you (primitives automatically, Objects automatically through their toString method), thus for logging the following are all equal:

Log.d("TAG1", Float.toString(x) + " " + Float.toString(y));
Log.d("TAG1", new Float(x) + " " + new Float(y));
Log.d("TAG1", x + " " + y);

Short Solution (a hack):

If, and only if, you didn't understand what I've just explained. Then you can fix it by changing

setContentView(R.layout.board_layout);

to

setContentView(boardView);
Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
  • I know about your first point that was just done to ensure compatibility to other code I wrote that's why I deliberately wasted memory. Thanks for the second point. But this in no sense answers my question. – Krist Feb 25 '17 at 18:24
  • I edited the code as per the suggestions, I was just being candid. Anyways that's what is bugging me too that I think the code is right but somehow the onClick() function doesn't works. – Krist Feb 25 '17 at 18:29
  • If I remove that code I the app crashes upon clicking the button calling setName(). Caused by: java.lang.NullPointerException: Attempt to read from null array at com.example.user.tictactoe_basic.MainActivity.setBoard(MainActivity.java:84) - This line is the same as boardCells[0][0] = (Button) findViewById(R.id.cellOne); – Krist Feb 25 '17 at 18:57
  • 1
    I don't understand your last comment. I've added a short hacky way to fix your code at the bottom of my answer too. You said you're new to android. I suggest you take a look at this book (which incidentally is written by another user here) https://commonsware.com/Android/ – Nick Cardoso Feb 25 '17 at 19:03
  • I meant that on doing what you suggested the app crashes and I get the above mentioned error mentioned on android monitor. Initially inflater code was not there but since the error came up earlier I added that code from here - http://stackoverflow.com/questions/16192378/findviewbyid-returns-null-tried-everything. – Krist Feb 25 '17 at 19:15
  • The hack doesn't works either :(. The buttons just become unclickable in case of `setContentView(boardView);` – Krist Feb 25 '17 at 19:16
  • So the buttons you see on the screen, are they in activity_main layout!? – Nick Cardoso Feb 25 '17 at 19:17
  • No they are in board_layout.xml as mentioned in the question. – Krist Feb 25 '17 at 19:29
  • Did you try doing setContent before you set the onClickListener? – Nick Cardoso Feb 25 '17 at 20:34
  • Yes I did. No luck that way as well. – Krist Feb 26 '17 at 08:11
-1

You should'nt use that code ,

boardCells[i][j].setOnClickListener(new MyClickListener(i, j));

I think , bugs starts from here.

You can ,

boardCells[i][j].setOnClickListener(new OnClickListener(....
Log.d("Testing","Clicked : " + i + "-" + j);
...)
alp_the_developer
  • 615
  • 1
  • 9
  • 23