I am working on a task related to reading an excel which contains radio buttons and checkbox using Java, I have tried a lot but can't move further on this, when I am trying to read the data of cell containing Checkbox it is returning a null value.
Can someone please help with this.
Controls are not contained in the cells but hovers in the drawing layer over the sheet as shapes and only are anchored to the cells. So the cell may be null (because of no content in it) although a shape hovers over it and is anchored to it.
Moreover there are two different kinds of controls possible. There are form controls and ActiveX controls. The states of ActiveX controls are stored in binary code parts activeX1.bin and thus getting the state of them is very hard.
In earlier Excel versions (2007 for example) the anchor informations of all controls are stored in /xl/drawings/vmlDrawing1.vml only. Later versions are storing them in default drawing and within
<controls>
<mc:AlternateContent>
...
</mc:AlternateContent>
</controls>
parts in the sheet XML too. Fortunately there is also /xl/drawings/vmlDrawing1.vml for backwards compatibility.
The following code does parsing the /xl/drawings/vmlDrawing1.vml to get the control which is possible anchored to a cell. If found, it gets this control and if this control is a form control and not a ActiveX control, then it also can get it's state. For ActiveX controls it gets only the information, that a "Pict" is anchored to this cell.
Excel:
Code:
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import javax.xml.namespace.QName;
class ReadExcelXSSFControls {
public ReadExcelXSSFControls() throws Exception {
XSSFWorkbook wb = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("ExcelWithControls.xlsx"));
Sheet sheet = wb.getSheetAt(0);
for (Row row : sheet) {
for (int c = 0; c < 2; c++) {
Cell cell = row.getCell(c);
if (row.getRowNum() == 0) {
System.out.print(cell + "\t");
} else {
if (c == 0) {
System.out.print(cell + "\t");
} else if (c == 1) {
if (cell == null) cell = row.createCell(c);
Control contol = getControlAt((XSSFCell)cell);
System.out.print(contol);
}
}
}
System.out.println();
}
wb.close();
}
private Control getControlAt(XSSFCell cell) throws Exception {
XSSFSheet sheet = cell.getSheet();
Row row = cell.getRow();
int r = row.getRowNum();
int c = cell.getColumnIndex();
int drheight = (int)Math.round(sheet.getDefaultRowHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI);
int rheight = (int)Math.round(row.getHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI);
row = null;
if(r > 0) row = sheet.getRow(r-1);
int rheightbefore = (row!=null)?(int)Math.round(row.getHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI):drheight;
row = sheet.getRow(r+1);
int rheightafter = (row!=null)?(int)Math.round(row.getHeightInPoints() * Units.PIXEL_DPI / Units.POINT_DPI):drheight;
String name = null;
String objectType = null;
String checked = null;
XmlCursor xmlcursor = null;
if (sheet.getCTWorksheet().getLegacyDrawing() != null) {
String legacyDrawingId = sheet.getCTWorksheet().getLegacyDrawing().getId();
POIXMLDocumentPart part = sheet.getRelationById(legacyDrawingId);
XmlObject xmlDrawing = XmlObject.Factory.parse(part.getPackagePart().getInputStream());
xmlcursor = xmlDrawing.newCursor();
QName qnameClientData = new QName("urn:schemas-microsoft-com:office:excel", "ClientData", "x");
QName qnameAnchor = new QName("urn:schemas-microsoft-com:office:excel", "Anchor", "x");
boolean controlFound = false;
while (xmlcursor.hasNextToken()) {
XmlCursor.TokenType tokentype = xmlcursor.toNextToken();
if (tokentype.isStart()) {
if (qnameClientData.equals(xmlcursor.getName())) {
controlFound = true;
XmlObject clientdata = xmlcursor.getObject();
XmlObject[] xmlchecked = clientdata.selectPath("declare namespace x='urn:schemas-microsoft-com:office:excel' x:Checked");
if (xmlchecked.length > 0) {
checked = "Checked";
} else {
checked = "Not checked";
}
while (xmlcursor.hasNextToken()) {
tokentype = xmlcursor.toNextToken();
if (tokentype.isAttr()) {
if (new QName("ObjectType").equals(xmlcursor.getName())) {
objectType = xmlcursor.getTextValue();
name = objectType + " in row " + (r+1);
}
} else {
break;
}
}
} else if (qnameAnchor.equals(xmlcursor.getName()) && controlFound) {
controlFound = false;
String anchorContent = xmlcursor.getTextValue().trim();
String[] anchorparts = anchorContent.split(",");
int fromCol = Integer.parseInt(anchorparts[0].trim());
int fromColDx = Integer.parseInt(anchorparts[1].trim());
int fromRow = Integer.parseInt(anchorparts[2].trim());
int fromRowDy = Integer.parseInt(anchorparts[3].trim());
int toCol = Integer.parseInt(anchorparts[4].trim());
int toColDx = Integer.parseInt(anchorparts[5].trim());
int toRow = Integer.parseInt(anchorparts[6].trim());
int toRowDy = Integer.parseInt(anchorparts[7].trim());
if (fromCol == c /*needs only starting into the column*/
&& (fromRow == r || (fromRow == r-1 && fromRowDy > rheightbefore/2f))
&& (toRow == r || (toRow == r+1 && toRowDy < rheightafter/2f))) {
//System.out.print(fromCol + ":" +fromColDx + ":" + fromRow + ":" + fromRowDy + ":" + toCol + ":" + toColDx + ":" + toRow + ":" + toRowDy);
break;
}
}
}
}
}
if (xmlcursor!=null && xmlcursor.hasNextToken())
return new Control(name, objectType, checked, r, c);
return new Control("Not found", "unknown", "undefined", r, c);
}
public static void main(String[] args) throws Exception {
ReadExcelXSSFControls o = new ReadExcelXSSFControls();
}
private class Control {
private String name;
private String objectType;
private String checked;
private int row;
private int col;
public Control(String name, String objectType, String checked, int row, int col) {
this.name = name;
this.objectType = objectType;
this.checked = checked;
this.row = row;
this.col= col;
}
public String getName() {
return this.name;
}
public String getObjectType() {
return this.objectType;
}
public String getChecked() {
return this.checked;
}
public int getRow() {
return this.row;
}
public int getCol() {
return this.col;
}
public String toString() {
return this.name + ":r/c:" +row+ "/" + col + ":" + this.checked;
}
}
}
Result:
axel#arichter:~/Dokumente/JAVA/poi/poi-3.17$ java -cp .:./*:./lib/*:./ooxml-lib/* ReadExcelXSSFControls
Product Status
a Checkbox in row 2:r/c:1/1:Checked
b Not found:r/c:2/1:undefined
c Checkbox in row 4:r/c:3/1:Not checked
d Checkbox in row 5:r/c:4/1:Checked
e Radio in row 6:r/c:5/1:Checked
f Not found:r/c:6/1:undefined
g Not found:r/c:7/1:undefined
e Checkbox in row 9:r/c:8/1:Checked
f Not found:r/c:9/1:undefined
h Radio in row 11:r/c:10/1:Not checked
ActiveX Pict in row 14:r/c:13/1:Not checked
Related
case: my string position is dynamic so i have to search the string position in the excel and then fetch the value next to that position.
This is what i have tried
public class AssetRegisterdepreciationReport
{
public static void ReadAssetExcelData() throws IOException
{
String cellContent = "Grand Total:";
int rownr, colnr = 5;
FileInputStream AssetReport = new FileInputStream("C:\\Users\\Admin\\Downloads\\Asset_Depreciation_14_03_2020 19-05-31.xlsx");
#SuppressWarnings("resource")
XSSFWorkbook Assetworkbook = new XSSFWorkbook(AssetReport);
XSSFSheet sheetnumber1 = Assetworkbook.getSheetAt(0);
rownr = findRow(sheetnumber1, cellContent);
output(sheetnumber1, rownr, colnr);
finish();
}
private static void output(XSSFSheet sheetnumber1, int rownr, int colnr)
{
XSSFRow row = sheetnumber1.getRow(rownr);
XSSFCell cell = row.getCell(colnr);
System.out.println("Your total is: " + cell);
}
private static int findRow(XSSFSheet sheetnumber1, String cellContent)
{
int rowNum = 0;
for(Row row : sheetnumber1)
{
for(Cell cell : row)
{
switch(cell.getCellType())
{
case STRING:
if(cell.getRichStringCellValue().getString() == cellContent)
{
rowNum = row.getRowNum();
System.out.println(rowNum);
}
default:
break;
}
}
}
return rowNum;
}
private static void finish()
{
System.exit(0);
}
}
findrow() is not returning any rownum value (default value is returned).
For string comparison in Java please use .equals() and not ==.
if(cell.getRichStringCellValue().getString().equals(cellContent))
I am making an android Hashikawekero puzzle game, I have implemented a algorithm to spawn nodes (Islands) at random positions using a 2-d array this works fine it creates the node at random position but most of the times the map cant be solved. The map nodes spawn at random.
BoardCreation.java Class - this generates the map.
package Island_and_Bridges.Hashi;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.Log;
import java.util.Random;
import static junit.framework.Assert.*;
//This class Creates the map by random using a 2d array
public class BoardCreation {
// This class member is used for random initialization purposes.
static private final Random random = new Random();
// The difficulty levels.
private static final int EASY = 0;
static public final int MEDIUM = 1;
static public final int HARD = 2;
static public final int EMPTY = 0;
private static int ConnectionFingerprint(BoardElement start, BoardElement end) {
int x = start.row * 100 + start.col;
int y = end.row * 100 + end.col;
// Swap to get always the same fingerprint independent whether we are called
// start-end or end-start
if (x > y ) {
int temp = x;
x = y;
y = temp;
}
Log.d("", String.format("%d %d" , x ,y));
return x ^ y;
}
public class State {
// The elements of the board are stored in this array.
// A value defined by "EMPTY" means that its not set yet.
public BoardElement [][] board_elements = null;
public int [][] cell_occupied = null;
// The width of the board. We only assume squared boards.
public int board_width=0;
public State(int width) {
board_width = width;
board_elements = new BoardElement[width][width];
cell_occupied = new int[width][width];
}
public State CloneWithoutConnections() {
State newstate = new State(board_width);
if (board_elements != null) {
newstate.board_elements = new BoardElement[board_elements.length][board_elements.length];
for (int i = 0; i < board_elements.length; ++i) {
for (int j = 0; j < board_elements.length; ++j) {
if (board_elements[i][j] == null)
continue;
newstate.board_elements[i][j] = board_elements[i][j].clone();
}
}
}
if (cell_occupied != null) {
assert board_elements != null;
newstate.cell_occupied = new int[board_elements.length][board_elements.length];
for (int i = 0; i < board_elements.length; ++i) {
System.arraycopy(cell_occupied[i], 0, newstate.cell_occupied[i], 0, board_elements.length);
}
}
return newstate;
}
public void AddToBridgeCache(BoardElement first, BoardElement second) {
if (first == null || second == null) { return; }
final int fingerprint = ConnectionFingerprint(first, second);
Log.d(getClass().getName(),
String.format("Fingerprint of this bridge %d", fingerprint));
// mark the end points as occupied.
cell_occupied[first.row][first.col] = fingerprint;
cell_occupied[second.row][second.col] = fingerprint;
int dcol = second.col - first.col;
int drow = second.row - first.row;
if (first.row == second.row) {
for (int i = (int) (first.col + Math.signum(dcol)); i != second.col; i += Math.signum(dcol)) {
cell_occupied[first.row][i] = fingerprint;
String.format("deleting bridge");
}
} else {
assert first.col == second.col;
for (int i = (int) (first.row + Math.signum(drow)); i != second.row; i+= Math.signum(drow)) {
cell_occupied[i][first.col] = fingerprint;
}
}
}
} // end of state
private State current_state, old_state;
static private final int WIDTH_EASY = 7;
private void NewGame(int hardness) {
switch(hardness) {
case EASY:
Log.d(getClass().getName(), "Initializing new easy game");
InitializeEasy();
old_state = getCurrentState().CloneWithoutConnections();
break;
}
}
public void ResetGame() {
if (old_state != null) {
Log.d(getClass().getName(), "Setting board_elements to old_elements");
setCurrentState(old_state.CloneWithoutConnections());
} else {
Log.d(getClass().getName(), "old_lements are zero");
}
}
public BoardCreation(int hardness) {
NewGame(hardness);
}
public boolean TryAddNewBridge(BoardElement start, BoardElement end, int count) {
assertEquals(count, 1);
assert (start != null);
assert (end != null);
final int fingerprint = ConnectionFingerprint(start, end);
Log.d(getClass().getName(),
String.format("considering (%d,%d) and (%d,%d)", start.row,start.col, end.row,end.col));
if (start.row == end.row && start.col == end.col) {
Log.d(getClass().getName(), "Same nodes selected!");
return false;
}
assert count > 0;
int dcol = end.col - start.col;
int drow = end.row - start.row;
// It must be a vertical or horizontal bridge:
if (Math.abs(dcol) > 0 && Math.abs(drow) > 0) {
Log.d(getClass().getName(), "not a horizontal or vertical bridge.");
return false;
}
// First we check whether start and end elements can take the specified bridge counts.
int count_start = start.GetCurrentCount();
int count_end = end.GetCurrentCount();
if (count_start + count > start.max_connecting_bridges ||
count_end + count > end.max_connecting_bridges) {
Log.d(getClass().getName(), "This Bridge is not allowed");
return false;
}
Log.d(getClass().getName(),
String.format("Sums:%d # (%d,%d) and %d # (%d,%d)",
count_start, start.row, start.col,
count_end, end.row, end.col));
Connection start_connection = null;
Connection end_connection = null;
// Next we check whether we are crossing any lines.
if (start.row == end.row) {
for (int i = (int) (start.col + Math.signum(dcol)); i != end.col; i += Math.signum(dcol)) {
if (getCurrentState().cell_occupied[start.row][i] > 0 &&
getCurrentState().cell_occupied[start.row][i] != fingerprint) {
Log.d(getClass().getName(), "Crossing an occupied cell.");
return false;
}
}
assert start.col != end.col;
if (start.col > end.col) {
start.connecting_east = GetOrCreateConnection(end, start.connecting_east);
end.connecting_west = GetOrCreateConnection(start, end.connecting_west);
start_connection = start.connecting_east;
end_connection = end.connecting_west;
} else {
start.connecting_west = GetOrCreateConnection(end, start.connecting_west);
end.connecting_east = GetOrCreateConnection(start, end.connecting_east);
start_connection = start.connecting_west;
end_connection = end.connecting_east;
}
} else {
assert start.col == end.col;
for (int i = (int) (start.row + Math.signum(drow)); i != end.row ; i += Math.signum(drow)) {
if (getCurrentState().cell_occupied[i][start.col] > 0 &&
getCurrentState().cell_occupied[i][start.col] != fingerprint) {
Log.d(getClass().getName(), "Crossing an occupied cell.");
return false;
}
}
if (start.row > end.row ) {
start.connecting_north = GetOrCreateConnection(end, start.connecting_north);
end.connecting_south = GetOrCreateConnection(start, end.connecting_south);
start_connection = start.connecting_north;
end_connection = end.connecting_south;
} else {
start.connecting_south= GetOrCreateConnection(end, start.connecting_south);
end.connecting_north = GetOrCreateConnection(start, end.connecting_north);
start_connection = start.connecting_south;
end_connection = end.connecting_north;
}
}
start_connection.destination = end;
end_connection.destination = start;
start_connection.second += count;
end_connection.second += count;
getCurrentState().AddToBridgeCache(start, end);
Log.d(getClass().getName(),
String.format("New bridge added. Sums:%d # (%d,%d) and %d # (%d,%d)",
count_start, start.row,start.col,
count_end, end.row,end.col));
return true;
}
private Connection GetOrCreateConnection(
BoardElement end,
Connection connection) {
if (connection!= null) { return connection; }
return new Connection();
}
#TargetApi(Build.VERSION_CODES.N)
private void InitializeEasy() {
Random rand = new Random();
String[][] debug_board_state = new String[7][7];
setCurrentState(new State(WIDTH_EASY));
for (int row = 0; row < debug_board_state.length; row++) {
for (int column = 0; column < debug_board_state[row].length; column++) {
debug_board_state[row][column] = String.valueOf(rand.nextInt(5));
}
}
for (int row = 0; row < debug_board_state.length; row++) {
for (int column = 0; column < debug_board_state[row].length; column++) {
System.out.print(debug_board_state[row][column] + " ");
}
System.out.println();
}
for (int row = 0; row < WIDTH_EASY; ++row) {
for (int column = 0; column < WIDTH_EASY; ++column) {
getCurrentState().board_elements[row][column] = new BoardElement();
getCurrentState().board_elements[row][column].max_connecting_bridges = Integer.parseInt(debug_board_state[row][column]);
getCurrentState().board_elements[row][column].row = row;
getCurrentState().board_elements[row][column].col = column;
if (getCurrentState().board_elements[row][column].max_connecting_bridges > 0) {
getCurrentState().board_elements[row][column].is_island = true;
}
}
}
}
private void setCurrentState(State new_state) {
this.current_state = new_state;
}
public State getCurrentState() {
return current_state;
}
}
What algorithm could I use to make sure the Map can be Solved (Islands Connected with Bridges) before spawning the nodes.
This is what the map looks like (don't mind the design)
One thing to consider would be to start with a blank board. Place an island. Then place another island that can be connected to the first one (i.e. on one of the four cardinal directions). Connect the two with a bridge, and increment each island's count.
Now, pick one of the two islands and place another island that it can connect. Add the bridge and increment.
Continue in this way until you've placed the number of islands that you want to place.
The beauty here is that you start with an empty board, and during construction the board is always valid.
You'll have to ensure that you're not crossing bridges when you place new islands, but that's pretty easy to do, since you know where the existing bridges are.
I have a program with two tables, no Jtables, an I want to add drag and drop.
it is nearly working with:
public void drop(DropTargetEvent event) {
if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) {
DropTarget target = (DropTarget) event.widget;
TableItem item = (TableItem) event.item;
String data = (String) event.data;
int col=1;
item.setText(col,data);
System.out.println(" DROP: " + data);
}
}
With event.item I got the right row, but how did I get the right column for "int col" when I released the mouse button?
thx to greg-449, so it is working
public void drop(DropTargetEvent event) {
if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) {
// Get the dropped data
DropTarget target = (DropTarget) event.widget;
Table table = (Table) target.getControl();
TableItem item = (TableItem) event.item;
String data = calc.KM_DEC_KM_V((String) event.data);
int col=1;
Control absolut = getParent().getDisplay().getFocusControl();
if (item != null){
col = getColumn(table,event.x + absolut.toControl(item.getBounds(0).x,item.getBounds(0).y).x);
if (col==1 || col==2) {
item.setText(col,data);
}
}
table.redraw();
System.out.println(" DROP: " + data);
}
}
with
private static int getColumn(Table table, int x)
{
int overallWidth = 0;
for(int i = 0; i < table.getColumnCount(); i++)
{
overallWidth += table.getColumn(i).getWidth();
System.out.println("overallWidth "+ overallWidth);
if(x < overallWidth)
{
return i;
}
}
return -1;
}
The class is extracted from a game project. Everything works as it should, I can play the game, start a new game, etc. Only the .gif pics are not displayed. Where in my project do I have to store the .gif pics, so that the image path works? I hope the code is understandable.
class Field extends JPanel {
//fields in x direction
static final int max_x = 10;
//fields in y direction
static final int max_y = 10;
//array for field
JLabel[][] label = new JLabel [max_x][max_y];
//home-directory
String img_source = MainWindow.getDirectory();
//separator
String separator = MainWindow.getSeparator();
//stringarray for different viewing direction
String [] viewing_direction = {"player.gif", "player1.gif", "player2.gif", "player3.gif", "player4.gif", null, "player6.gif", "player7.gif", "player8.gif", "player9.gif", "loss.gif", "win.gif"};
//integer for last viewing direction
int viewing_direction_Player = 0;
Field() {
super();
this.setLayout(new GridLayout(max_x, max_y));
//set theme "game"
img_source = img_source + "image" + separator + "Game";
addJLabels();
}
//creates field with alternating background
private void addJLabels() {
for (int i = 0; i < max_y; i++) {
for (int k = 0; k < max_x; k++) {
label[i][k] = new JLabel();
if((i+k) % 2 == 0) { //modulo operation for alternating background
label[i][k].setBackground(Color.BLACK);
}
else {
label[i][k].setBackground(Color.WHITE);
}
label[i][k].setPreferredSize(new Dimension(50, 50));
label[i][k].setOpaque(true);
this.add(label[i][k]);
}
}
}
//method to get last viewing direction
public void setLastDirection(int lastDirection) {
viewing_direction_Player = lastDirection;
}
//method that draws all pawns
public void drawAllPawns(AbstractPawn[] pawns) {
for(int i = 0; i < (int) pawns.length; i++) {
if ((pawns[i].getX() < 0) || (pawns[i].getY() < 0)) {
} else if (pawns[i] instanceof Player) {
label[pawns[i].getY()][pawns[i].getX()].setIcon(new ImageIcon(img_source + separator + viewing_direction[viewing_direction_Player]));
} else if(pawns[i] instanceof Opponent) {
label[pawns[i].getY()][pawns[i].getX()].setIcon(new ImageIcon(img_source + separator + "opponent.gif"));
} else if(pawns[i] instanceof Vortex) {
label[pawns[i].getY()][pawns[i].getX()].setIcon(new ImageIcon(img_source + separator + "vortex.gif"));
} else if(pawns[i] instanceof Obstacle) {
label[pawns[i].getY()][pawns[i].getX()].setIcon(new ImageIcon(img_source + separator + "obstacle.gif"));
} else if(pawns[i] instanceof Destruction) {
label[pawns[i].getY()][pawns[i].getX()].setIcon(new ImageIcon(img_source + separator + "destruction.gif"));
} else if(pawns[i] instanceof Ammo) {
label[pawns[i].getY()][pawns[i].getX()].setIcon(new ImageIcon(img_source + separator + "ammo.gif"));
}
}
}
Here is the getDirectory from my mainwindow class
public static String getDirectory() {
String home = System.getProperty("user.dir");
return (home + getSeparator());
}
Simple way to use image
First drag and drop image in src folder
than type this code
ImageIcon ic = new ImageIcon(getResource().getClass("Example.jpg")); // image name instead of Example.jpg
OK, I don't know how to word this question, but maybe my code will spell out the problem:
public class ControllerTest
{
public static void main(String [] args)
{
GamePadController rockbandDrum = new GamePadController();
DrumMachine drum = new DrumMachine();
while(true)
{
try{
rockbandDrum.poll();
if(rockbandDrum.isButtonPressed(1)) //BLUE PAD HhiHat)
{
drum.playSound("hiHat.wav");
Thread.sleep(50);
}
if(rockbandDrum.isButtonPressed(2)) //GREEN PAD (Crash)
{
//Todo: Change to Crash
drum.playSound("hiHat.wav");
Thread.sleep(50);
}
//Etc....
}
}
}
public class DrumMachine
{
InputStream soundPlayer = null;
AudioStream audio = null;
static boolean running = true;
public void playSound(String soundFile)
{
//Tak a sound file as a paramater and then
//play that sound file
try{
soundPlayer = new FileInputStream(soundFile);
audio = new AudioStream(soundPlayer);
}
catch(FileNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
AudioPlayer.player.start(audio);
}
//Etc... Methods for multiple audio clip playing
}
Now the problem is, if I lower the delay in the
Thread.sleep(50)
then the sound plays multiple times a second, but if I keep at this level or any higher, I could miss sounds being played...
It's an odd problem, where if the delay is too low, the sound loops. But if it's too high it misses playing sounds. Is this just a problem where I would need to tweak the settings, or is there any other way to poll the controller without looping sound?
Edit: If I need to post the code for polling the controller I will...
import java.io.*;
import net.java.games.input.*;
import net.java.games.input.Component.POV;
public class GamePadController
{
public static final int NUM_BUTTONS = 13;
// public stick and hat compass positions
public static final int NUM_COMPASS_DIRS = 9;
public static final int NW = 0;
public static final int NORTH = 1;
public static final int NE = 2;
public static final int WEST = 3;
public static final int NONE = 4; // default value
public static final int EAST = 5;
public static final int SW = 6;
public static final int SOUTH = 7;
public static final int SE = 8;
private Controller controller;
private Component[] comps; // holds the components
// comps[] indices for specific components
private int xAxisIdx, yAxisIdx, zAxisIdx, rzAxisIdx;
// indices for the analog sticks axes
private int povIdx; // index for the POV hat
private int buttonsIdx[]; // indices for the buttons
private Rumbler[] rumblers;
private int rumblerIdx; // index for the rumbler being used
private boolean rumblerOn = false; // whether rumbler is on or off
public GamePadController()
{
// get the controllers
ControllerEnvironment ce =
ControllerEnvironment.getDefaultEnvironment();
Controller[] cs = ce.getControllers();
if (cs.length == 0) {
System.out.println("No controllers found");
System.exit(0);
}
else
System.out.println("Num. controllers: " + cs.length);
// get the game pad controller
controller = findGamePad(cs);
System.out.println("Game controller: " +
controller.getName() + ", " +
controller.getType());
// collect indices for the required game pad components
findCompIndices(controller);
findRumblers(controller);
} // end of GamePadController()
private Controller findGamePad(Controller[] cs)
/* Search the array of controllers until a suitable game pad
controller is found (eith of type GAMEPAD or STICK).
*/
{
Controller.Type type;
int i = 0;
while(i < cs.length) {
type = cs[i].getType();
if ((type == Controller.Type.GAMEPAD) ||
(type == Controller.Type.STICK))
break;
i++;
}
if (i == cs.length) {
System.out.println("No game pad found");
System.exit(0);
}
else
System.out.println("Game pad index: " + i);
return cs[i];
} // end of findGamePad()
private void findCompIndices(Controller controller)
/* Store the indices for the analog sticks axes
(x,y) and (z,rz), POV hat, and
button components of the controller.
*/
{
comps = controller.getComponents();
if (comps.length == 0) {
System.out.println("No Components found");
System.exit(0);
}
else
System.out.println("Num. Components: " + comps.length);
// get the indices for the axes of the analog sticks: (x,y) and (z,rz)
xAxisIdx = findCompIndex(comps, Component.Identifier.Axis.X, "x-axis");
yAxisIdx = findCompIndex(comps, Component.Identifier.Axis.Y, "y-axis");
zAxisIdx = findCompIndex(comps, Component.Identifier.Axis.Z, "z-axis");
rzAxisIdx = findCompIndex(comps, Component.Identifier.Axis.RZ, "rz-axis");
// get POV hat index
povIdx = findCompIndex(comps, Component.Identifier.Axis.POV, "POV hat");
findButtons(comps);
} // end of findCompIndices()
private int findCompIndex(Component[] comps,
Component.Identifier id, String nm)
/* Search through comps[] for id, returning the corresponding
array index, or -1 */
{
Component c;
for(int i=0; i < comps.length; i++) {
c = comps[i];
if ((c.getIdentifier() == id) && !c.isRelative()) {
System.out.println("Found " + c.getName() + "; index: " + i);
return i;
}
}
System.out.println("No " + nm + " component found");
return -1;
} // end of findCompIndex()
private void findButtons(Component[] comps)
/* Search through comps[] for NUM_BUTTONS buttons, storing
their indices in buttonsIdx[]. Ignore excessive buttons.
If there aren't enough buttons, then fill the empty spots in
buttonsIdx[] with -1's. */
{
buttonsIdx = new int[NUM_BUTTONS];
int numButtons = 0;
Component c;
for(int i=0; i < comps.length; i++) {
c = comps[i];
if (isButton(c)) { // deal with a button
if (numButtons == NUM_BUTTONS) // already enough buttons
System.out.println("Found an extra button; index: " + i + ". Ignoring it");
else {
buttonsIdx[numButtons] = i; // store button index
System.out.println("Found " + c.getName() + "; index: " + i);
numButtons++;
}
}
}
// fill empty spots in buttonsIdx[] with -1's
if (numButtons < NUM_BUTTONS) {
System.out.println("Too few buttons (" + numButtons +
"); expecting " + NUM_BUTTONS);
while (numButtons < NUM_BUTTONS) {
buttonsIdx[numButtons] = -1;
numButtons++;
}
}
} // end of findButtons()
private boolean isButton(Component c)
/* Return true if the component is a digital/absolute button, and
its identifier name ends with "Button" (i.e. the
identifier class is Component.Identifier.Button).
*/
{
if (!c.isAnalog() && !c.isRelative()) { // digital and absolute
String className = c.getIdentifier().getClass().getName();
// System.out.println(c.getName() + " identifier: " + className);
if (className.endsWith("Button"))
return true;
}
return false;
} // end of isButton()
private void findRumblers(Controller controller)
/* Find the rumblers. Use the last rumbler for making vibrations,
an arbitrary decision. */
{
// get the game pad's rumblers
rumblers = controller.getRumblers();
if (rumblers.length == 0) {
System.out.println("No Rumblers found");
rumblerIdx = -1;
}
else {
System.out.println("Rumblers found: " + rumblers.length);
rumblerIdx = rumblers.length-1; // use last rumbler
}
} // end of findRumblers()
// ----------------- polling and getting data ------------------
public void poll()
// update the component values in the controller
{
controller.poll();
}
public int getXYStickDir()
// return the (x,y) analog stick compass direction
{
if ((xAxisIdx == -1) || (yAxisIdx == -1)) {
System.out.println("(x,y) axis data unavailable");
return NONE;
}
else
return getCompassDir(xAxisIdx, yAxisIdx);
} // end of getXYStickDir()
public int getZRZStickDir()
// return the (z,rz) analog stick compass direction
{
if ((zAxisIdx == -1) || (rzAxisIdx == -1)) {
System.out.println("(z,rz) axis data unavailable");
return NONE;
}
else
return getCompassDir(zAxisIdx, rzAxisIdx);
} // end of getXYStickDir()
private int getCompassDir(int xA, int yA)
// Return the axes as a single compass value
{
float xCoord = comps[ xA ].getPollData();
float yCoord = comps[ yA ].getPollData();
// System.out.println("(x,y): (" + xCoord + "," + yCoord + ")");
int xc = Math.round(xCoord);
int yc = Math.round(yCoord);
// System.out.println("Rounded (x,y): (" + xc + "," + yc + ")");
if ((yc == -1) && (xc == -1)) // (y,x)
return NW;
else if ((yc == -1) && (xc == 0))
return NORTH;
else if ((yc == -1) && (xc == 1))
return NE;
else if ((yc == 0) && (xc == -1))
return WEST;
else if ((yc == 0) && (xc == 0))
return NONE;
else if ((yc == 0) && (xc == 1))
return EAST;
else if ((yc == 1) && (xc == -1))
return SW;
else if ((yc == 1) && (xc == 0))
return SOUTH;
else if ((yc == 1) && (xc == 1))
return SE;
else {
System.out.println("Unknown (x,y): (" + xc + "," + yc + ")");
return NONE;
}
} // end of getCompassDir()
public int getHatDir()
// Return the POV hat's direction as a compass direction
{
if (povIdx == -1) {
System.out.println("POV hat data unavailable");
return NONE;
}
else {
float povDir = comps[povIdx].getPollData();
if (povDir == POV.CENTER) // 0.0f
return NONE;
else if (povDir == POV.DOWN) // 0.75f
return SOUTH;
else if (povDir == POV.DOWN_LEFT) // 0.875f
return SW;
else if (povDir == POV.DOWN_RIGHT) // 0.625f
return SE;
else if (povDir == POV.LEFT) // 1.0f
return WEST;
else if (povDir == POV.RIGHT) // 0.5f
return EAST;
else if (povDir == POV.UP) // 0.25f
return NORTH;
else if (povDir == POV.UP_LEFT) // 0.125f
return NW;
else if (povDir == POV.UP_RIGHT) // 0.375f
return NE;
else { // assume center
System.out.println("POV hat value out of range: " + povDir);
return NONE;
}
}
} // end of getHatDir()
public boolean[] getButtons()
/* Return all the buttons in a single array. Each button value is
a boolean. */
{
boolean[] buttons = new boolean[NUM_BUTTONS];
float value;
for(int i=0; i < NUM_BUTTONS; i++) {
value = comps[ buttonsIdx[i] ].getPollData();
buttons[i] = ((value == 0.0f) ? false : true);
}
return buttons;
} // end of getButtons()
public boolean isButtonPressed(int pos)
/* Return the button value (a boolean) for button number 'pos'.
pos is in the range 1-NUM_BUTTONS to match the game pad
button labels.
*/
{
if ((pos < 1) || (pos > NUM_BUTTONS)) {
System.out.println("Button position out of range (1-" +
NUM_BUTTONS + "): " + pos);
return false;
}
if (buttonsIdx[pos-1] == -1) // no button found at that pos
return false;
float value = comps[ buttonsIdx[pos-1] ].getPollData();
// array range is 0-NUM_BUTTONS-1
return ((value == 0.0f) ? false : true);
} // end of isButtonPressed()
// ------------------- Trigger a rumbler -------------------
public void setRumbler(boolean switchOn)
// turn the rumbler on or off
{
if (rumblerIdx != -1) {
if (switchOn)
rumblers[rumblerIdx].rumble(0.8f); // almost full on for last rumbler
else // switch off
rumblers[rumblerIdx].rumble(0.0f);
rumblerOn = switchOn; // record rumbler's new status
}
} // end of setRumbler()
public boolean isRumblerOn()
{ return rumblerOn; }
} // end of GamePadController class
I think you are using the wrong design pattern here. You should use the observer pattern for this type of thing.
A polling loop not very efficient, and as you've noticed doesn't really yield the desired results.
I'm not sure what you are using inside your objects to detect if a key is pressed, but if it's a GUI architecture such as Swing or AWT it will be based on the observer pattern via the use of EventListeners, etc.
Here is a (slightly simplified) Observer-pattern
applied to your situation.
The advantage of this design is that when a button
is pressed and hold, method 'buttonChanged' will
still only be called once, instead of start
'repeating' every 50 ms.
public static final int BUTTON_01 = 0x00000001;
public static final int BUTTON_02 = 0x00000002;
public static final int BUTTON_03 = 0x00000004;
public static final int BUTTON_04 = 0x00000008; // hex 8 == dec 8
public static final int BUTTON_05 = 0x00000010; // hex 10 == dec 16
public static final int BUTTON_06 = 0x00000020; // hex 20 == dec 32
public static final int BUTTON_07 = 0x00000040; // hex 40 == dec 64
public static final int BUTTON_08 = 0x00000080; // etc.
public static final int BUTTON_09 = 0x00000100;
public static final int BUTTON_10 = 0x00000200;
public static final int BUTTON_11 = 0x00000400;
public static final int BUTTON_12 = 0x00000800;
private int previousButtons = 0;
void poll()
{
rockbandDrum.poll();
handleButtons();
}
private void handleButtons()
{
boolean[] buttons = getButtons();
int pressedButtons = getPressedButtons(buttons);
if (pressedButtons != previousButtons)
{
buttonChanged(pressedButtons); // Notify 'listener'.
previousButtons = pressedButtons;
}
}
public boolean[] getButtons()
{
// Return all the buttons in a single array. Each button-value is a boolean.
boolean[] buttons = new boolean[MAX_NUMBER_OF_BUTTONS];
float value;
for (int i = 0; i < MAX_NUMBER_OF_BUTTONS-1; i++)
{
int index = buttonsIndex[i];
if (index < 0) { continue; }
value = comps[index].getPollData();
buttons[i] = ((value == 0.0f) ? false : true);
}
return buttons;
}
private int getPressedButtons(boolean[] array)
{
// Mold all pressed buttons into a single number by OR-ing their values.
int pressedButtons = 0;
int i = 1;
for (boolean isBbuttonPressed : array)
{
if (isBbuttonPressed) { pressedButtons |= getOrValue(i); }
i++;
}
return pressedButtons;
}
private int getOrValue(int btnNumber) // Get a value to 'OR' with.
{
int btnValue = 0;
switch (btnNumber)
{
case 1 : btnValue = BUTTON_01; break;
case 2 : btnValue = BUTTON_02; break;
case 3 : btnValue = BUTTON_03; break;
case 4 : btnValue = BUTTON_04; break;
case 5 : btnValue = BUTTON_05; break;
case 6 : btnValue = BUTTON_06; break;
case 7 : btnValue = BUTTON_07; break;
case 8 : btnValue = BUTTON_08; break;
case 9 : btnValue = BUTTON_09; break;
case 10 : btnValue = BUTTON_10; break;
case 11 : btnValue = BUTTON_11; break;
case 12 : btnValue = BUTTON_12; break;
default : assert false : "Invalid button-number";
}
return btnValue;
}
public static boolean checkButton(int pressedButtons, int buttonToCheckFor)
{
return (pressedButtons & buttonToCheckFor) == buttonToCheckFor;
}
public void buttonChanged(int buttons)
{
if (checkButton(buttons, BUTTON_01)
{
drum.playSound("hiHat.wav");
}
if (checkButton(buttons, BUTTON_02)
{
drum.playSound("crash.wav");
}
}
Please post more information about the GamePadController class that you are using.
More than likely, that same library will offer an "event" API, where a "callback" that you register with a game pad object will be called as soon as the user presses a button. With this kind of setup, the "polling" loop is in the framework, not your application, and it can be much more efficient, because it uses signals from the hardware rather than a busy-wait polling loop.
Okay, I looked at the JInput API, and it is not really event-driven; you have to poll it as you are doing. Does the sound stop looping when you release the button? If so, is your goal to have the sound play just once, and not again until the button is release and pressed again? In that case, you'll need to track the previous button state each time through the loop.
Human response time is about 250 ms (for an old guy like me, anyway). If you are polling every 50 ms, I'd expect the controller to report the button depressed for several iterations of the loop. Can you try something like this:
boolean played = false;
while (true) {
String sound = null;
if (controller.isButtonPressed(1))
sound = "hiHat.wav";
if (controller.isButtonPressed(2))
sound = "crash.wav";
if (sound != null) {
if (!played) {
drum.playSound(sound);
played = true;
}
} else {
played = false;
}
Thread.sleep(50);
}