I've been following this tutorial here: Link to tutorial. I can't seem to get the application displaying properly though. When I run the application I expect to see a screen like CalendarCanvas from tutorial, but I get this:
Here is my code, I'm using standard MIDP classes.
Class CreateCalendar:
import java.util.Date;
import java.util.Calendar;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class CreateCalendar
{
/**
* Array of strings which holds data for the month and day
* for the calendar application.
*/
static final String[] month_labels = new String[]
{
"January", "Febuary", "March", "April", "May", "June", "July", "August", "Sepetember", "October", "November", "Decemeber"
};
static final String[] weekdays_labels = new String[]
{
"Mon", "Tue", "Wed", "Thur", "Fri", "Sat", "Sun"
};
public int startWeekday = 0;
public int padding = 1;
public int borderWidth = 4;
public int borderColor = 0x009900;
/**
* Weekday Labels
*/
public Font weekdayFont = Font.getDefaultFont();
public int weekdayBackgroundColor = 0x009900;
public int weekdayColor = 0xffffff;
/**
* Month/Year Labels
*/
public Font headerFont = Font.getDefaultFont();
public int headerBackgroundColor = 0x009900;
public int headerColor = 0xffffff;
/**
* Cells Labels
*/
public Font font = Font.getDefaultFont();
public int foreColor = 0xffffff;
public int backgroundColor = 0x009900;
public int selectedBackgroundColor = 0xCCFF00;
public int selectedForegroundColor = 0xffffff;
/**
* Size properties
*/
int width = 0;
int height = 0;
int headerHeight = 0;
int weekHeight = 0;
int cellWidth = 0;
int cellHeight = 0;
/**
* Internal time properties
*/
long currentTimeStamp = 0;
Calendar calendar = null;
int weeks = 0;
public CreateCalendar(Date date)
{
calendar = Calendar.getInstance();
setDate(date);
initialize();
}
public Date getSelectedDate()
{
return calendar.getTime();
}
public void setDate(Date d)
{
currentTimeStamp = d.getTime();
calendar.setTime(d);
this.weeks = (int)Math.ceil(((double)getStartWeekday() + getMonthDays()) / 7);
}
public void setDate(long timestamp)
{
setDate(new Date(timestamp));
}
public void initialize()
{
this.cellWidth = font.stringWidth("MM") + 2 * padding;
this.cellHeight = font.getHeight() + 2 * padding;
this.headerHeight = headerFont.getHeight() + 2 * padding;
this.weekHeight = weekdayFont.getHeight() + 2 * padding;
this.width = 7 * (cellWidth + borderWidth) + borderWidth;
initHeight();
}
void initHeight()
{
this.height = headerHeight + weekHeight + this.weeks * (cellHeight + borderWidth) + borderWidth;
}
int getMonthDays()
{
int month = calendar.get(Calendar.MONTH);
switch (month)
{
case 3:
case 5:
case 8:
case 10:
return 30;
case 1:
int year = calendar.get(Calendar.YEAR);
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ? 29 : 28;
default:
return 31;
}
}
int getStartWeekday()
{
Calendar c = Calendar.getInstance();
c.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
c.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
c.set(Calendar.DAY_OF_MONTH, 1);
return (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
}
public void KeyPressed(int key)
{
switch(key)
{
case Canvas.UP:
go(-7);
break;
case Canvas.DOWN:
go(7);
break;
case Canvas.RIGHT:
go(1);
break;
case Canvas.LEFT:
go(-1);
break;
}
}
void go(int delta)
{
int prevMonth = calendar.get(Calendar.MONTH);
setDate(currentTimeStamp + 864000000 * delta);
if(calendar.get(Calendar.MONTH) != prevMonth)
{
initHeight();
}
}
public void paint(Graphics g)
{
g.setColor(backgroundColor);
g.fillRect(0, 0, width, height);
g.setFont(headerFont);
g.setColor(headerColor);
g.drawString(month_labels[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.YEAR), width / 2, padding, Graphics.TOP | Graphics.HCENTER);
g.translate(0, headerHeight);
g.setColor(weekdayBackgroundColor);
g.fillRect(0, 0, width, weekHeight);
g.setColor(weekdayColor);
g.setFont(weekdayFont);
for(int i = 0; i < 7; i++)
{
g.drawString(weekdays_labels[(i + startWeekday) % 7], borderWidth + i * (cellWidth + borderWidth) + cellWidth / 2, padding, Graphics.TOP | Graphics.HCENTER);
}
g.translate(0, weekHeight);
g.setColor(borderColor);
for(int i = 0; i <= weeks; i++)
{
g.fillRect(0, i * (cellHeight + borderWidth), width, borderWidth);
}
for(int i = 0; i <=7; i++)
{
g.fillRect(i * (cellWidth + borderWidth), 0, borderWidth, height - headerHeight - weekHeight);
}
int days = getMonthDays();
int dayIndex = (getStartWeekday() - this.startWeekday + 7) % 7;
g.setColor(foreColor);
int currentDay = calendar.get(Calendar.DAY_OF_MONTH);
for(int i = 0; i < days; i++)
{
int weekday = (dayIndex + i) % 7;
int row = (dayIndex + i) / 7;
int x = borderWidth + weekday * (cellWidth + borderWidth) + cellWidth / 2;
int y = borderWidth + row * (cellHeight + cellWidth) + padding;
if(i + 1 == currentDay)
{
g.setColor(selectedBackgroundColor);
g.fillRect(borderWidth + weekday * (cellWidth + borderWidth), borderWidth + row * (cellHeight + borderWidth), cellWidth, cellHeight);
g.setColor(selectedForegroundColor);
}
g.drawString("" + (i + 1), x, y, Graphics.TOP | Graphics.HCENTER);
if(i + 1 == currentDay)
{
g.setColor(foreColor);
}
}
g.translate(0, - headerHeight - weekHeight);
}
private Date getTime() {
throw new UnsupportedOperationException("Not yet implemented"); //TODO get current Time
}
Class CalFrontEnd (extends MIDlet):
public class CalFrontEnd extends MIDlet
{
public CreateCalendar calendar;
protected Display display;
protected Form mainForm;
public CalFrontEnd()
{
}
public void startApp()
{
calendar = new CreateCalendar(new Date());
calendar.headerFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE);
calendar.weekdayFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
calendar.weekdayBackgroundColor = 0xccccff;
calendar.weekdayColor = 0x0000ff;
calendar.headerColor = 0xffffff;
calendar.initialize();
display.getDisplay(this).setCurrent(
new intCalendar(this));
}
public void pauseApp()
{
}
public void destroyApp(boolean destroy)
{
notifyDestroyed();
}
}}
Code in the class CreateCalendar looks very problematic.
In prior question you mentioned few minor variable name differences done to code from tutorial, but from what is shown in your code snippet, this is not so.
To find a way to reuse tutorial code, most straightforward approach would be like as follows.
Copy the source code from tutorial - files CalendarWidget.java and CalendarCanvas.java
Copy as-is, only adjust the package statements if necessary.
Modify CalFrontEnd about as follows
if needed, add import statement for CalendarCanvas
replace current code in startApp with simplest invocation for CalendarCanvas, like this:
public void startApp() {
Display.getDisplay(this).setCurrent(
new CalendarCanvas(this));
}
Test the code, tune and fix it until your MIDlet shows what you would expect of CalendarCanvas
After above is done, proceed with modifying the code to further match your needs.
Don't forget to test the changes you make, to make sure that things indeed work as you expect.
Related
So basically, it's as the title says. I'm implementing a recycle view inside a fragment to create a map on one part of the screen. I need two recycle views on the page but only one isn't working. When I run the debugger in Android Studio it never reaches the override methods in the Adapter class. Below is the code, unfortunately it's a lot.
I should also mention
Number of items is caclulated and is not zero
Adapter is added to recycler view
Layout manager is added to recycler view
Why aren't any of the overriden methods being called?
Please let me know if you require further information
FRAGMENT
public class FragmentMap extends Fragment {
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
MapData mapData;
StructureData structures;
public FragmentMap(MapData mapData, StructureData structures)
{
this.mapData = mapData;
this.structures = structures;
}
public FragmentMap() {
// Required empty public constructor
}
public static FragmentMap newInstance(String param1, String param2) {
FragmentMap fragment = new FragmentMap();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_map, container, false);
RecyclerView rv = view.findViewById(R.id.mapRecyclerView);
SelectorAdapter myAdapter = new SelectorAdapter(structures);
MapAdapter mapAdapter = new MapAdapter(mapData);
rv.setAdapter(mapAdapter);
rv.setLayoutManager(new GridLayoutManager(
getActivity(),
MapData.HEIGHT,
GridLayoutManager.HORIZONTAL,
false));
return view;
}
}
ADAPTER
public class MapAdapter extends RecyclerView.Adapter<MapViewHolder>{
MapData mapData;
public MapAdapter(MapData mapData)
{
this.mapData = mapData;
}
#NonNull
#Override
public MapViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.grid_cell,parent,false);
MapViewHolder myViewHolder = new MapViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MapViewHolder holder, int position) {
int row = position % MapData.HEIGHT;
int col = position / MapData.HEIGHT;
MapElement mapElement = mapData.get(row, col);
holder.cellOne.setImageResource(mapElement.getNorthEast());
}
#Override
public int getItemCount() {
Log.d("TAG", "getItemCount: " + MapData.HEIGHT * MapData.WIDTH);
return MapData.HEIGHT * MapData.WIDTH;
}
}
VIEWHOLDER
public class MapViewHolder extends RecyclerView.ViewHolder {
ImageView cellOne, cellTwo, cellThree, cellFour, structure;
ConstraintLayout parent;
public MapViewHolder(#NonNull View itemView) {
super(itemView);
cellOne = itemView.findViewById(R.id.imageOne);
cellTwo = itemView.findViewById(R.id.imageTwo);
cellThree = itemView.findViewById(R.id.imageThree);
cellFour = itemView.findViewById(R.id.imageFour);
parent = itemView.findViewById(R.id.parent);
int size = parent.getMeasuredHeight() / MapData.HEIGHT + 1;
ViewGroup.LayoutParams lp = itemView.getLayoutParams();
lp.width = size;
lp.height = size;
}
}
MAPDATA
public class MapData
{
public static final int WIDTH = 30;
public static final int HEIGHT = 10;
private static final int WATER = R.drawable.ic_water;
private static final int[] GRASS = {R.drawable.ic_grass1, R.drawable.ic_grass2,
R.drawable.ic_grass3, R.drawable.ic_grass4};
private static final Random rng = new Random();
private MapElement[][] grid;
private static MapData instance = null;
public static MapData get()
{
if(instance == null)
{
instance = new MapData(generateGrid());
}
return instance;
}
private static MapElement[][] generateGrid()
{
final int HEIGHT_RANGE = 256;
final int WATER_LEVEL = 112;
final int INLAND_BIAS = 24;
final int AREA_SIZE = 1;
final int SMOOTHING_ITERATIONS = 2;
int[][] heightField = new int[HEIGHT][WIDTH];
for(int i = 0; i < HEIGHT; i++)
{
for(int j = 0; j < WIDTH; j++)
{
heightField[i][j] =
rng.nextInt(HEIGHT_RANGE)
+ INLAND_BIAS * (
Math.min(Math.min(i, j), Math.min(HEIGHT - i - 1, WIDTH - j - 1)) -
Math.min(HEIGHT, WIDTH) / 4);
}
}
int[][] newHf = new int[HEIGHT][WIDTH];
for(int s = 0; s < SMOOTHING_ITERATIONS; s++)
{
for(int i = 0; i < HEIGHT; i++)
{
for(int j = 0; j < WIDTH; j++)
{
int areaSize = 0;
int heightSum = 0;
for(int areaI = Math.max(0, i - AREA_SIZE);
areaI < Math.min(HEIGHT, i + AREA_SIZE + 1);
areaI++)
{
for(int areaJ = Math.max(0, j - AREA_SIZE);
areaJ < Math.min(WIDTH, j + AREA_SIZE + 1);
areaJ++)
{
areaSize++;
heightSum += heightField[areaI][areaJ];
}
}
newHf[i][j] = heightSum / areaSize;
}
}
int[][] tmpHf = heightField;
heightField = newHf;
newHf = tmpHf;
}
MapElement[][] grid = new MapElement[HEIGHT][WIDTH];
for(int i = 0; i < HEIGHT; i++)
{
for(int j = 0; j < WIDTH; j++)
{
MapElement element;
if(heightField[i][j] >= WATER_LEVEL)
{
boolean waterN = (i == 0) || (heightField[i - 1][j] < WATER_LEVEL);
boolean waterE = (j == WIDTH - 1) || (heightField[i][j + 1] < WATER_LEVEL);
boolean waterS = (i == HEIGHT - 1) || (heightField[i + 1][j] < WATER_LEVEL);
boolean waterW = (j == 0) || (heightField[i][j - 1] < WATER_LEVEL);
boolean waterNW = (i == 0) || (j == 0) || (heightField[i - 1][j - 1] < WATER_LEVEL);
boolean waterNE = (i == 0) || (j == WIDTH - 1) || (heightField[i - 1][j + 1] < WATER_LEVEL);
boolean waterSW = (i == HEIGHT - 1) || (j == 0) || (heightField[i + 1][j - 1] < WATER_LEVEL);
boolean waterSE = (i == HEIGHT - 1) || (j == WIDTH - 1) || (heightField[i + 1][j + 1] < WATER_LEVEL);
boolean coast = waterN || waterE || waterS || waterW ||
waterNW || waterNE || waterSW || waterSE;
grid[i][j] = new MapElement(
!coast,
choose(waterN, waterW, waterNW,
R.drawable.ic_coast_north, R.drawable.ic_coast_west,
R.drawable.ic_coast_northwest, R.drawable.ic_coast_northwest_concave),
choose(waterN, waterE, waterNE,
R.drawable.ic_coast_north, R.drawable.ic_coast_east,
R.drawable.ic_coast_northeast, R.drawable.ic_coast_northeast_concave),
choose(waterS, waterW, waterSW,
R.drawable.ic_coast_south, R.drawable.ic_coast_west,
R.drawable.ic_coast_southwest, R.drawable.ic_coast_southwest_concave),
choose(waterS, waterE, waterSE,
R.drawable.ic_coast_south, R.drawable.ic_coast_east,
R.drawable.ic_coast_southeast, R.drawable.ic_coast_southeast_concave),
null);
}
else
{
grid[i][j] = new MapElement(
false, WATER, WATER, WATER, WATER, null);
}
}
}
return grid;
}
private static int choose(boolean nsWater, boolean ewWater, boolean diagWater,
int nsCoastId, int ewCoastId, int convexCoastId, int concaveCoastId)
{
int id;
if(nsWater)
{
if(ewWater)
{
id = convexCoastId;
}
else
{
id = nsCoastId;
}
}
else
{
if(ewWater)
{
id = ewCoastId;
}
else if(diagWater)
{
id = concaveCoastId;
}
else
{
id = GRASS[rng.nextInt(GRASS.length)];
}
}
return id;
}
protected MapData(MapElement[][] grid)
{
this.grid = grid;
}
public void regenerate()
{
this.grid = generateGrid();
}
public MapElement get(int i, int j)
{
return grid[i][j];
}
}
MAP ELEMENT
public class MapElement
{
private final boolean buildable;
private final int terrainNorthWest;
private final int terrainSouthWest;
private final int terrainNorthEast;
private final int terrainSouthEast;
private Structure structure;
public MapElement(boolean buildable, int northWest, int northEast,
int southWest, int southEast, Structure structure)
{
this.buildable = buildable;
this.terrainNorthWest = northWest;
this.terrainNorthEast = northEast;
this.terrainSouthWest = southWest;
this.terrainSouthEast = southEast;
this.structure = structure;
}
public boolean isBuildable()
{
return buildable;
}
public int getNorthWest()
{
return terrainNorthWest;
}
public int getSouthWest()
{
return terrainSouthWest;
}
public int getNorthEast()
{
return terrainNorthEast;
}
public int getSouthEast()
{
return terrainSouthEast;
}
/**
* Retrieves the structure built on this map element.
* #return The structure, or null if one is not present.
*/
public Structure getStructure()
{
return structure;
}
public void setStructure(Structure structure)
{
this.structure = structure;
}
}
You are initializing only one RecyclerView in onCreateView, if you want to have two Recyclerviews you just need to initialize second one in the same way.
!! This is not my code - I am using this to learn !! However, I do not understand what might cause the sprite to stop it's directional movement when colliding with the "STONE" object. The entire 2-D Game will run and the sprite works properly, but when I move close to the end of the screen or too close to the stone, I get stuck in place. I could not find an explanation I understood completely. I apologize in advance if this is a trivial question to anyone.
Basic Solid Tile Class:
public class BasicSolidTile extends BasicTile {
public BasicSolidTile(int id, int x, int y, int tileColour) {
super(id, x, y, tileColour);
this.solid = true;
}
}
Tile Class:
public abstract class Tile {
public static final Tile[] tiles = new Tile[256];
public static final Tile VOID = new BasicSolidTile(0, 0, 0, Colours.get(000, -1, -1, -1));
public static final Tile STONE = new BasicSolidTile(1, 1, 0, Colours.get(-1, 333, -1, -1));
public static final Tile GRASS = new BasicTile(2, 2, 0, Colours.get(-1, 131, 141, -1));
protected byte id;
protected boolean solid;
protected boolean emitter;
public Tile(int id, boolean isSolid, boolean isEmitter) {
this.id = (byte) id;
if (tiles[id] != null) {
throw new RuntimeException("Duplicate tile id on" + id);
}
this.solid = isSolid;
this.emitter = isEmitter;
tiles[id] = this;
}
public byte getId() {
return id;
}
public boolean isSolid() {
return solid;
}
public boolean isEmitter() {
return emitter;
}
public abstract void render(Screen screen, Level level, int x, int y);
}
Mob Class:
public abstract class Mob extends Entity {
protected String name;
protected int speed;
protected int numSteps = 0;
protected boolean isMoving;
protected int movingDir = 1;
protected int scale = 1;
public Mob(Level level, String name, int x, int y, int speed) {
super(level);
this.name = name;
this.x = x;
this.y = y;
this.speed = speed;
}
public void move(int xa, int ya) {
// you want to check if they are not zero
if (xa != 0 && ya != 0) {
move(xa, 0);
move(0, ya);
numSteps--;
return;
}
numSteps++;
if (!hasCollided(x, y)) {
if (ya < 0)
movingDir = 0;
if (ya > 0)
movingDir = 1;
if (xa < 0)
movingDir = 2;
if (xa > 0)
movingDir = 3;
x += xa * speed;
y += ya * speed;
}
}
public abstract boolean hasCollided(int xa, int ya);
protected boolean isSolidTile(int xa, int ya, int x, int y) {
if (level == null) {
return false;
}
Tile lastTile = level.getTile((this.x + x) >> 3, (this.y + y) >> 3);
Tile newTile = level.getTile((this.x + x + xa) >> 3, (this.y + y + ya) >> 3);
if (!lastTile.equals(newTile) && newTile.isSolid()) {
return true;
}
return false;
}
public String getName() {
return name;
}
}
Level Class:
public class Level {
// array of id's
private byte[] tiles;
public int width;
public int height;
public List<Entity> entities = new ArrayList<Entity>();
public Level(int width, int height) {
tiles = new byte[width * height];
this.width = width;
this.height = height;
this.generateLevel();
}
public void generateLevel() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (x * y %10 < 7) {
tiles[x + y * width] = Tile.GRASS.getId();
} else {
tiles[x + y * width] = Tile.STONE.getId();
}
}
}
}
public void tick() {
// loop through all the vars if you dont need an index var
for (Entity e : entities) {
e.tick();
}
}
public void renderTiles(Screen screen, int xOffset, int yOffset) {
if (xOffset < 0)
xOffset = 0;
if (xOffset > ((width << 3) - screen.width))
xOffset = ((width << 3) - screen.width);
if (yOffset < 0)
yOffset = 0;
if (yOffset > ((height << 3) - screen.height))
yOffset = ((height << 3) - screen.height);
screen.setOffset(xOffset, yOffset);
for (int y = (yOffset >> 3); y < (yOffset + screen.height >> 3) + 1; y++) {
for (int x = (xOffset >> 3); x < (xOffset + screen.width >> 3) + 1; x++) {
getTile(x, y).render(screen, this, x << 3, y << 3);
}
}
}
public void renderEntities(Screen screen) {
for (Entity e : entities) {
e.render(screen);
}
}
public Tile getTile(int x, int y) {
if (0 > x || x >= width || 0 > y || y >= height)
return Tile.VOID;
return Tile.tiles[tiles[x + y * width]];
}
public void addEntity(Entity entity) {
this.entities.add(entity);
}
}
Player Class:
public class Player extends Mob {
private InputHandler input;
private int colour = Colours.get(-1, 111, 145, 543);
private int scale = 1;
public Player(Level level, int x, int y, InputHandler input) {
super(level, "Player", x, y, 1);
this.input = input;
}
public void tick() {
int xa = 0;
int ya = 0;
if (input.up.isPressed()) {
ya--;
}
if (input.down.isPressed()) {
ya++;
}
if (input.left.isPressed()) {
xa--;
}
if (input.right.isPressed()) {
xa++;
}
if (xa != 0 || ya != 0) {
move(xa, ya);
isMoving = true;
} else {
isMoving = false;
}
this.scale = 1;
}
public void render(Screen screen) {
int xTile = 0;
int yTile = 28;
int walkingSpeed = 4;
int flipTop = (numSteps >> walkingSpeed) & 1;
int flipBottom = (numSteps >> walkingSpeed) & 1;
if (movingDir == 1) {
xTile += 2;
} else if (movingDir > 1) {
xTile += 4 + ((numSteps >> walkingSpeed) & 1) * 2;
flipTop = (movingDir - 1) % 2;
}
int modifier = 8 * scale;
int xOffset = x - modifier / 2;
int yOffset = y - modifier / 2 - 4;
// upper body
screen.render(xOffset + (modifier * flipTop), yOffset, xTile + yTile * 32, colour, flipTop, scale);
screen.render(xOffset + modifier - (modifier * flipTop), yOffset, (xTile + 1) + yTile * 32, colour, flipTop, scale);
// lower body
screen.render(xOffset + (modifier * flipBottom), yOffset + modifier, xTile + (yTile + 1) * 32, colour, flipBottom, scale);
screen.render(xOffset + modifier - (modifier * flipBottom), yOffset + modifier, (xTile + 1) + (yTile + 1) * 32, colour, flipBottom,
scale);
}
public boolean hasCollided(int xa, int ya) {
int xMin = 0;
int xMax = 7;
int yMin = 3;
int yMax = 7;
for (int x = xMin; x < xMax; x++) {
if (isSolidTile(xa, ya, x, yMin)) {
return true;
}
}
for (int x = xMin; x < xMax; x++) {
if (isSolidTile(xa, ya, x, yMax)) {
return true;
}
}
for (int y = yMin; y < yMax; y++) {
if (isSolidTile(xa, ya, xMin, y)) {
return true;
}
}
for (int y = yMin; y < yMax; y++) {
if (isSolidTile(xa, ya, xMax, y)) {
return true;
}
}
return false;
}
}
Okay. It took me FOREVER, but it was a simple fix. All I had to do was change the "(x, y)" to "(xa, ya)"
if (!hasCollided(xa, ya)) {
if (ya < 0)
movingDir = 0;
if (ya > 0)
movingDir = 1;
if (xa < 0)
movingDir = 2;
if (xa > 0)
movingDir = 3;
x += xa * speed;
y += ya * speed;
}
}
How do you create a video wall like in the JavaFX 2.0 demo here:
https://www.youtube.com/watch?v=UXSmJYFrulY#t=411
For a start it doesn't have to be videos, it can be images as well. All I'd like to have is to place the nodes like they are in the video, i. e. in a curved shape like the insides of a cylinder or a sphere.
Or is the source of that demo available somewhere?
Thank you very much.
I researched and found a very awesome site with the relevant information:
http://paulbourke.net/geometry/transformationprojection/
The relevant part was the Coordinate System Transformation, in particular the equations for converting between cartesian and spherical coordinates.
double x = r * Math.sin(angle1) * Math.cos(angle2);
double y = r * Math.sin(angle1) * Math.sin(angle2);
double z = r * Math.cos(angle1);
In my example below y isn't used from the formula, since the image rows are stacked.
Note: By using these formulas in 2 nested for-loops from -Math.PI to Math.PI you can lay out the nodes around a sphere. The difficult part regarding the full sphere was to rotate the nodes towards the center, that one I couldn't figure out.
Since I wasn't familiar with Java3D I also checked out the Building a 3D Sample Application.
In the end I got a video wall, the code is reduced to this:
public class VideoWall extends Application {
Random rand = new Random();
Group root = new Group();
PerspectiveCamera camera;
private static final double CAMERA_INITIAL_DISTANCE = -850;
private static final double CAMERA_NEAR_CLIP = 0.1;
private static final double CAMERA_FAR_CLIP = 10000.0;
Image[] images = new Image[] {
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg"),
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/White_Lion.jpg/320px-White_Lion.jpg"),
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Lion_female.jpg/319px-Lion_female.jpg")
};
public VideoWall(){
}
public static void main(String[] args) {
launch(args);
}
/**
* Create ImageView with random Image.
* #return
*/
private ImageView createImageView() {
Image image = images[ rand.nextInt(images.length)];
ImageView c = new ImageView( image);
c.setFitWidth(140);
c.setFitWidth(100);
c.setPreserveRatio(true);
return c;
}
#Override
public void start(Stage primaryStage) {
// build camera
camera = new PerspectiveCamera(true);
camera.setNearClip(CAMERA_NEAR_CLIP);
camera.setFarClip(CAMERA_FAR_CLIP);
camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
// we display any node (imageview, webview, etc)
Node node;
// create a single webview; we only add it once because we don't want to flood youtube
WebView webView = new WebView();
webView.getEngine().load(
"http://www.youtube.com/embed/utUPth77L_o?autoplay=1"
);
webView.setPrefSize(100, 70);
// wall. the degrees depend on the distance, image size, translate start points, etc. so these values were just as they fit
double ringBeginDeg = -30;
double ringEndDeg = 38;
double r = 1300;
double yOffset = 80; // offset per image row
double yOffsetInitial = 120; // initial y offset from "floor"
int count=0;
for( double angle1=Math.toRadians(ringBeginDeg); angle1 <Math.toRadians(ringEndDeg); angle1+=0.08)
{
double angle2 = Math.PI;
for( int i=-3; i <= 3; i++)
{
double x = r * Math.sin(angle1) * Math.cos(angle2);
// double y = r * Math.sin(angle1) * Math.sin(angle2);
double z = r * Math.cos(angle1);
// add 1 webview, the rest imageviews
if( count == 16) {
node = webView;
} else {
node = createImageView();
}
node.setTranslateX(x);
node.setTranslateY(yOffset * i - yOffsetInitial);
node.setTranslateZ(z);
// rotate towards viewer position
Rotate rx = new Rotate();
rx.setAxis(Rotate.Y_AXIS);
rx.setAngle(Math.toDegrees( -angle1));
node.getTransforms().addAll(rx);
root.getChildren().add( node);
count++;
}
}
Scene scene = new Scene(root, 1600, 900, Color.BLACK);
primaryStage.setScene( scene);
primaryStage.show();
scene.setCamera(camera);
}
}
You can add whatever node you prefer. I added a youtube webview for testing. It plays, but the video doesn't get loaded, so all you see is static noise (the grey tile in the screenshot). So in theory you could make the nodes all webview with youtube videos, but that would mean flooding youtube. Better use some offline videos.
Here's a screenshot:
I also toyed around with the full 3d example and creating a ring. That's how it looked like (with always the same image) from an outer view:
Having the camera in the center you can nicely scroll the ring.
If someone wants to toy around, here's a quick & dirty gist with the navigable ring. Use left/right/middle mouse buttons for navigation.
And if you'd like to toy around with a full sphere, you may use this:
// full sphere
for (double angle1 = -Math.PI; angle1 <= Math.PI; angle1 += 0.15) {
for (double angle2 = -Math.PI; angle2 <= Math.PI; angle2 += 0.15) {
double x = r * Math.sin(angle1) * Math.cos(angle2);
double y = r * Math.sin(angle1) * Math.sin(angle2);
double z = r * Math.cos(angle1);
c = createImageView();
c.setTranslateX(x);
c.setTranslateY(y);
c.setTranslateZ(z);
Rotate rx = new Rotate();
rx.setAxis(Rotate.Y_AXIS);
rx.setAngle(Math.toDegrees(-angle1));
c.getTransforms().addAll(rx);
world.getChildren().add(c);
}
}
Which looks like this:
But as mentioned, I haven't figured out yet how to rotate all tiles so that they look into the center. And they'd need to be equally distributed. But that's just for fun and off-topic.
Since it's part of the video in my question, it was only a matter of keeping a list of parallel transitions to create the "build-up" animation of the tiles. The bottom row has a reflection now.
The extended code:
public class VideoWall extends Application {
Random rand = new Random();
Group root = new Group();
PerspectiveCamera camera;
private static final double CAMERA_INITIAL_DISTANCE = -850;
private static final double CAMERA_NEAR_CLIP = 0.1;
private static final double CAMERA_FAR_CLIP = 10000.0;
Image[] images = new Image[] {
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg"),
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/White_Lion.jpg/320px-White_Lion.jpg"),
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Lion_female.jpg/319px-Lion_female.jpg")
};
List<ParallelTransition> transitionList = new ArrayList<>();
public VideoWall(){
}
public static void main(String[] args) {
launch(args);
}
/**
* Create ImageView with random Image.
* #return
*/
private ImageView createImageView() {
Image image = images[ rand.nextInt(images.length)];
ImageView c = new ImageView( image);
c.setFitWidth(140);
c.setFitWidth(100);
c.setPreserveRatio(true);
return c;
}
#Override
public void start(Stage primaryStage) {
// build camera
camera = new PerspectiveCamera(true);
camera.setNearClip(CAMERA_NEAR_CLIP);
camera.setFarClip(CAMERA_FAR_CLIP);
camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
// we display any node (imageview, webview, etc)
Node node;
// wall. the degrees depend on the distance, image size, translate start points, etc. so these values were just as they fit
double ringBeginDeg = -30;
double ringEndDeg = 38;
double r = 1300;
double yOffset = 80; // offset per image row
double yOffsetInitial = 120; // initial y offset from "floor"
int min = -3;
int max = 3;
for( double angle1=Math.toRadians(ringBeginDeg); angle1 <Math.toRadians(ringEndDeg); angle1+=0.08)
{
double angle2 = Math.PI;
for( int i=min; i <= max; i++)
{
double x = r * Math.sin(angle1) * Math.cos(angle2);
// double y = r * Math.sin(angle1) * Math.sin(angle2);
double z = r * Math.cos(angle1);
node = createImageView();
node.setTranslateX(x);
node.setTranslateY(yOffset * i - yOffsetInitial);
node.setTranslateZ(z);
// rotate towards viewer position
Rotate rx = new Rotate();
rx.setAxis(Rotate.Y_AXIS);
rx.setAngle(Math.toDegrees( -angle1));
node.getTransforms().addAll(rx);
// reflection on bottom row
if( i==max) {
Reflection refl = new Reflection();
refl.setFraction(0.8f);
node.setEffect(refl);
}
// build the wall using a transition
node.setVisible(false);
transitionList.add( createTransition( node));
root.getChildren().add( node);
}
}
Scene scene = new Scene(root, 1600, 900, Color.BLACK);
primaryStage.setScene( scene);
primaryStage.show();
scene.setCamera(camera);
AnimationTimer timer = createAnimation();
timer.start();
}
private AnimationTimer createAnimation() {
Collections.sort(transitionList, new Comparator<ParallelTransition>() {
#Override
public int compare(ParallelTransition arg0, ParallelTransition arg1) {
// bottom right to top left
Point2D ref = new Point2D(1000,1000);
Point2D pt0 = new Point2D( arg0.getNode().getTranslateX(), arg0.getNode().getTranslateY());
Point2D pt1 = new Point2D( arg1.getNode().getTranslateX(), arg1.getNode().getTranslateY());
return Double.compare(ref.distance(pt0), ref.distance(pt1));
// bottom row first
// return -Double.compare( arg0.getNode().getTranslateY(), arg1.getNode().getTranslateY());
}
});
AnimationTimer timer = new AnimationTimer() {
long last = 0;
#Override
public void handle(long now) {
//if( (now - last) > 1_000_000_000)
if( (now - last) > 40_000_000)
{
if( transitionList.size() > 0) {
ParallelTransition t = transitionList.remove(0);
t.getNode().setVisible(true);
t.play();
}
last = now;
}
if( transitionList.size() == 0) {
stop();
}
}
};
return timer;
}
private ParallelTransition createTransition( final Node node) {
Path path = new Path();
path.getElements().add(new MoveToAbs( node, node.getTranslateX() - 1000, node.getTranslateY() - 900));
path.getElements().add(new LineToAbs( node, node.getTranslateX(), node.getTranslateY()));
Duration duration = Duration.millis(1500);
PathTransition pt = new PathTransition( duration, path, node);
RotateTransition rt = new RotateTransition( duration, node);
rt.setByAngle(720);
rt.setAutoReverse(true);
ParallelTransition parallelTransition = new ParallelTransition();
parallelTransition.setNode(node);
parallelTransition.getChildren().addAll(pt, rt);
return parallelTransition;
}
public static class MoveToAbs extends MoveTo {
public MoveToAbs( Node node, double x, double y) {
super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
public static class LineToAbs extends LineTo {
public LineToAbs( Node node, double x, double y) {
super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
}
Well to me it seems to be a matter of creating a Grid, or "Mesh" out of ImageViews.
Then you would Map all the Viewport's to the Image('s) you want to display.
Here for example is a Skybox for 3D implementation using this approach.
Note that this is just a simple Cube.
The image is setup similar to this, though I made it into 6 separate Images.
If you wanted to use video, I recommend you use VLCJ,
They have samples for JavaFX setup Here
For this you would apply the same principals to the WritableImage(s)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
I added a couple things for you to play with ...
/**
* A self initializing First Person Shooter camera
*
* #author Jason Pollastrini aka jdub1581
*/
public class SimpleFPSCamera extends Parent {
public SimpleFPSCamera() {
initialize();
}
private void update() {
updateControls();
}
private void updateControls() {
if (fwd && !back) {
moveForward();
}
if (strafeL) {
strafeLeft();
}
if (strafeR) {
strafeRight();
}
if (back && !fwd) {
moveBack();
}
if (up && !down) {
moveUp();
}
if (down && !up) {
moveDown();
}
}
/*==========================================================================
Initialization
*/
private final Group root = new Group();
private final Affine affine = new Affine();
private final Translate t = new Translate(0, 0, 0);
private final Rotate rotateX = new Rotate(0, Rotate.X_AXIS),
rotateY = new Rotate(0, Rotate.Y_AXIS),
rotateZ = new Rotate(0, Rotate.Z_AXIS);
private boolean fwd, strafeL, strafeR, back, up, down, shift;
private double mouseSpeed = 1.0, mouseModifier = 0.1;
private double moveSpeed = 10.0;
private double mousePosX;
private double mousePosY;
private double mouseOldX;
private double mouseOldY;
private double mouseDeltaX;
private double mouseDeltaY;
private void initialize() {
getChildren().add(root);
getTransforms().addAll(affine);
initializeCamera();
startUpdateThread();
}
public void loadControlsForSubScene(SubScene scene) {
sceneProperty().addListener(l -> {
if (getScene() != null) {
getScene().addEventHandler(KeyEvent.ANY, ke -> {
if (ke.getEventType() == KeyEvent.KEY_PRESSED) {
switch (ke.getCode()) {
case Q:
up = true;
break;
case E:
down = true;
break;
case W:
fwd = true;
break;
case S:
back = true;
break;
case A:
strafeL = true;
break;
case D:
strafeR = true;
break;
case SHIFT:
shift = true;
moveSpeed = 20;
break;
}
} else if (ke.getEventType() == KeyEvent.KEY_RELEASED) {
switch (ke.getCode()) {
case Q:
up = false;
break;
case E:
down = false;
break;
case W:
fwd = false;
break;
case S:
back = false;
break;
case A:
strafeL = false;
break;
case D:
strafeR = false;
break;
case SHIFT:
moveSpeed = 10;
shift = false;
break;
}
}
ke.consume();
});
}
});
scene.addEventHandler(MouseEvent.ANY, me -> {
if (me.getEventType().equals(MouseEvent.MOUSE_PRESSED)) {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
} else if (me.getEventType().equals(MouseEvent.MOUSE_DRAGGED)) {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
mouseSpeed = 1.0;
mouseModifier = 0.1;
if (me.isPrimaryButtonDown()) {
if (me.isControlDown()) {
mouseSpeed = 0.1;
}
if (me.isShiftDown()) {
mouseSpeed = 1.0;
}
t.setX(getPosition().getX());
t.setY(getPosition().getY());
t.setZ(getPosition().getZ());
affine.setToIdentity();
rotateY.setAngle(
Utils.clamp(-360, ((rotateY.getAngle() + mouseDeltaX * (mouseSpeed * mouseModifier)) % 360 + 540) % 360 - 180, 360)
); // horizontal
rotateX.setAngle(
Utils.clamp(-45, ((rotateX.getAngle() - mouseDeltaY * (mouseSpeed * mouseModifier)) % 360 + 540) % 360 - 180, 35)
); // vertical
affine.prepend(t.createConcatenation(rotateY.createConcatenation(rotateX)));
} else if (me.isSecondaryButtonDown()) {
/*
init zoom?
*/
} else if (me.isMiddleButtonDown()) {
/*
init panning?
*/
}
}
});
scene.addEventHandler(ScrollEvent.ANY, se -> {
if (se.getEventType().equals(ScrollEvent.SCROLL_STARTED)) {
} else if (se.getEventType().equals(ScrollEvent.SCROLL)) {
} else if (se.getEventType().equals(ScrollEvent.SCROLL_FINISHED)) {
}
});
}
public void loadControlsForScene(Scene scene) {
scene.addEventHandler(KeyEvent.ANY, ke -> {
if (ke.getEventType() == KeyEvent.KEY_PRESSED) {
switch (ke.getCode()) {
case Q:
up = true;
break;
case E:
down = true;
break;
case W:
fwd = true;
break;
case S:
back = true;
break;
case A:
strafeL = true;
break;
case D:
strafeR = true;
break;
case SHIFT:
shift = true;
moveSpeed = 20;
break;
}
} else if (ke.getEventType() == KeyEvent.KEY_RELEASED) {
switch (ke.getCode()) {
case Q:
up = false;
break;
case E:
down = false;
break;
case W:
fwd = false;
break;
case S:
back = false;
break;
case A:
strafeL = false;
break;
case D:
strafeR = false;
break;
case SHIFT:
moveSpeed = 10;
shift = false;
break;
}
}
ke.consume();
});
scene.addEventHandler(MouseEvent.ANY, me -> {
if (me.getEventType().equals(MouseEvent.MOUSE_PRESSED)) {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
} else if (me.getEventType().equals(MouseEvent.MOUSE_DRAGGED)) {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
mouseSpeed = 1.0;
mouseModifier = 0.1;
if (me.isPrimaryButtonDown()) {
if (me.isControlDown()) {
mouseSpeed = 0.1;
}
if (me.isShiftDown()) {
mouseSpeed = 1.0;
}
t.setX(getPosition().getX());
t.setY(getPosition().getY());
t.setZ(getPosition().getZ());
affine.setToIdentity();
rotateY.setAngle(
Utils.clamp(-360, ((rotateY.getAngle() + mouseDeltaX * (mouseSpeed * mouseModifier)) % 360 + 540) % 360 - 180, 360)
); // horizontal
rotateX.setAngle(
Utils.clamp(-45, ((rotateX.getAngle() - mouseDeltaY * (mouseSpeed * mouseModifier)) % 360 + 540) % 360 - 180, 35)
); // vertical
affine.prepend(t.createConcatenation(rotateY.createConcatenation(rotateX)));
} else if (me.isSecondaryButtonDown()) {
/*
init zoom?
*/
} else if (me.isMiddleButtonDown()) {
/*
init panning?
*/
}
}
});
scene.addEventHandler(ScrollEvent.ANY, se -> {
if (se.getEventType().equals(ScrollEvent.SCROLL_STARTED)) {
} else if (se.getEventType().equals(ScrollEvent.SCROLL)) {
} else if (se.getEventType().equals(ScrollEvent.SCROLL_FINISHED)) {
}
});
}
private void initializeCamera() {
getCamera().setNearClip(0.1);
getCamera().setFarClip(100000);
getCamera().setFieldOfView(42);
getCamera().setVerticalFieldOfView(true);
//getCamera().getTransforms().add(new Rotate(180, Rotate.Z_AXIS));
root.getChildren().add(getCamera());
}
private void startUpdateThread() {
new AnimationTimer() {
#Override
public void handle(long now) {
update();
}
}.start();
}
/*==========================================================================
Movement
*/
private void moveForward() {
affine.setTx(getPosition().getX() + moveSpeed * getN().getX());
affine.setTy(getPosition().getY() + moveSpeed * getN().getY());
affine.setTz(getPosition().getZ() + moveSpeed * getN().getZ());
}
private void strafeLeft() {
affine.setTx(getPosition().getX() + moveSpeed * -getU().getX());
affine.setTy(getPosition().getY() + moveSpeed * -getU().getY());
affine.setTz(getPosition().getZ() + moveSpeed * -getU().getZ());
}
private void strafeRight() {
affine.setTx(getPosition().getX() + moveSpeed * getU().getX());
affine.setTy(getPosition().getY() + moveSpeed * getU().getY());
affine.setTz(getPosition().getZ() + moveSpeed * getU().getZ());
}
private void moveBack() {
affine.setTx(getPosition().getX() + moveSpeed * -getN().getX());
affine.setTy(getPosition().getY() + moveSpeed * -getN().getY());
affine.setTz(getPosition().getZ() + moveSpeed * -getN().getZ());
}
private void moveUp() {
affine.setTx(getPosition().getX() + moveSpeed * -getV().getX());
affine.setTy(getPosition().getY() + moveSpeed * -getV().getY());
affine.setTz(getPosition().getZ() + moveSpeed * -getV().getZ());
}
private void moveDown() {
affine.setTx(getPosition().getX() + moveSpeed * getV().getX());
affine.setTy(getPosition().getY() + moveSpeed * getV().getY());
affine.setTz(getPosition().getZ() + moveSpeed * getV().getZ());
}
/*==========================================================================
Properties
*/
private final ReadOnlyObjectWrapper<PerspectiveCamera> camera = new ReadOnlyObjectWrapper<>(this, "camera", new PerspectiveCamera(true));
public final PerspectiveCamera getCamera() {
return camera.get();
}
public ReadOnlyObjectProperty cameraProperty() {
return camera.getReadOnlyProperty();
}
/*==========================================================================
Callbacks
| R | Up| F | | P|
U |mxx|mxy|mxz| |tx|
V |myx|myy|myz| |ty|
N |mzx|mzy|mzz| |tz|
*/
//Forward / look direction
private final Callback<Transform, Point3D> F = (a) -> {
return new Point3D(a.getMzx(), a.getMzy(), a.getMzz());
};
private final Callback<Transform, Point3D> N = (a) -> {
return new Point3D(a.getMxz(), a.getMyz(), a.getMzz());
};
// up direction
private final Callback<Transform, Point3D> UP = (a) -> {
return new Point3D(a.getMyx(), a.getMyy(), a.getMyz());
};
private final Callback<Transform, Point3D> V = (a) -> {
return new Point3D(a.getMxy(), a.getMyy(), a.getMzy());
};
// right direction
private final Callback<Transform, Point3D> R = (a) -> {
return new Point3D(a.getMxx(), a.getMxy(), a.getMxz());
};
private final Callback<Transform, Point3D> U = (a) -> {
return new Point3D(a.getMxx(), a.getMyx(), a.getMzx());
};
//position
private final Callback<Transform, Point3D> P = (a) -> {
return new Point3D(a.getTx(), a.getTy(), a.getTz());
};
private Point3D getF() {
return F.call(getLocalToSceneTransform());
}
public Point3D getLookDirection() {
return getF();
}
private Point3D getN() {
return N.call(getLocalToSceneTransform());
}
public Point3D getLookNormal() {
return getN();
}
private Point3D getR() {
return R.call(getLocalToSceneTransform());
}
private Point3D getU() {
return U.call(getLocalToSceneTransform());
}
private Point3D getUp() {
return UP.call(getLocalToSceneTransform());
}
private Point3D getV() {
return V.call(getLocalToSceneTransform());
}
public final Point3D getPosition() {
return P.call(getLocalToSceneTransform());
}
}
Your Modified code:
public class VideoWall extends Application {
Random rand = new Random();
Group root = new Group();
PerspectiveCamera camera;
SimpleFPSCamera fpsCam;
private static final double CAMERA_INITIAL_DISTANCE = -10000;
private static final double CAMERA_NEAR_CLIP = 0.1;
private static final double CAMERA_FAR_CLIP = 100000.0;
Image[] images = new Image[]{
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg"),
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/White_Lion.jpg/320px-White_Lion.jpg"),
new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Lion_female.jpg/319px-Lion_female.jpg")
};
List<ParallelTransition> transitionList = new ArrayList<>();
List<ImageView> imageList = new ArrayList<>();
public VideoWall() {
}
public static void main(String[] args) {
launch(args);
}
/**
* Create ImageView with random Image.
*
* #return
*/
private ImageView createImageView() {
Image image = images[rand.nextInt(images.length)];
ImageView c = new ImageView(image);
c.setFitWidth(140);
c.setFitWidth(100);
c.setPreserveRatio(true);
return c;
}
private BillboardImage createBillboardImage() {
Image image = images[rand.nextInt(images.length)];
BillboardImage c = new BillboardImage(image);
c.setFitWidth(140);
c.setFitWidth(100);
c.setPreserveRatio(true);
return c;
}
#Override
public void start(Stage primaryStage) {
// build camera
//camera = new PerspectiveCamera(true);
//camera.setNearClip(CAMERA_NEAR_CLIP);
//camera.setFarClip(CAMERA_FAR_CLIP);
//camera.setTranslateZ(CAMERA_INITIAL_DISTANCE);
fpsCam = new SimpleFPSCamera();
// we display any node (imageview, webview, etc)
Node node;
// wall. the degrees depend on the distance, image size, translate start points, etc. so these values were just as they fit
double ringBeginDeg = -30;
double ringEndDeg = 38;
double r = 1300;
double yOffset = 80; // offset per image row
double yOffsetInitial = 120; // initial y offset from "floor"
int min = -3;
int max = 3;
/*
for (double angle1 = Math.toRadians(ringBeginDeg); angle1 < Math.toRadians(ringEndDeg); angle1 += 0.08) {
double angle2 = Math.PI;
for (int i = min; i <= max; i++) {
double x = r * Math.sin(angle1) * Math.cos(angle2);
// double y = r * Math.sin(angle1) * Math.sin(angle2);
double z = r * Math.cos(angle1);
node = createImageView();
node.setTranslateX(x);
node.setTranslateY(yOffset * i - yOffsetInitial);
node.setTranslateZ(z);
// rotate towards viewer position
Rotate rx = new Rotate();
rx.setAxis(Rotate.Y_AXIS);
rx.setAngle(Math.toDegrees(-angle1));
node.getTransforms().addAll(rx);
// reflection on bottom row
if (i == max) {
Reflection refl = new Reflection();
refl.setFraction(0.8f);
node.setEffect(refl);
}
// build the wall using a transition
node.setVisible(false);
transitionList.add(createTransition(node));
root.getChildren().add(node);
}
}*/
// full sphere
for (double angle1 = -Math.PI; angle1 <= Math.PI; angle1 += 0.48) {
for (double angle2 = -Math.PI; angle2 <= Math.PI; angle2 += 0.48) {
double x = r * Math.sin(angle1) * Math.cos(angle2);
double y = r * Math.sin(angle1) * Math.sin(angle2);
double z = r * Math.cos(angle1);
BillboardImage c = createBillboardImage();
c.setTranslateX(x);
c.setTranslateY(y);
c.setTranslateZ(z);
imageList.add(c);
}
}
root.getChildren().add(fpsCam);
root.getChildren().addAll(imageList);
Scene scene = new Scene(root, 1600, 900, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.BLACK);
scene.setCamera(fpsCam.getCamera());
fpsCam.loadControlsForScene(scene);
primaryStage.setScene(scene);
primaryStage.show();
AnimationTimer timer = createBillboardTimer();
timer.start();
}
private AnimationTimer createBillboardTimer() {
return new AnimationTimer() {
#Override
public void handle(long now) {
if(!imageList.isEmpty()){
imageList.stream().forEach(bbi ->{
((BillboardImage)bbi).updateMatrix(bbi, fpsCam);
});
}
}
};
}
private AnimationTimer createAnimation() {
Collections.sort(transitionList, new Comparator<ParallelTransition>() {
#Override
public int compare(ParallelTransition arg0, ParallelTransition arg1) {
// bottom right to top left
Point2D ref = new Point2D(1000, 1000);
Point2D pt0 = new Point2D(arg0.getNode().getTranslateX(), arg0.getNode().getTranslateY());
Point2D pt1 = new Point2D(arg1.getNode().getTranslateX(), arg1.getNode().getTranslateY());
return Double.compare(ref.distance(pt0), ref.distance(pt1));
// bottom row first
// return -Double.compare( arg0.getNode().getTranslateY(), arg1.getNode().getTranslateY());
}
});
AnimationTimer timer = new AnimationTimer() {
long last = 0;
#Override
public void handle(long now) {
//if( (now - last) > 1_000_000_000)
if ((now - last) > 40_000_000) {
if (transitionList.size() > 0) {
ParallelTransition t = transitionList.remove(0);
t.getNode().setVisible(true);
t.play();
}
last = now;
}
if (transitionList.size() == 0) {
stop();
}
}
};
return timer;
}
private ParallelTransition createTransition(final Node node) {
Path path = new Path();
path.getElements().add(new MoveToAbs(node, node.getTranslateX() - 1000, node.getTranslateY() - 900));
path.getElements().add(new LineToAbs(node, node.getTranslateX(), node.getTranslateY()));
Duration duration = Duration.millis(1500);
PathTransition pt = new PathTransition(duration, path, node);
RotateTransition rt = new RotateTransition(duration, node);
rt.setByAngle(720);
rt.setAutoReverse(true);
ParallelTransition parallelTransition = new ParallelTransition();
parallelTransition.setNode(node);
parallelTransition.getChildren().addAll(pt, rt);
return parallelTransition;
}
public static class MoveToAbs extends MoveTo {
public MoveToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
public static class LineToAbs extends LineTo {
public LineToAbs(Node node, double x, double y) {
super(x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
/*
*/
public enum BillboardMode {
SPHERICAL,
CYLINDRICAL;
}
private class BillboardImage extends ImageView{
// Add transform to Node that needs to look at target..
public Affine affine = new Affine();
public BillboardImage() {
this.getTransforms().add(affine);
}
public BillboardImage(String url) {
super(url);
this.getTransforms().add(affine);
}
public BillboardImage(Image image) {
super(image);
this.getTransforms().add(affine);
}
// set up to look at camera, can change to any other Node
protected void updateMatrix(Node billBoardNode, Node other) {
Transform self = billBoardNode.getLocalToSceneTransform(),
oth = other.getLocalToSceneTransform();
Bounds b;
double cX, cY, cZ;
if (!(billBoardNode instanceof Shape3D)) {
b = billBoardNode.getBoundsInLocal();
cX = b.getWidth() / 2;
cY = b.getHeight() / 2;
cZ = b.getDepth() / 2;
} else {
cX = self.getTx();
cY = self.getTy();
cZ = self.getTz();
}
Point3D otherPos = Point3D.ZERO.add(oth.getTx(), oth.getTy(), oth.getTz());
Point3D selfPos = new Point3D(cX, cY, cZ);
Point3D up = Point3D.ZERO.add(0, -1, 0),
forward = new Point3D(
(selfPos.getX()) - otherPos.getX(),
(selfPos.getY()) - otherPos.getY(),
(selfPos.getZ()) - otherPos.getZ()
).normalize(),
right = up.crossProduct(forward).normalize();
up = forward.crossProduct(right).normalize();
switch (getBillboardMode()) {
case SPHERICAL:
affine.setMxx(right.getX()); affine.setMxy(up.getX());affine.setMzx(forward.getX());
affine.setMyx(right.getY());affine.setMyy(up.getY()); affine.setMzy(forward.getY());
affine.setMzx(right.getZ());affine.setMzy(up.getZ());affine.setMzz(forward.getZ());
affine.setTx(cX * (1 - affine.getMxx()) - cY * affine.getMxy() - cZ * affine.getMxz());
affine.setTy(cY * (1 - affine.getMyy()) - cX * affine.getMyx() - cZ * affine.getMyz());
affine.setTz(cZ * (1 - affine.getMzz()) - cX * affine.getMzx() - cY * affine.getMzy());
break;
case CYLINDRICAL:
affine.setMxx(right.getX());affine.setMxy(0);affine.setMzx(forward.getX());
affine.setMyx(0);affine.setMyy(1);affine.setMzy(0);
affine.setMzx(right.getZ()); affine.setMzy(0);affine.setMzz(forward.getZ());
affine.setTx(cX * (1 - affine.getMxx()) - cY * affine.getMxy() - cZ * affine.getMxz());
affine.setTy(cY * (1 - affine.getMyy()) - cX * affine.getMyx() - cZ * affine.getMyz());
affine.setTz(cZ * (1 - affine.getMzz()) - cX * affine.getMzx() - cY * affine.getMzy());
break;
}
}
public BillboardMode getBillboardMode() {
return BillboardMode.SPHERICAL;
}
}
}
At least now the Images are billboarded to the Camera..
Feel free to play with my FPSCamera as well ..
Controls are like any standard 1st person shooter
w = forward, s = back,
a = left-strafe, d = right-strafe,
q & e are up and down.
I've built a list and inserted labels in each cell. For now the text that is too long simply disappear. I'd like to wrap the text so it is entirely visible inside each cell.
Can you please help?
update: issue solved
For those who need an answer, I used LWUIT's HTMLComponent inside a container. The HTMLComponent allows you to use HTML code. That would allow you to format your list the way you want it to be.
Here is more details on the solution.
In Java ME with LWUIT, I used a HTMLComponent to get the precise layout I wanted. The best way for me was to use an HTML Table inside the HTMLComponent. It just behaves like HTML.
String html_code = "";
html_code = "<table width='100%'>";
html_code += "<tr><td><strong>"+fullname+"</strong></td></tr>";
if (title.length()>0) { html_code += "<tr><td><i>"+title+"</i></td></tr>"; }
if (message.length()>0) { html_code += "<tr><td>"+message+"</td></tr>"; }
if (date.length()>0) { html_code += "<tr><td><i>"+date+"</i></td></tr>"; }
html_code += "</table>";
HTMLComponent html = new HTMLComponent(null);
html.setBodyText(html_code);
Just incase if you are looking for a more "elegant" solution, i found a handy resource online. I am posting here for reference purposes, but HtmlComponent does the job.
import com.sun.lwuit.Font;
/** A class supporting word wrap for MIDP. */
public class WordWrap {
Font font;
int width;
String txt;
int pos;
/**
* Initializes the WordWrap object with the given Font, the text string
* to be wrapped, and the target width.
*
* #param font: The Font to be used to calculate the character widths.
* #param txt: The text string to be wrapped.
* #param width: The line width.
*/
public WordWrap (Font font, String txt, int width) {
this.font = font;
this.txt = txt;
this.width = width;
}
/**
* returns the next line break position. If no text is left, -1 is returned.
*/
public int next () {
int i = pos;
int len = txt.length ();
if (pos >= len) return -1;
int start = pos;
while (true) {
while (i < len && txt.charAt (i) > ' ')
i++;
int w = font.stringWidth (txt.substring (start, i));
if (pos == start) {
if (w > width) {
while (font.stringWidth (txt.substring (start, --i)) > width)
{ }
pos = i;
break;
}
}
if (w <= width) pos = i;
if (w > width || i >= len || txt.charAt(i) == '\n') break;
i++;
}
return pos >= len ? pos : ++pos;
}
}
import com.sun.lwuit.Button;
import com.sun.lwuit.Component;
import com.sun.lwuit.Container;
import com.sun.lwuit.Display;
import com.sun.lwuit.Label;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.events.FocusListener;
import com.sun.lwuit.geom.Dimension;
import com.sun.lwuit.layouts.BoxLayout;
import com.sun.lwuit.plaf.Border;
import com.sun.lwuit.plaf.Style;
/**
*
* #author rubycube
*/
public class WrapList extends Container {
private Button hiddenButton;
private int id;
public WrapList(String text, int containerID) {
id = containerID;
this.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
this.setFocusable(false);
final Style thisContainerStyle = this.getStyle();
Border thisContainerBorder = Border.createRoundBorder(20, 20, 0xcccccc);
thisContainerStyle.setBorder(thisContainerBorder);
hiddenButton = new Button(" ");
hiddenButton.setPreferredSize(new Dimension(1, 1));
Style style = hiddenButton.getStyle();
style.setBgTransparency(0, false);
style.setBorder(Border.createEmpty());
FocusListener hiddenButtonFL = new FocusListener() {
public void focusGained(Component cmp) {
WrapList parentContainer = ((WrapList) (cmp.getParent()));
Border parentContainerBorder = Border.createRoundBorder(20, 20, 0xff6600);
Style parentContainerStyle = parentContainer.getStyle();
parentContainerStyle.setBorder(parentContainerBorder);
parentContainerStyle.setBgColor(0xff9900);
parentContainerStyle.setBgTransparency(50);
parentContainer.repaint();
}
public void focusLost(Component cmp) {
WrapList parentContainer = ((WrapList) (cmp.getParent()));
Border parentContainerBorder = Border.createRoundBorder(20, 20, 0xcccccc);
Style parentContainerStyle = parentContainer.getStyle();
parentContainerStyle.setBorder(parentContainerBorder);
parentContainerStyle.setBgTransparency(0);
parentContainer.repaint();
}
};
hiddenButton.addFocusListener(hiddenButtonFL);
Label l = new Label(text);
l.setSelectedStyle(thisContainerStyle);
//l.setUnselectedStyle(thisContainerStyle);
WordWrap ww = new WordWrap(l.getStyle().getFont(), text, (Display.getInstance().getDisplayWidth() - 10));
int si = 0;
int ei = 0;
while (true) {
int np = ww.next();
if (np == -1) {
break;
} else {
si = ei;
ei = np;
}
String lineText = text.substring(si, ei);
Label line = new Label(lineText);
line.setEndsWith3Points(false);
this.addComponent(line);
}
this.addComponent(hiddenButton);
}
public void addActionListener(ActionListener actionlistener) {
hiddenButton.addActionListener(actionlistener);
}
/**
* #return the id
*/
public int getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(int id) {
this.id = id;
}
}
Is there any way to create a Tab Menu in j2me?
I found a code but I am unable to understand it
In this code there is Tab Menu created which is in Canvas class and then Tab menu is created which is totally done in Canvas or painted. The only part I found difficult to grasp was the void go() method and then
When I try to draw anything above and below this code using paint method, it doesn't work - what's the problem?
Below is the code
// Tab Menu CANVAS class
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
public class TabMenuCanvas extends Canvas
{
TabMenu menu = null;
public TabMenuCanvas()
{
menu = new TabMenu(
new String[]{"Home", "News", "Community", "Your files", "Credits", "Events", "Blog", "Upload", "Forum Nokia"},
getWidth() - 20
);
}
protected void keyPressed(int key)
{
int gameAction = getGameAction(key);
if(gameAction == Canvas.RIGHT)
{
menu.goRight();
repaint();
}
else if(gameAction == Canvas.LEFT)
{
menu.goLeft();
repaint();
}
}
protected void paint(Graphics g)
{
g.translate(10, 30);
menu.paint(g);
g.translate(- 10, - 30);
}
}
// Tab Menu Class
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
public class TabMenu
{
int background = 0xffffff;
int bgColor = 0xcccccc;
int bgFocusedColor = 0x0000ff;
int foreColor = 0x000000;
int foreFocusedColor = 0xffffff;
int cornerRadius = 6;
int padding = 2;
int margin = 2;
Font font = Font.getDefaultFont();
int scrollStep = 20;
int selectedTab = 0; //selected tab index
int[] tabsWidth = null; //width of single tabs
int[] tabsLeft = null; //left X coordinate of single tabs
int tabHeight = 0; //height of tabs (equal for all tabs)
String[] tabs = null; //tab labels
int menuWidth = 0; //total menu width
int viewportWidth = 0; //visible viewport width
int viewportX = 0; //current viewport X coordinate
public TabMenu(String[] tabs, int width)
{
this.tabs = tabs;
this.viewportWidth = width;
initialize();
}
void initialize()
{
tabHeight = font.getHeight() + cornerRadius + 2 * padding; //[ same for all tabs]
menuWidth = 0;
tabsWidth = new int[tabs.length];
tabsLeft = new int[tabs.length];
for(int i = 0; i < tabsWidth.length; i++)
{
tabsWidth[i] = font.stringWidth(tabs[i]) + 2 * padding + 2 * cornerRadius;
tabsLeft[i] = menuWidth;
menuWidth += tabsWidth[i];
if(i > 0)
{
menuWidth += margin;
}
}
}
public void goRight()
{
go(+1);
}
public void goLeft()
{
go(-1);
}
private void go(int delta)
{
int newTab = Math.max(0, Math.min(tabs.length - 1, selectedTab + delta));
boolean scroll = true;
if(newTab != selectedTab && isTabVisible(newTab))
{
selectedTab = newTab;
if( (delta > 0 && tabsLeft[selectedTab] + tabsWidth[selectedTab] > viewportX + viewportWidth) ||
(delta < 0 && tabsLeft[selectedTab] < viewportX))
{
scroll = true;
}
else
{
scroll = false;
}
}
if(scroll)
{
viewportX = Math.max(0, Math.min(menuWidth - viewportWidth, viewportX + delta * scrollStep));
}
}
private boolean isTabVisible(int tabIndex)
{
return tabsLeft[tabIndex] < viewportX + viewportWidth &&
tabsLeft[tabIndex] + tabsWidth[tabIndex] >= viewportX;
}
public void paint(Graphics g)
{
int currentX = - viewportX;
g.setClip(0, 0, viewportWidth, tabHeight);
g.setColor(background);
g.fillRect(0, 0, viewportWidth, tabHeight);
for(int i = 0; i < tabs.length; i++)
{
g.setColor(i == selectedTab ? bgFocusedColor : bgColor);
g.fillRoundRect(currentX, 0, tabsWidth[i], tabHeight + cornerRadius, 2 * cornerRadius, 2 * cornerRadius);
g.setColor(i == selectedTab ? foreFocusedColor : foreColor);
g.drawString(tabs[i], currentX + cornerRadius + padding, cornerRadius + padding, Graphics.LEFT | Graphics.TOP);
currentX += tabsWidth[i] + margin;
}
}
}
When I try to draw anything above and below this code using paint method, it doesn't work
what of the paint methods you use to draw above and below? Pay attention that there are two methods named that way - first is in TabMenuCanvas, second is in TabMenu (second method is invoked from TabMenuCanvas#repaint).
whatever you would try to draw in TabMenuCanvas#paint will most likely be overwritten by setClip and fillRect when TabMenu#paint is invoked following repaint request
The only place where one can expect to be able to draw something visible seems to be in TabMenu#paint method, inside the clip area that is set there.
You can use GUI Libraries for J2ME,for example Lightweight User Interface Toolkit (LWUIT),Flemil have "tab menu".You can see list of GUI Libraries here.