我试图为gridworld案例研究实施GUI并陷入困境。我正在尝试使用自定义的JTable实现网格。我使用TreeMap实现了BoundedGrid和UnboundedGrid类。但是,UnboundedGrid类需要GUI动态更改行数和列数,即基于填充Grid的元素的位置。 JTable的行数和列数应由具有最大和最小x / y坐标的元素确定。我不知道如何正确扩展AbstractTableModel类。
此外,存储在Grid,Actors中的对象存储其容器Grid及其各自的位置,而Grid接口中的put和remove方法在Actor类中使用,因此Actors上的任何更改都必须调用相应的方法。 Actor类,而不是Grid接口中的那些: 例如,Actor中的一个方法是:
public void putSelfInGrid(Grid<Actor> grid, Location loc){
if (this.grid != null){
throw new IllegalStateException("This actor: " + this + " is already contained in another grid.");
}
Actor actor = grid.get(loc);
if (actor != null) {
actor.removeSelfFromGrid();
}
grid.put(loc, this);
this.grid = grid;
this.location = loc;
}
此方法调用Grid接口中的put()方法并更新Actor对象的位置。这应该被考虑在内,因为我需要一个像GridManager这样的类来存储Grid和actor来更新Grid上的更改。另一个问题:GridManager应该直接扩展JTable,还是应该创建另一个类,如GridTable,它扩展了JTable并使用GridManager自动更新表。
这是我的代码:
https://www.dropbox.com/sh/a6qku7pphrihtf5/AABxOypOCxKlS05i-AeMpBOZa?dl=0
public interface Grid<E> {
E put(Location loc, E obj);
E remove(Location loc);
E get(Location loc);
int getNumRows();
int getNumCols();
boolean isValid(Location loc);
default boolean isEmpty(Location loc){...}
default Set<Location> getValidAdjacentLocations(Location loc){...}
default Optional<Location> getValidAdjacentLocationToward(Location loc, Location.Direction direction){...}
default Set<Location> getEmptyAdjacentLocations(Location loc){...}
default Optional<Location> getEmptyAdjacentLocationToward(Location loc, Location.Direction direction){...}
default Set<Location> getOccupiedAdjacentLocations(Location loc){...}
default Optional<Location> getOccupiedAdjacentLocationToward(Location loc, Location.Direction direction){...}
default Set<E> getNeighbors(Location loc){...}
Set<Location> getOccupiedLocations();
default Set<E> getAllElements(){...}
}
public abstract class AbstractGrid<E> implements Grid<E>{
private final int rowNum;
private final int colNum;
protected AbstractGrid(){
this(-1, -1);
};
protected AbstractGrid(int rowNum, int colNum){
if (rowNum < -1 || colNum < -1){
throw new IllegalArgumentException("Invalid Dimension");
}
this.rowNum = rowNum;
this.colNum = colNum;
}
protected AbstractGrid(Dimension dimension){
this(dimension.height, dimension.width);
}
@Override
public int getNumRows() {
return rowNum;
}
@Override
public int getNumCols() {
return colNum;
}
//toString, equals and hashCode ...
}
public class Location implements Comparable<Location>{
public static enum Direction{
NORTH(0),
NORTHEAST(45),
EAST(90),
SOUTHEAST(135),
SOUTH(180),
SOUTHWEST(225),
WEST(270),
NORTHWEST(315),
;
private final int value;
private Direction(int value){
this.value = value;
}
public int getValue() {
return value;
}
public Direction turn(Turning turning){
int newDegree = Math.floorMod(getValue() + turning.getValue(), 360);
for (Direction direction : Direction.values()) {
if (direction.getValue() == newDegree) {
return direction;
}
}
throw new UnknownError("missing direction values");
}
public int getColOffset(){
if ((getValue() == 0 || getValue() == 180)) {
return 0;
} else if (0 < getValue() && getValue() < 180){
return 1;
} else if (180 < getValue() && getValue() < 360){
return -1;
} else{
throw new UnknownError("unexpected value");
}
}
public int getRowOffset(){
if (getValue() == 90 || getValue() == 270){
return 0;
} else if (90 < getValue() && getValue() < 270){
return 1;
} else if ((0 <= getValue() && getValue() < 90) || (270 < getValue() && getValue() < 360)){
return -1;
} else {
throw new UnknownError("unexpected value");
}
}
}
public static enum Turning{
AHEAD(0),
HALF_RIGHT(45),
HALF_LEFT(-45),
RIGHT(90),
LEFT(-90),
HALF_CIRCLE(180),
CIRCLE(360),
;
private final int value;
private Turning(int value){
this.value = value;
}
public int getValue() {
return value;
}
}
private final int row;
private final int col;
public Location(int row, int col){
this.row = row;
this.col = col;
}
public int getCol() {
return col;
}
public int getRow() {
return row;
}
public Location getAdjacentLocation(Direction direction){...}
public Set<Location> getAdjacentLocations(){...}
public Direction getDirectionToward(Location target){...}
@Override
public int compareTo(Location o) {
if (this.row != o.row){
return (int)Math.signum(Integer.compare(this.row, o.row));
} else {
return (int)Math.signum(Integer.compare(this.col, o.col));
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Location)) return false;
Location location = (Location) o;
if (col != location.col) return false;
if (row != location.row) return false;
return true;
}
//hashCode and toString
}
public class BoundedGrid<E> extends AbstractGrid<E>{
private Map<Location, E> occupants;
public BoundedGrid(int rowNum, int colNum) {
super(rowNum, colNum);
occupants = new TreeMap<>();
}
@Override
public E put(Location loc, E obj) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.put(loc, obj);
}
@Override
public E remove(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.remove(loc);
}
@Override
public E get(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.get(loc);
}
@Override
public boolean isValid(Location loc) {
return 0 <= loc.getRow() && loc.getRow() < getNumRows() && 0 <= loc.getCol() && loc.getCol() < getNumCols();
}
@Override
public Set<Location> getOccupiedLocations() {
return occupants.keySet();
}
}
public class UnboundedGrid<E> extends AbstractGrid<E>{
public static enum Characteristics{
HORIZONTALLY_BOUNDED, VERTICALLY_BOUNDED, UNBOUNDED;
}
private Characteristics characteristics;
private Map<Location, E> occupants;
public UnboundedGrid(){
super();
characteristics = Characteristics.UNBOUNDED;
occupants = new TreeMap<>();
}
public UnboundedGrid(Characteristics characteristics, int num){
super(characteristics == Characteristics.HORIZONTALLY_BOUNDED ? new Dimension(num, -1) : (characteristics == Characteristics.VERTICALLY_BOUNDED ? new Dimension(-1, num) : new Dimension(-1, -1)));
this.characteristics = characteristics;
occupants = new TreeMap<>();
}
@Override
public E put(Location loc, E obj) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.put(loc, obj);
}
@Override
public E remove(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.remove(loc);
}
@Override
public E get(Location loc) {
if (!isValid(loc)){
throw new IllegalArgumentException("Location " + loc + "is invalid");
}
return occupants.get(loc);
}
@Override
public boolean isValid(Location loc) {
switch (characteristics){
case HORIZONTALLY_BOUNDED:
return 0 <= loc.getCol() && loc.getCol() < getNumCols();
case VERTICALLY_BOUNDED:
return 0 <= loc.getRow() && loc.getRow() < getNumRows();
case UNBOUNDED:
return true;
default:
throw new UnknownError("Check UnboundedGrid.Characteristics");
}
}
@Override
public Set<Location> getOccupiedLocations() {
return occupants.keySet();
}
}