简体   繁体   中英

Buttons in gridview not responding to onClickListener

Background info:

I am required to create an SOS game. I decided to implement a grid view with a 7x7 grid of SOSButtons (Button extending AppCompatButton).


Problem:

After calling setOnClickListener() within my SOSButton class, I expect to see an onClickListener of some sort to be attached to my button, however there is none.

When running the application, it has no issue, and displays perfectly. When clicking on any button within the grid, no listener is fired.

When clicking any other button outside of the grid, it fires as expected.

Note: I see this by setting a breakpoint after the setOnClickListener() line, and view the SOSButton object "mListeners = null"

Note 2: I attempted to remove all unnecessaries and use a simple Button object, with an onClickListener() as shown below, but with no avail:

Button button = new Button(this);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Toast.makeText(MainActivity.this, "click occured from main avtivity delcaration", Toast.LENGTH_SHORT).show();
        Button b = (Button) view;
        String s = b.getText().toString();
        switch (s){
            case "S" : {
                b.setText("O");
                break;
            }
            case "O" : {
                b.setText("");
                break;
            }
            case "" : {
                b.setText("S");
                break;
            }
        }
    }
});
list.add(button);

I tried:

  • SOSButton with onClickListener set in Button constructor
  • SOSButton with onClickListener set in Button constructor and GridView.setOnItemClickListener
  • SOSButton with only GridView.setOnItemClickListener
  • Button with onClickListener set in Button constructor
  • Button with onClickListener set in Button constructor and GridView.setOnItemClickListener
  • Button with only GridView.setOnItemClickListener

None of the above fired any listener. The only listeners firing were those of buttons NOT in the gridview.


More Info:

SOSButton.java

package wrap302.nmu.task1_sos;

import android.content.Context;
import android.support.v7.widget.AppCompatButton;
import android.view.View;

public class SOSButton extends AppCompatButton {

    private SO_Select so_select;

    public SOSButton(Context context) {
        super(context);
        so_select = SO_Select.None;
        OnClickListener onClickListener = new OnClickListener() {
            @Override
            public void onClick(View view) {
                switch (so_select) {
                    case None: {
                        so_select = SO_Select.S;
                        break;
                    }
                    case S: {
                        so_select = SO_Select.O;
                        break;
                    }
                    case O: {
                        so_select = SO_Select.None;
                        break;
                    }
                }
                SOSButton.this.update();
            }
        };
        setOnClickListener(onClickListener);
        update();
    }

    private void update(){
        setText(so_select.toString());
    }

    @Override
    public String toString() {
        return "SOSButton{" +
                "so_select=" + so_select.toString() +
                '}';
    }
}

SO_Select enum:

package wrap302.nmu.task1_sos;

public enum SO_Select {
    None(""),
    S("S"),
    O("O");

    private String state;

    SO_Select(String state) {
        this.state = state;
    }

    @Override
    public String toString() {
        return state;
    }
}

SOSButtonMatch interface:

package wrap302.nmu.task1_sos;

import android.widget.Button;

interface SOSButtonMatch {
    /**
     * Interface to check 3 buttons' text and return true if text follows a sequence specified
     *
     * @param b1 Start button
     * @param b2 Middle button
     * @param b3 End button
     * @return Boolean
     */
    boolean check(Button b1, Button b2, Button b3);
}

SOS_Adapter for the grid:

package wrap302.nmu.task1_sos;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;

import java.util.List;

public class SOS_Adapter<T> extends ArrayAdapter<T>{

    private int resourceId;
    private Context mContext;
    private List<T> items;

    public SOS_Adapter(Context context, int resource, List<T> objects) {
        super(context, resource, objects);
        this.mContext = context;
        this.items = objects;
        this.resourceId = resource;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflater.inflate(resourceId, viewGroup, false);
        v.setTag(getItem(i));
        Button b = v.findViewById(R.id.sosButton);
        b.setText(((SOSButton)getItem(i)).getText());
        return b; //runs twice
    }
}

MainActivity code:

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

/**
 * Initialize gui objects
 */
private void initGui() {
    btnDone = (Button) findViewById(R.id.btnDone);
    btnExit = (Button) findViewById(R.id.btnExit);
    btnReset = (Button) findViewById(R.id.btnReset);
    grid = (GridView) findViewById(R.id.grid);
    lblGridPosOpen = (TextView) findViewById(R.id.lblGridPosOpen);
    lblP1_Score = (TextView) findViewById(R.id.lblP1_Score);
    lblP2_Score = (TextView) findViewById(R.id.lblP2_Score);
    lblPTurn = (TextView) findViewById(R.id.lblPTurn);

    createAdapter();
    grid.setAdapter(sos_adapter);

    OnItemClickListener clickListener = new OnItemClickListener() {   //runs twice
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            Toast.makeText(MainActivity.this, "clicked", Toast.LENGTH_SHORT).show();
            /*SO_Select so_select = SO_Select.None;
            switch (so_select) {
                case None: {
                    so_select = SO_Select.S;
                    break;
                }
                case S: {
                    so_select = SO_Select.O;
                    break;
                }
                case O: {
                    so_select = SO_Select.None;
                    break;
                }
            }
            Object itemAtPosition = adapterView.getItemAtPosition(i);
            SOSButton b = ((SOSButton) itemAtPosition);
            b.setText(so_select.toString());*/
        }
    };
//        grid.setOnItemClickListener(clickListener);
    Toast.makeText(this, "displaying", Toast.LENGTH_SHORT).show();

}

private void createAdapter() {
    List<SOSButton> sosButtons = generateButtons(GRID_SIZE);
    sos_adapter = new SOS_Adapter(this, R.layout.sosbutton,sosButtons);
}

private List<SOSButton> generateButtons(int grid_size) {
    List<SOSButton> l = new ArrayList<>();
    for (int i = 0; i < grid_size; i++) {
        SOSButton sosButton = new SOSButton(this);
        sosButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "click occured from main avtivity delcaration", Toast.LENGTH_SHORT).show();
            }
        });
        l.add(sosButton);
    }
    return l;

}

well, I found the solution after some more Googling.

Getting the button to respond to a click event whilst in the adapter is not as simple as adding a listener to the button itself.

In the SOS_Adapter, the getView() is the place to add the listener.

This is done simply by adding:

        b.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Button b = (Button) view;
            String s = b.getText().toString();
            switch (s){
                case "S" : {
                    b.setText("O");
                    break;
                }
                case "O" : {
                    b.setText("");
                    break;
                }
                case "" : {
                    b.setText("S");
                    break;
                }
            }
        }
    });

right before the return

Then one recieves a hard earned response to one's touch.

Could someone please explain why this needs to be done in a beat-around-the-bush way?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM