I have a JPopUpMenu with several JCheckBoxMenuItem's on it.
Actually what I would like to do is basicly to select an item of the JPopUpMenu with a specific index.
For exemple, a method like myPopUpMenu.setSelected(2), which would select "Algérie" in my JPopUpMenu.
The problem is that I don't know any method which would allow me to check an item manually...
Here's the code of my JPopUpMenu :
MainVue.java:
public class MainVue extends JFrame implements ActionListener {
private static final JScrollPopupMenu menuProduit = new JScrollPopupMenu();
private static final JScrollPopupMenu menuPays = new JScrollPopupMenu();
private static List<String> listeFiltres = new ArrayList<String>();
private String listeDeFiltres;
private String[] tableauFiltrePermanent;
private String listeFiltrePermanent;
private String[] tableauPays = { "Autres", "Afrique du sud", "Algérie", "Allemagne", "Arabie Saoudite", "Argentine",
"Australie", "Bangladesh", "Belgique", "Brésil", "Bulgarie", "Canada", "Chine", "Corée du sud", "Egypte",
"Emirats-Arabes Unis", "Espagne", "Etats-Unis", "Ethiopie", "Europe", "France", "Hongrie", "Inde",
"Indonésie", "Irak", "Iran", "Israél", "Italie", "Japon", "Jordanie", "Kazakhstan", "Koweit", "Liban",
"Libye", "Malaisie", "Maroc", "Mexique", "Monde", "Oman", "Pakistan", "Pays-Bas", "Philippines", "Poligne",
"Portugal", "Qatar", "République tchéque", "Roumanie", "Russie", "Taïwan", "Tunisie", "Turquie",
"Ukraine" };
private String[] tableauProduit = { "Blé", "Colza", "Mais", "Orge", "Orge de Brasserie", "Palme", "Soja",
"Tournesol", "Tourteaux De Colza", "Tourteaux de Soja", "Huile de Soja", "Huile De Colza" };
private List<JCheckBoxMenuItem> listJCBProduit = new ArrayList<JCheckBoxMenuItem>();
private List<JCheckBoxMenuItem> listJCBPays = new ArrayList<JCheckBoxMenuItem>();
public static PropertiesConfiguration prop;
public MainVue(Modele modele, Controleur controleur) throws ClassNotFoundException, SQLException, IOException {
prop = new PropertiesConfiguration("config.properties");
for (int i = 0; i < tableauProduit.length; i++) {
listJCBProduit.add(new JCheckBoxMenuItem(tableauProduit[i]));
}
for (int j = 0; j < listJCBProduit.size(); j++) {
JCheckBoxMenuItem produitActuel = listJCBProduit.get(j);
menuProduit.add(produitActuel);
produitActuel.addActionListener(new OpenAction(menuProduit, boutonProduit));
}
for (int i = 0; i < tableauPays.length; i++) {
listJCBPays.add(new JCheckBoxMenuItem(tableauPays[i]));
}
for (int j = 0; j < listJCBPays.size(); j++) {
JCheckBoxMenuItem paysActuel = listJCBPays.get(j);
menuPays.add(paysActuel);
paysActuel.addActionListener(new OpenAction(menuPays, boutonPays));
}
listeDeFiltres = "";
for (int p = 0; p < listeFiltres.size(); p++) {
String filtreActuel = listeFiltres.get(p);
if (listeDeFiltres == "") {
listeDeFiltres += filtreActuel;
} else {
listeDeFiltres += "," + filtreActuel;
}
}
prop.setProperty("listeFiltres", listeDeFiltres);
}
}
Here's the JScrollPopUpMenu component :
JScrollPopUpMenu.java:
package fr.views;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
public class JScrollPopupMenu extends JPopupMenu {
protected int maximumVisibleRows = 10;
public JScrollPopupMenu() {
this(null);
}
public JScrollPopupMenu(String label) {
super(label);
setLayout(new ScrollPopupMenuLayout());
super.add(getScrollBar());
addMouseWheelListener(new MouseWheelListener() {
#Override public void mouseWheelMoved(MouseWheelEvent event) {
JScrollBar scrollBar = getScrollBar();
int amount = (event.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
? event.getUnitsToScroll() * scrollBar.getUnitIncrement()
: (event.getWheelRotation() < 0 ? -1 : 1) * scrollBar.getBlockIncrement();
scrollBar.setValue(scrollBar.getValue() + amount);
event.consume();
}
});
}
private JScrollBar popupScrollBar;
protected JScrollBar getScrollBar() {
if(popupScrollBar == null) {
popupScrollBar = new JScrollBar(JScrollBar.VERTICAL);
popupScrollBar.addAdjustmentListener(new AdjustmentListener() {
#Override public void adjustmentValueChanged(AdjustmentEvent e) {
doLayout();
repaint();
}
});
popupScrollBar.setVisible(false);
}
return popupScrollBar;
}
public int getMaximumVisibleRows() {
return maximumVisibleRows;
}
public void setMaximumVisibleRows(int maximumVisibleRows) {
this.maximumVisibleRows = maximumVisibleRows;
}
public void paintChildren(Graphics g){
Insets insets = getInsets();
g.clipRect(insets.left, insets.top, getWidth(), getHeight() - insets.top - insets.bottom);
super.paintChildren(g);
}
protected void addImpl(Component comp, Object constraints, int index) {
super.addImpl(comp, constraints, index);
if(maximumVisibleRows < getComponentCount()-1) {
getScrollBar().setVisible(true);
}
}
public void remove(int index) {
// can't remove the scrollbar
++index;
super.remove(index);
if(maximumVisibleRows >= getComponentCount()-1) {
getScrollBar().setVisible(false);
}
}
public void show(Component invoker, int x, int y){
JScrollBar scrollBar = getScrollBar();
if(scrollBar.isVisible()){
int extent = 0;
int max = 0;
int i = 0;
int unit = -1;
int width = 0;
for(Component comp : getComponents()) {
if(!(comp instanceof JScrollBar)) {
Dimension preferredSize = comp.getPreferredSize();
width = Math.max(width, preferredSize.width);
if(unit < 0){
unit = preferredSize.height;
}
if(i++ < maximumVisibleRows) {
extent += preferredSize.height;
}
max += preferredSize.height;
}
}
Insets insets = getInsets();
int widthMargin = insets.left + insets.right;
int heightMargin = insets.top + insets.bottom;
scrollBar.setUnitIncrement(unit);
scrollBar.setBlockIncrement(extent);
scrollBar.setValues(0, heightMargin + extent, 0, heightMargin + max);
width += scrollBar.getPreferredSize().width + widthMargin;
int height = heightMargin + extent;
setPopupSize(new Dimension(width, height));
}
super.show(invoker, x, y);
}
protected static class ScrollPopupMenuLayout implements LayoutManager{
#Override public void addLayoutComponent(String name, Component comp) {}
#Override public void removeLayoutComponent(Component comp) {}
#Override public Dimension preferredLayoutSize(Container parent) {
int visibleAmount = Integer.MAX_VALUE;
Dimension dim = new Dimension();
for(Component comp :parent.getComponents()){
if(comp.isVisible()) {
if(comp instanceof JScrollBar){
JScrollBar scrollBar = (JScrollBar) comp;
visibleAmount = scrollBar.getVisibleAmount();
}
else {
Dimension pref = comp.getPreferredSize();
dim.width = Math.max(dim.width, pref.width);
dim.height += pref.height;
}
}
}
Insets insets = parent.getInsets();
dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
return dim;
}
#Override public Dimension minimumLayoutSize(Container parent) {
int visibleAmount = Integer.MAX_VALUE;
Dimension dim = new Dimension();
for(Component comp : parent.getComponents()) {
if(comp.isVisible()){
if(comp instanceof JScrollBar) {
JScrollBar scrollBar = (JScrollBar) comp;
visibleAmount = scrollBar.getVisibleAmount();
}
else {
Dimension min = comp.getMinimumSize();
dim.width = Math.max(dim.width, min.width);
dim.height += min.height;
}
}
}
Insets insets = parent.getInsets();
dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
return dim;
}
#Override public void layoutContainer(Container parent) {
Insets insets = parent.getInsets();
int width = parent.getWidth() - insets.left - insets.right;
int height = parent.getHeight() - insets.top - insets.bottom;
int x = insets.left;
int y = insets.top;
int position = 0;
for(Component comp : parent.getComponents()) {
if((comp instanceof JScrollBar) && comp.isVisible()) {
JScrollBar scrollBar = (JScrollBar) comp;
Dimension dim = scrollBar.getPreferredSize();
scrollBar.setBounds(x + width-dim.width, y, dim.width, height);
width -= dim.width;
position = scrollBar.getValue();
}
}
y -= position;
for(Component comp : parent.getComponents()) {
if(!(comp instanceof JScrollBar) && comp.isVisible()) {
Dimension pref = comp.getPreferredSize();
comp.setBounds(x, y, width, pref.height);
y += pref.height;
}
}
}
}
}
Thanks in advance for any help !
The index which you get from getIndex() method use as follows. You are adding ScrollBar in your JScrollPopupMenu at 0 index. So to remove casting error update your code as follow.
int index = getIndex("name");
((JCheckBoxMenuItem)menuProduit.getComponentAtIndex(index+1)).setState(true);
Related
I am currently messing around with a procedural 2D game in unity. Below are the scripts i am using to generate the dungeon and wanted to know if there was anyway of specifying a standard starting room. I have prefab room built but would like to have a single prefab room players always start in.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class DungeonGeneration : MonoBehaviour {
[SerializeField]
private int numberOfRooms;
[SerializeField]
private int numberOfObstacles;
[SerializeField]
private Vector2Int[] possibleObstacleSizes;
[SerializeField]
private int numberOfEnemies;
[SerializeField]
private GameObject[] possibleEnemies;
[SerializeField]
private GameObject goalPrefab;
[SerializeField]
private TileBase obstacleTile;
private Room[,] rooms;
private Room currentRoom;
private static DungeonGeneration instance = null;
void Awake () {
if (instance == null) {
DontDestroyOnLoad (this.gameObject);
instance = this;
this.currentRoom = GenerateDungeon ();
} else {
string roomPrefabName = instance.currentRoom.PrefabName ();
GameObject roomObject = (GameObject) Instantiate (Resources.Load (roomPrefabName));
Tilemap tilemap = roomObject.GetComponentInChildren<Tilemap> ();
instance.currentRoom.AddPopulationToTilemap (tilemap, instance.obstacleTile);
Destroy (this.gameObject);
}
}
void Start () {
string roomPrefabName = this.currentRoom.PrefabName ();
GameObject roomObject = (GameObject) Instantiate (Resources.Load (roomPrefabName));
Tilemap tilemap = roomObject.GetComponentInChildren<Tilemap> ();
this.currentRoom.AddPopulationToTilemap (tilemap, this.obstacleTile);
}
private Room GenerateDungeon() {
int gridSize = 3 * numberOfRooms;
rooms = new Room[gridSize, gridSize];
Vector2Int initialRoomCoordinate = new Vector2Int ((gridSize / 2) - 1, (gridSize / 2) - 1);
Queue<Room> roomsToCreate = new Queue<Room> ();
roomsToCreate.Enqueue (new Room(initialRoomCoordinate.x, initialRoomCoordinate.y));
List<Room> createdRooms = new List<Room> ();
while (roomsToCreate.Count > 0 && createdRooms.Count < numberOfRooms) {
Room currentRoom = roomsToCreate.Dequeue ();
this.rooms [currentRoom.roomCoordinate.x, currentRoom.roomCoordinate.y] = currentRoom;
createdRooms.Add (currentRoom);
AddNeighbors (currentRoom, roomsToCreate);
}
int maximumDistanceToInitialRoom = 0;
Room finalRoom = null;
foreach (Room room in createdRooms) {
List<Vector2Int> neighborCoordinates = room.NeighborCoordinates ();
foreach (Vector2Int coordinate in neighborCoordinates) {
Room neighbor = this.rooms [coordinate.x, coordinate.y];
if (neighbor != null) {
room.Connect (neighbor);
}
}
room.PopulateObstacles (this.numberOfObstacles, this.possibleObstacleSizes);
room.PopulatePrefabs (this.numberOfEnemies, this.possibleEnemies);
int distanceToInitialRoom = Mathf.Abs (room.roomCoordinate.x - initialRoomCoordinate.x) + Mathf.Abs(room.roomCoordinate.y - initialRoomCoordinate.y);
if (distanceToInitialRoom > maximumDistanceToInitialRoom) {
maximumDistanceToInitialRoom = distanceToInitialRoom;
finalRoom = room;
}
}
GameObject[] goalPrefabs = { this.goalPrefab };
finalRoom.PopulatePrefabs(1, goalPrefabs);
return this.rooms [initialRoomCoordinate.x, initialRoomCoordinate.y];
}
private void AddNeighbors(Room currentRoom, Queue<Room> roomsToCreate) {
List<Vector2Int> neighborCoordinates = currentRoom.NeighborCoordinates ();
List<Vector2Int> availableNeighbors = new List<Vector2Int> ();
foreach (Vector2Int coordinate in neighborCoordinates) {
if (this.rooms[coordinate.x, coordinate.y] == null) {
availableNeighbors.Add (coordinate);
}
}
int numberOfNeighbors = (int)Random.Range (1, availableNeighbors.Count);
for (int neighborIndex = 0; neighborIndex < numberOfNeighbors; neighborIndex++) {
float randomNumber = Random.value;
float roomFrac = 1f / (float)availableNeighbors.Count;
Vector2Int chosenNeighbor = new Vector2Int(0, 0);
foreach (Vector2Int coordinate in availableNeighbors) {
if (randomNumber < roomFrac) {
chosenNeighbor = coordinate;
break;
} else {
roomFrac += 1f / (float)availableNeighbors.Count;
}
}
roomsToCreate.Enqueue (new Room(chosenNeighbor));
availableNeighbors.Remove (chosenNeighbor);
}
}
private void PrintGrid() {
for (int rowIndex = 0; rowIndex < this.rooms.GetLength (1); rowIndex++) {
string row = "";
for (int columnIndex = 0; columnIndex < this.rooms.GetLength (0); columnIndex++) {
if (this.rooms [columnIndex, rowIndex] == null) {
row += "X";
} else {
row += "R";
}
}
Debug.Log (row);
}
}
public void MoveToRoom(Room room) {
this.currentRoom = room;
}
public Room CurrentRoom() {
return this.currentRoom;
}
public void ResetDungeon() {
this.currentRoom = GenerateDungeon ();
}
}
and
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class Room
{
public Vector2Int roomCoordinate;
public Dictionary<string, Room> neighbors;
private string[,] population;
private Dictionary<string, GameObject> name2Prefab;
public Room (int xCoordinate, int yCoordinate)
{
this.roomCoordinate = new Vector2Int (xCoordinate, yCoordinate);
this.neighbors = new Dictionary<string, Room> ();
this.population = new string[18, 10];
for (int xIndex = 0; xIndex < 18; xIndex += 1) {
for (int yIndex = 0; yIndex < 10; yIndex += 1) {
this.population [xIndex, yIndex] = "";
}
}
this.population [8, 5] = "Player";
this.name2Prefab = new Dictionary<string, GameObject> ();
}
public Room (Vector2Int roomCoordinate)
{
this.roomCoordinate = roomCoordinate;
this.neighbors = new Dictionary<string, Room> ();
this.population = new string[18, 10];
for (int xIndex = 0; xIndex < 18; xIndex += 1) {
for (int yIndex = 0; yIndex < 10; yIndex += 1) {
this.population [xIndex, yIndex] = "";
}
}
this.population [8, 5] = "Player";
this.name2Prefab = new Dictionary<string, GameObject> ();
}
public List<Vector2Int> NeighborCoordinates () {
List<Vector2Int> neighborCoordinates = new List<Vector2Int> ();
neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x, this.roomCoordinate.y - 1));
neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x + 1, this.roomCoordinate.y));
neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x, this.roomCoordinate.y + 1));
neighborCoordinates.Add (new Vector2Int(this.roomCoordinate.x - 1, this.roomCoordinate.y));
return neighborCoordinates;
}
public void Connect (Room neighbor) {
string direction = "";
if (neighbor.roomCoordinate.y < this.roomCoordinate.y) {
direction = "N";
}
if (neighbor.roomCoordinate.x > this.roomCoordinate.x) {
direction = "E";
}
if (neighbor.roomCoordinate.y > this.roomCoordinate.y) {
direction = "S";
}
if (neighbor.roomCoordinate.x < this.roomCoordinate.x) {
direction = "W";
}
this.neighbors.Add (direction, neighbor);
}
public string PrefabName () {
string name = "Room_";
foreach (KeyValuePair<string, Room> neighborPair in neighbors) {
name += neighborPair.Key;
}
return name;
}
public Room Neighbor (string direction) {
return this.neighbors [direction];
}
public void PopulateObstacles (int numberOfObstacles, Vector2Int[] possibleSizes) {
for (int obstacleIndex = 0; obstacleIndex < numberOfObstacles; obstacleIndex += 1) {
int sizeIndex = Random.Range (0, possibleSizes.Length);
Vector2Int regionSize = possibleSizes [sizeIndex];
List<Vector2Int> region = FindFreeRegion (regionSize);
foreach (Vector2Int coordinate in region) {
this.population [coordinate.x, coordinate.y] = "Obstacle";
}
}
}
public void PopulatePrefabs (int numberOfPrefabs, GameObject[] possiblePrefabs) {
for (int prefabIndex = 0; prefabIndex < numberOfPrefabs; prefabIndex += 1) {
int choiceIndex = Random.Range (0, possiblePrefabs.Length);
GameObject prefab = possiblePrefabs [choiceIndex];
List<Vector2Int> region = FindFreeRegion (new Vector2Int(1, 1));
this.population [region[0].x, region[0].y] = prefab.name;
this.name2Prefab [prefab.name] = prefab;
}
}
private List<Vector2Int> FindFreeRegion (Vector2Int sizeInTiles) {
List<Vector2Int> region = new List<Vector2Int>();
do {
region.Clear();
Vector2Int centerTile = new Vector2Int(UnityEngine.Random.Range(2, 18 - 3), UnityEngine.Random.Range(2, 10 - 3));
region.Add(centerTile);
int initialXCoordinate = (centerTile.x - (int)Mathf.Floor(sizeInTiles.x / 2));
int initialYCoordinate = (centerTile.y - (int)Mathf.Floor(sizeInTiles.y / 2));
for (int xCoordinate = initialXCoordinate; xCoordinate < initialXCoordinate + sizeInTiles.x; xCoordinate += 1) {
for (int yCoordinate = initialYCoordinate; yCoordinate < initialYCoordinate + sizeInTiles.y; yCoordinate += 1) {
region.Add(new Vector2Int(xCoordinate, yCoordinate));
}
}
} while(!IsFree (region));
return region;
}
private bool IsFree (List<Vector2Int> region) {
foreach (Vector2Int tile in region) {
if (this.population [tile.x, tile.y] != "") {
return false;
}
}
return true;
}
public void AddPopulationToTilemap (Tilemap tilemap, TileBase obstacleTile) {
for (int xIndex = 0; xIndex < 18; xIndex += 1) {
for (int yIndex = 0; yIndex < 10; yIndex += 1) {
if (this.population [xIndex, yIndex] == "Obstacle") {
tilemap.SetTile (new Vector3Int (xIndex - 9, yIndex - 5, 0), obstacleTile);
} else if (this.population [xIndex, yIndex] != "" && this.population [xIndex, yIndex] != "Player") {
GameObject prefab = GameObject.Instantiate (this.name2Prefab[this.population [xIndex, yIndex]]);
prefab.transform.position = new Vector2 (xIndex - 9 + 0.5f, yIndex - 5 + 0.5f);
}
}
}
}
}
any help would be awesome even if you can point me in the direction to a how to.
Nice procedural dungeon generator! Just as a suggestion, could you cache the first room at the beginning when you are generating the dungeon? Then you can grab the initialRoomForPlayerSpawn coordinates/ position as a reference point for the character placement.
Room initialRoomForPlayerSpawn = null;
while (roomsToCreate.Count > 0 && createdRooms.Count < numberOfRooms) {
Room currentRoom = roomsToCreate.Dequeue ();
this.rooms [currentRoom.roomCoordinate.x, currentRoom.roomCoordinate.y] = currentRoom;
createdRooms.Add (currentRoom);
AddNeighbors (currentRoom, roomsToCreate);
/* Cache First Room */
if(createdRooms != null && createdRooms.Count <= 1) {
initialRoomForPlayerSpawn = currentRoom;
}
}
I made a program that creates a frame and then creates 2 balls and moves them around seperately, my problem is that somehow the first or second ball is getting the coordinates of the other, and therefore are being painted on each other, sorry for the bad indentation this is my first time posting a question .
Main Class:
public class Game extends JPanel {
Ball ball01 = new Ball();
Ball ball02 = new Ball();
int ball01x = 0,ball01y = 0,ball02x = 0,ball02y = 0;
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.fillOval(ball01x, ball01y, 10, 10);
g2d.setColor(Color.RED);
g2d.fillOval(ball02x, ball02y, 10, 10);
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Sample Frame");
Game game = new Game();
frame.add(game);
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.createBalls();
while (true) {
game.getCoords();
game.updateBalls();
game.repaint();
System.out.println("Ball01 x: " + game.ball01x + " Ball02 x " + game.ball02x);
Thread.sleep(10);
}
}
public void getCoords(){
ball01x = ball01.getX();
ball01y = ball01.getY();
ball02x = ball02.getX();
ball02y = ball02.getY();
}
public void createBalls(){
ball01.create(300,300);
ball02.create(50,20);
}
public void updateBalls(){
ball01.Ball();
ball02.Ball();
}
}
Ball Class:
public class Ball{
private static int x;
private static int y;
private static int xSize = 30;
private static int ySize = 30;
static boolean xright = true,ydown = true;
static boolean grow = false;
public void Ball(){
this.moveBall();
this.ballSize();
}
public void create(int startX, int startY){
this.x = startX;
this.y = startY;
}
private void moveBall() {
if(this.x >= 370){
xright = false;
}
if(this.x <= 0){
xright = true;
}
if(this.y >= 370){
ydown = false;
}
if(this.y < 0){
ydown = true;
}
if(xright == true){
this.x = this.x + 1;
}else if (xright == false){
this.x = this.x - 1;
}
if(ydown == true){
this.y = this.y + 1;
}else if (ydown == false){
this.y = this.y - 1;
}
}
private void ballSize(){
if (xSize <= 5 && ySize <= 5){
grow = true;
}else if (xSize >= 10 && ySize >= 10){
grow = false;
}
if (grow == true){
xSize = xSize + 1;
ySize = ySize + 1;
//System.out.println("Debug");
}else if (grow == false){
xSize--;
ySize--;
//System.out.println("Debug");
}
}
public int getX(){
return x;
}
public int getY(){
return y;
}
}
I am wondering if it's possible to start the Watchface Service from an activity?
I tried to start the service in the onCreate method of my activity but it does not show the Watchface:
Intent serviceIntent = new Intent(this, CustomWatchFaceService);
startService(serviceIntent);
update
Here is the code for the WatchfaceService
public class CustomWatchFaceService extends CanvasWatchFaceService {
private static final String TAG = "DigitalWatchFaceService";
private static final Typeface BOLD_TYPEFACE =
Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
private static final Typeface NORMAL_TYPEFACE =
Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
private static final long NORMAL_UPDATE_RATE_MS = 500;
private static final long MUTE_UPDATE_RATE_MS = TimeUnit.MINUTES.toMillis(1);
#Override
public Engine onCreateEngine() {
return new Engine();
}
private class Engine extends CanvasWatchFaceService.Engine {
static final String COLON_STRING = ":";
static final int MUTE_ALPHA = 100;
static final int NORMAL_ALPHA = 255;
static final int MSG_UPDATE_TIME = 0;
long mInteractiveUpdateRateMs = NORMAL_UPDATE_RATE_MS;
final Handler mUpdateTimeHandler = new Handler() {
#Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_UPDATE_TIME:
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "updating time");
}
invalidate();
if (shouldTimerBeRunning()) {
long timeMs = System.currentTimeMillis();
long delayMs =
mInteractiveUpdateRateMs - (timeMs % mInteractiveUpdateRateMs);
mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
}
break;
}
}
};
Paint mBackgroundPaint;
Bitmap mBackgroundBitmap;
Bitmap wifiIconOn;
Bitmap wifiIconOff;
Paint mDatePaint;
Paint mNotificationPaint;
Paint mNotificationMax;
Paint mNotificationHigh;
Paint mHourPaint;
Paint mMinutePaint;
Paint mSecondPaint;
Paint mAmPmPaint;
Paint mColonPaint;
float mColonWidth;
boolean mMute;
Calendar mCalendar;
Date mDate;
SimpleDateFormat mDayOfWeekFormat;
java.text.DateFormat mDateFormat;
boolean mShouldDrawColons;
float mXOffset;
float mYOffset;
float mLineHeight;
int mInteractiveBackgroundColor =
R.color.interactive_bg;
int mInteractiveNotificationMax =
R.color.notification_max;
int mInteractiveNotificationHigh =
R.color.notification_high;
int mInteractiveNotificationColor =
R.color.notification;
int mInteractiveHourDigitsColor =
R.color.interactive_time;
int mInteractiveMinuteDigitsColor =
R.color.interactive_time;
int mInteractiveSecondDigitsColor =
R.color.interactive_time;
boolean mLowBitAmbient;
#Override
public void onCreate(SurfaceHolder holder) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onCreate");
}
super.onCreate(holder);
Locale locale = new Locale("de");
Locale.setDefault(locale);
Configuration config = getResources().getConfiguration();
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
setWatchFaceStyle(new WatchFaceStyle.Builder(CustomWatchFaceService.this)
.setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
.setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
.setShowSystemUiTime(false)
.build());
Resources resources = CustomWatchFaceService.this.getResources();
mYOffset = resources.getDimension(R.dimen.digital_y_offset);
mLineHeight = resources.getDimension(R.dimen.digital_line_height);
setInteractiveColors();
// Not sure why the text color needs to be set here again ... it should be set in setDefaultColors()!
mDatePaint.setColor(getColor(R.color.digital_date));
mNotificationPaint.setColor(getColor(R.color.notification));
mNotificationMax.setColor(getColor(R.color.notification_max));
mNotificationHigh.setColor(getColor(R.color.notification_high));
mHourPaint.setColor(getColor(R.color.interactive_time));
mMinutePaint.setColor(getColor(R.color.interactive_time));
mSecondPaint.setColor(getColor(R.color.interactive_time));
mColonPaint.setColor(getColor(R.color.interactive_time));
//Images should be loaded here so they can be called during the Draw Method
wifiIconOn = BitmapFactory.decodeResource(CustomWatchFaceService.this.getResources(), R.drawable.wifi_on_small);
wifiIconOff = BitmapFactory.decodeResource(CustomWatchFaceService.this.getResources(), R.drawable.wifi_off_small);
mBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.customcart_logo_240_alpha);
mCalendar = Calendar.getInstance();
mDate = new Date();
initFormats();
}
public void setInteractiveColors() {
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(getColor(mInteractiveBackgroundColor));
mNotificationPaint = createTextPaint(mInteractiveNotificationColor);
mNotificationMax = createTextPaint(mInteractiveNotificationMax);
mNotificationHigh = createTextPaint(mInteractiveNotificationHigh);
mDatePaint = createTextPaint(R.color.digital_date);
mHourPaint = createTextPaint(mInteractiveHourDigitsColor, BOLD_TYPEFACE);
mMinutePaint = createTextPaint(mInteractiveMinuteDigitsColor);
mSecondPaint = createTextPaint(mInteractiveSecondDigitsColor);
mColonPaint = createTextPaint(R.color.digital_colons);
}
#Override
public void onDestroy() {
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
super.onDestroy();
}
private Paint createTextPaint(int defaultInteractiveColor) {
return createTextPaint(defaultInteractiveColor, NORMAL_TYPEFACE);
}
private Paint createTextPaint(int defaultInteractiveColor, Typeface typeface) {
Paint paint = new Paint();
paint.setColor(defaultInteractiveColor);
paint.setTypeface(typeface);
paint.setAntiAlias(true);
return paint;
}
#Override
public void onVisibilityChanged(boolean visible) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onVisibilityChanged: " + visible);
}
super.onVisibilityChanged(visible);
updateTimer();
}
private void initFormats() {
mDayOfWeekFormat = new SimpleDateFormat("EEEE", Locale.getDefault());
mDayOfWeekFormat.setCalendar(mCalendar);
mDateFormat = DateFormat.getDateFormat(CustomWatchFaceService.this);
mDateFormat.setCalendar(mCalendar);
}
#Override
public void onApplyWindowInsets(WindowInsets insets) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onApplyWindowInsets: " + (insets.isRound() ? "round" : "square"));
}
super.onApplyWindowInsets(insets);
// Load resources that have alternate values for round watches.
Resources resources = CustomWatchFaceService.this.getResources();
boolean isRound = insets.isRound();
mXOffset = resources.getDimension(isRound
? R.dimen.digital_x_offset_round : R.dimen.digital_x_offset);
float textSize = resources.getDimension(isRound
? R.dimen.digital_text_size_round : R.dimen.digital_text_size);
float notificationTextSize = resources.getDimension(isRound
? R.dimen.notification_text_size : R.dimen.notification_text_size);
mDatePaint.setTextSize(resources.getDimension(R.dimen.digital_date_text_size));
mHourPaint.setTextSize(textSize);
mMinutePaint.setTextSize(textSize);
mSecondPaint.setTextSize(textSize);
mColonPaint.setTextSize(textSize);
mNotificationPaint.setTextSize(notificationTextSize);
mColonWidth = mColonPaint.measureText(COLON_STRING);
}
#Override
public void onPropertiesChanged(Bundle properties) {
super.onPropertiesChanged(properties);
boolean burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
mHourPaint.setTypeface(burnInProtection ? NORMAL_TYPEFACE : BOLD_TYPEFACE);
mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onPropertiesChanged: burn-in protection = " + burnInProtection
+ ", low-bit ambient = " + mLowBitAmbient);
}
}
#Override
public void onTimeTick() {
super.onTimeTick();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
}
invalidate();
}
#Override
public void onAmbientModeChanged(boolean inAmbientMode) {
super.onAmbientModeChanged(inAmbientMode);
if (!isInAmbientMode()) {
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(getColor(R.color.interactive_bg));
mDatePaint.setColor(getColor(R.color.digital_date));
mHourPaint.setColor(getColor(R.color.interactive_time));
mMinutePaint.setColor(getColor(R.color.interactive_time));
mSecondPaint.setColor(getColor(R.color.interactive_time));
mColonPaint.setColor(getColor(R.color.interactive_time));
}
else {
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(getColor(R.color.ambient_bg));
mDatePaint.setColor(getColor(R.color.digital_date));
mHourPaint.setColor(getColor(R.color.ambient_time));
mMinutePaint.setColor(getColor(R.color.ambient_time));
mSecondPaint.setColor(getColor(R.color.ambient_time));
mColonPaint.setColor(getColor(R.color.ambient_time));
}
//Log.d("XXX", "onAmbientModeChanged: " + inAmbientMode);
if (mLowBitAmbient) {
boolean antiAlias = !inAmbientMode;
mDatePaint.setAntiAlias(antiAlias);
mHourPaint.setAntiAlias(antiAlias);
mMinutePaint.setAntiAlias(antiAlias);
mSecondPaint.setAntiAlias(antiAlias);
mAmPmPaint.setAntiAlias(antiAlias);
mColonPaint.setAntiAlias(antiAlias);
}
invalidate();
// Whether the timer should be running depends on whether we're in ambient mode (as well
// as whether we're visible), so we may need to start or stop the timer.
updateTimer();
}
#Override
public void onInterruptionFilterChanged(int interruptionFilter) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onInterruptionFilterChanged: " + interruptionFilter);
}
super.onInterruptionFilterChanged(interruptionFilter);
boolean inMuteMode = interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE;
// We only need to update once a minute in mute mode.
setInteractiveUpdateRateMs(inMuteMode ? MUTE_UPDATE_RATE_MS : NORMAL_UPDATE_RATE_MS);
if (mMute != inMuteMode) {
mMute = inMuteMode;
int alpha = inMuteMode ? MUTE_ALPHA : NORMAL_ALPHA;
mDatePaint.setAlpha(alpha);
mHourPaint.setAlpha(alpha);
mMinutePaint.setAlpha(alpha);
mColonPaint.setAlpha(alpha);
mAmPmPaint.setAlpha(alpha);
invalidate();
}
}
public void setInteractiveUpdateRateMs(long updateRateMs) {
if (updateRateMs == mInteractiveUpdateRateMs) {
return;
}
mInteractiveUpdateRateMs = updateRateMs;
// Stop and restart the timer so the new update rate takes effect immediately.
if (shouldTimerBeRunning()) {
updateTimer();
}
}
private String formatTwoDigitNumber(int hour) {
return String.format("%02d", hour);
}
#Override
public void onDraw(Canvas canvas, Rect bounds) {
long now = System.currentTimeMillis();
int width = bounds.width();
int height = bounds.height();
mCalendar.setTimeInMillis(now);
mDate.setTime(now);
boolean is24Hour = DateFormat.is24HourFormat(CustomWatchFaceService.this);
// Draw the background.
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
//Draw the background Image
if (mBackgroundBitmap == null
|| mBackgroundBitmap.getWidth() != width
|| mBackgroundBitmap.getHeight() != height) {
mBackgroundBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
width, height, true /* filter */);
}
//Log.d("XXX", "Width: "+ mBackgroundBitmap.getWidth() + "Height: "+mBackgroundBitmap.getHeight() );
if (isInAmbientMode() && (mLowBitAmbient)) {
canvas.drawColor(Color.BLACK);
} else if (isInAmbientMode()) {
canvas.drawColor(Color.BLACK);
} else {
canvas.drawBitmap(mBackgroundBitmap, 0, 0, mBackgroundPaint);
}
// Show colons for the first half of each second so the colons blink on when the time updates.
mShouldDrawColons = (System.currentTimeMillis() % 1000) < 500;
// Draw the hours.
float x = mXOffset;
String hourString;
if (is24Hour) {
hourString = formatTwoDigitNumber(mCalendar.get(Calendar.HOUR_OF_DAY));
} else {
int hour = mCalendar.get(Calendar.HOUR);
if (hour == 0) {
hour = 12;
}
hourString = String.valueOf(hour);
}
canvas.drawText(hourString, x, mYOffset, mHourPaint);
x += mHourPaint.measureText(hourString);
// In ambient and mute modes, always draw the first colon. Otherwise, draw the
// first colon for the first half of each second.
if (isInAmbientMode() || mMute || mShouldDrawColons) {
canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
}
x += mColonWidth;
// Draw the minutes.
String minuteString = formatTwoDigitNumber(mCalendar.get(Calendar.MINUTE));
canvas.drawText(minuteString, x, mYOffset, mMinutePaint);
x += mMinutePaint.measureText(minuteString);
// In unmuted interactive mode, draw a second blinking colon followed by the seconds.
// Otherwise, if we're in 12-hour mode, draw AM/PM
if (!isInAmbientMode() && !mMute) {
if (mShouldDrawColons) {
canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
}
x += mColonWidth;
canvas.drawText(formatTwoDigitNumber(
mCalendar.get(Calendar.SECOND)), x, mYOffset, mSecondPaint);
} else if (!is24Hour) {
x += mColonWidth;
}
// Only render the day of week and date if there is no peek card, so they do not bleed
// into each other in ambient mode.
if (getPeekCardPosition().isEmpty()) {
// Day of week
canvas.drawText(
mDayOfWeekFormat.format(mDate),
mXOffset, mYOffset + mLineHeight, mDatePaint);
// Date
canvas.drawText(
mDateFormat.format(mDate),
mXOffset, mYOffset + mLineHeight * 2, mDatePaint);
}
}
/**
* Starts the {#link #mUpdateTimeHandler} timer if it should be running and isn't currently
* or stops it if it shouldn't be running but currently is.
*/
private void updateTimer() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "updateTimer");
}
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
if (shouldTimerBeRunning()) {
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
/**
* Returns whether the {#link #mUpdateTimeHandler} timer should be running. The timer should
* only run when we're visible and in interactive mode.
*/
private boolean shouldTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
}
}
I have a problem again with the JavaFX Chart : D
Context :
I had Popup/Label on my chart to display the value on hover : JavaFX LineChart Hover Values (Jewelsea answer)
Problem :
But when the point are near the edges of chart, the popup is hidden by them.
The chart with problem, I highlighted the edges of chart.
This is a problem, because my popup is bigger and display more informations (x value, y value and the data serie)
Possible solutions :
May I can check where the edges are, and if the popup is hide. In this case, I should shift the popup. But when I look doc, I didn't found the right method :
XYChart
XYChart.Data#nodeProperty
May I can put the popup above the chart. Like z-index in CSS.
The code :
import javafx.application.Application;
import javafx.collections.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Displays a LineChart which displays the value of a plotted Node when you hover over the Node.
* #author original, jewelsea https://gist.github.com/jewelsea
*/
public class LineChartWithHover extends Application {
#SuppressWarnings("unchecked")
#Override public void start(Stage stage) {
final LineChart lineChart = new LineChart(
new NumberAxis(), new NumberAxis(),
FXCollections.observableArrayList(
new XYChart.Series(
"My portfolio",
FXCollections.observableArrayList(
plot(0, 14, 15, 24, 34, 36, 22, 55, 43, 17, 29, 25)
)
)
)
);
lineChart.setCursor(Cursor.CROSSHAIR);
lineChart.setTitle("Stock Monitoring, 2013");
stage.setScene(new Scene(lineChart, 500, 400));
stage.show();
System.out.println("test 1 = "+lineChart.getProperties());
}
/** #return plotted y values for monotonically increasing integer x values, starting from x=1 */
public ObservableList<XYChart.Data<Integer, Integer>> plot(int... y) {
final ObservableList<XYChart.Data<Integer, Integer>> dataset = FXCollections.observableArrayList();
int i = 0;
while (i < y.length) {
final XYChart.Data<Integer, Integer> data = new XYChart.Data<>(i + 1, y[i]);
data.setNode(
new HoveredThresholdNode(
(i == 0) ? 0 : y[i-1],
y[i]
)
);
dataset.add(data);
i++;
}
return dataset;
}
/** a node which displays a value on hover, but is otherwise empty */
class HoveredThresholdNode extends StackPane {
HoveredThresholdNode(int priorValue, int value) {
setPrefSize(15, 15);
final Label label = createDataThresholdLabel(priorValue, value);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
setCursor(Cursor.NONE);
toFront();
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().clear();
setCursor(Cursor.CROSSHAIR);
}
});
}
private Label createDataThresholdLabel(int priorValue, int value) {
final Label label = new Label(value + "");
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (value > priorValue) {
label.setTextFill(Color.FORESTGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
}
public static void main(String[] args) { launch(args); }
}
Thank you in advance ! And my apologies about my english, still learning !
I have been looking on the JavaFX CSS reference guide and I could'nt find anything to simply solve your problem.
A possible solution is to translate your symbol depending on how near it is to the max or min value.
I wrote something like this, based on your code :
/**
* Displays a LineChart which displays the value of a plotted Node when you hover over the Node.
* #author original, jewelsea https://gist.github.com/jewelsea
*/
public class LineChartWithHover extends Application {
#SuppressWarnings("unchecked")
#Override public void start(Stage stage) {
final LineChart lineChart = new LineChart(
new NumberAxis(), new NumberAxis(),
FXCollections.observableArrayList(
new XYChart.Series(
"My portfolio",
FXCollections.observableArrayList(
plot(0, 14, 15, 24, 34, 36, 22, 55, 43, 17, 29, 25)
)
)
)
);
lineChart.setCursor(Cursor.CROSSHAIR);
lineChart.setTitle("Stock Monitoring, 2013");
stage.setScene(new Scene(lineChart, 500, 400));
stage.show();
System.out.println("test 1 = "+lineChart.getProperties());
}
/** #return plotted y values for monotonically increasing integer x values, starting from x=1 */
public ObservableList<XYChart.Data<Integer, Integer>> plot(Integer... y) {
final ObservableList<XYChart.Data<Integer, Integer>> dataset = FXCollections.observableArrayList();
int i = 0;
List<Integer> list = Arrays.asList(y);
int min = Collections.min(list);
int max = Collections.max(list);
int minThreshold = 5;
int maxThreshold = 5;
while (i < y.length) {
final XYChart.Data<Integer, Integer> data = new XYChart.Data<>(i + 1, y[i]);
int topMargin = 0;
if(y[i] <= min + minThreshold) {
topMargin = -50;
} else if (y[i] >= max - maxThreshold) {
topMargin = 50;
}
StackPane stackPane = new HoveredThresholdNode(
(i == 0) ? 0 : y[i-1],
y[i],
topMargin
);
data.setNode(stackPane);
dataset.add(data);
i++;
}
return dataset;
}
/** a node which displays a value on hover, but is otherwise empty */
class HoveredThresholdNode extends StackPane {
HoveredThresholdNode(int priorValue, int value, int topMargin) {
setPrefSize(15, 15);
final Label label = createDataThresholdLabel(priorValue, value);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
setCursor(Cursor.NONE);
toFront();
setMargin(label, new Insets(topMargin,0,0,0));
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().clear();
setCursor(Cursor.CROSSHAIR);
}
});
}
private Label createDataThresholdLabel(int priorValue, int value) {
final Label label = new Label(value + "");
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (value > priorValue) {
label.setTextFill(Color.FORESTGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
}
public static void main(String[] args) { launch(args); }
}
Basically, I am just saying that all values <= min+5 and >= max-5 must be translated.
The +/- 5 is arbitrary and should be calculated from the ticks gap and plot scale to have a perfect repositioning. Anyway, without performing any maths, it is still quite satisfying.
Based on Mr Kwoinkwoin solution, I wrote my own.
Im not sure if its possible to optimize it or improve it. But seems to be working for me so far.
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.NumberAxis.DefaultFormatter;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import static javafx.scene.layout.StackPane.setMargin;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class StockLineChartApp extends Application {
private LineChart<Number, Number> chart;
private Series<Number, Number> series;
private NumberAxis xAxis;
private ZonedDateTime time;
public StockLineChartApp() {
time = ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2000, Month.JANUARY, 1), LocalTime.NOON), ZoneId.systemDefault());
}
public Parent createContent() {
xAxis = new NumberAxis();
xAxis.setLabel("Date/Time");
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelFormatter(new StringConverter<Number>() {
#Override
public String toString(Number t) {
long longValue = t.longValue();
ZonedDateTime zd = convertLongToZonedDateTime(longValue);
String formatDate = formatDate(zd, "dd/MM/yyyy");
return formatDate;
}
#Override
public Number fromString(String string) {
ZonedDateTime dl = ZonedDateTime.parse(string, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
long toEpochMilli = dl.toEpochSecond();
//DateTimeFormatter.ofPattern(string).p
return toEpochMilli;
}
});
final NumberAxis yAxis = new NumberAxis();
yAxis.setAutoRanging(true);
chart = new LineChart<>(xAxis, yAxis);
chart.setCursor(Cursor.CROSSHAIR);
chart.setAlternativeRowFillVisible(true);
chart.setAlternativeColumnFillVisible(true);
// setup chart
//final String stockLineChartCss= getClass().getResource("StockLineChart.css").toExternalForm();
//chart.getStylesheets().add(stockLineChartCss);
chart.setCreateSymbols(true);
chart.setAnimated(true);
chart.setLegendVisible(true);
chart.setTitle("ACME Company Stock");
yAxis.setLabel("Share Price");
yAxis.setTickLabelFormatter(new DefaultFormatter(yAxis, "$", null));
// add starting data
series = new Series<>();
series.setName("Data por Peça");
for (double m = 0; m < (10); m++) {
long data = nextTime();
addData(data, (long) (Math.random() * 10));
System.out.println(data);
}
//chart.
chart.getData().add(series);
//chart.getData().add(hourDataSeries);
return chart;
}
private void addData(long x, long y) {
Data<Number, Number> data = new Data<Number, Number>(x, y);
series.getData().add(data);
ZonedDateTime zd = convertLongToZonedDateTime(x);
String formatDate = formatDate(zd, "dd/MM/yyyy");
//String text = "(" + formatDate + ";" + y + ")";
String text = y + "";
if (text.length() > 4) {
text = text.substring(0, 4);
}
String t = formatDate + "\nValor: " + text;
data.setNode(new HoveredThresholdNode(t, data));
}
public static long convertZonedDateTimeToLong(ZonedDateTime zonedDateTime) {
long e = zonedDateTime.toInstant().toEpochMilli();
return e;
}
private long nextTime() {
time = time.plusYears(10);
return convertZonedDateTimeToLong(time);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent createContent = createContent();
//
final StackPane pane = new StackPane();
pane.getChildren().add(createContent);
final Scene scene = new Scene(pane, 500, 400);
//new ZoomManager(pane, chart, series);
//
primaryStage.setScene(scene);
primaryStage.show();
}
public static String formatDate(ZonedDateTime ts, String format) {
try {
if (ts == null) {
return "";
}
String format1 = ts.format(DateTimeFormatter.ofPattern(format));
return format1;
} catch (Exception ex) {
ex.printStackTrace();
}
return "";
}
public static ZonedDateTime convertLongToZonedDateTime(long e) {
Instant i = Instant.ofEpochMilli(e);
ZonedDateTime ofInstant = ZonedDateTime.ofInstant(i, ZoneId.systemDefault());
return ofInstant;
}
/**
* Java main for when running without JavaFX launcher
*/
public static void main(String[] args) {
launch(args);
}
public class HoveredThresholdNode extends StackPane {
//Reference
private Data<Number, Number> data;
private Label label;
private String value;
public HoveredThresholdNode(String value, Data<Number, Number> data) {
this.data = data;
this.value = value;
this.label = new Label(value);
this.label.getStyleClass().clear();
this.getStyleClass().clear();
this.label.setStyle("-fx-font-size: 12; fx-text-fill: black;");
this.label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
this.label.setWrapText(true);
this.label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
toFront();
boolean close_top = false, close_right = false, close_bottom = false, close_left = false;
long min_x = Long.MAX_VALUE;
long max_x = Long.MIN_VALUE;
long min_y = Long.MAX_VALUE;
long max_y = Long.MIN_VALUE;
ObservableList<Series<Number, Number>> chartSeries = chart.getData();
for (Series<Number, Number> s : chartSeries) {
ObservableList<Data<Number, Number>> chartData = s.getData();
for (Data<Number, Number> d : chartData) {
Number xValue = d.getXValue();
Number yValue = d.getYValue();
long kx = xValue.longValue();
long ky = yValue.longValue();
if (kx < min_x) {
min_x = kx;
}
if (kx > max_x) {
max_x = kx;
}
if (ky < min_y) {
min_y = ky;
}
if (ky > max_y) {
max_y = ky;
}
}
}
if (data.getXValue().longValue() - max_x == 0) {
close_right = true;
}
if (data.getXValue().longValue() - min_x == 0) {
close_left = true;
}
if (data.getYValue().longValue() - min_y == 0) {
close_bottom = true;
}
if (data.getYValue().longValue() - max_y == 0) {
close_top = true;
}
// System.out.println("\n");
// System.out.println(" close_right " + close_right);
// System.out.println(" close_left " + close_left);
// System.out.println(" close_bottom " + close_bottom);
// System.out.println(" close_top " + close_top);
double top = 0;
double right = 0;
double bottom = 0;
double left = 0;
if (close_top) {
top = 50;
}
if (close_bottom) {
bottom = 50;
}
if (close_right) {
right = 50;
}
if (close_left) {
left = 50;
}
setMargin(label, new Insets(top, right, bottom, left));
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
getChildren().clear();
}
});
}
public HoveredThresholdNode copy() {
HoveredThresholdNode copy = new HoveredThresholdNode(value, data);
return copy;
}
}
}
I am very new to blackberry development and i don't even know how to start. I already read some part of it from it's official site
http://developer.blackberry.com/devzone/files/design/bb7/UI_Guidelines_BlackBerry_Smartphones_7_1.pdf
and other so many link, but i can not post all the link as it is saying thay if you want to post more links then you must have 10 reputation and i dont have that so sorry for that,
Now my question is i want to design layout like this http://postimg.org/image/we3leycsd/
How can i design exactly this kind of layout. I am using eclipse for Blackberry development.
Please help i already tried many things but i am not able to achieve this.
Your any kind of help would be appreciated. Thank you in advance.
I'd create a custom HorizontalFieldManager with n VerticalFieldManagers inside, then override the add and delete methods. Here is something I made before, that should work for you, it adds the new fields to the shortest column.
StaggeredListView.java:
public class StaggeredListView extends HorizontalFieldManager
{
private int column_spacing = 0;
public StaggeredListView(int columns)
{
super(VERTICAL_SCROLL | VERTICAL_SCROLLBAR | NO_HORIZONTAL_SCROLL | NO_HORIZONTAL_SCROLLBAR | USE_ALL_WIDTH);
if (columns < 1)
{
throw new RuntimeException("Number of columns needs to be larger than 0.");
}
final int width = Display.getWidth() / columns;
for (int i = 0; i < columns; i++)
{
VerticalFieldManager vfm = new VerticalFieldManager(NO_VERTICAL_SCROLL | NO_VERTICAL_SCROLLBAR | NO_HORIZONTAL_SCROLL | NO_HORIZONTAL_SCROLLBAR)
{
protected void sublayout(int maxWidth, int maxHeight)
{
maxWidth = Math.min(width, getPreferredWidth());
maxHeight = Math.min(maxHeight, getPreferredHeight());
super.sublayout(width, maxHeight);
super.setExtent(width, maxHeight);
}
};
super.add(vfm);
}
}
public int getColumnCount()
{
return getFieldCount();
}
/**
* Sets the spacing between columns.
*
* <p>
* Spacing between fields is <i><b>not</b></i> set.
* </p>
*/
public void setColumnSpacing(int spacing)
{
if (spacing < 0) throw new RuntimeException("Column spacing my not be negative.");
int length = getColumnCount();
for (int i = 1; i < length; i++)
{
((VerticalFieldManager) getField(i)).setPadding(0, 0, 0, spacing);
}
column_spacing = spacing;
}
/**
* Get the value currently assigned via the {#link #setColumnSpacing(int)} method.
*
* #return
*/
public int getColumnSpacing()
{
return column_spacing;
}
/**
* Deletes all fields from each of the columns.
*/
public void clear()
{
int length = getColumnCount();
for (int i = 0; i < length; i++)
{
((VerticalFieldManager) getField(i)).deleteAll();
}
}
/**
* Delete specified field from the columns.
*
* <p>
* Does <b><i>not</i></b> rearrange fields.
* </p>
*/
public void delete(Field field)
{
int length = getColumnCount();
for (int i = 0; i < length; i++)
{
try
{
((VerticalFieldManager) getField(i)).delete(field);
break;
} catch (IllegalArgumentException e)
{
// field not in this manager
}
}
}
/**
* Adds the field to the column with the least height.
*/
public void add(Field field)
{
// find the vfm with least height
int index = 0;
int height = ((VerticalFieldManager) getField(index)).getPreferredHeight();
int length = getColumnCount();
for (int i = 1; i < length; i++)
{
int temp_height = ((VerticalFieldManager) getField(i)).getPreferredHeight();
if (temp_height < height)
{
height = temp_height;
index = i;
}
}
((VerticalFieldManager) getField(index)).add(field);
}
}
As for the item's contained in it, I'd create a field with an image and text, then paint it myself (I've had a lot of issues with focus and find it easier just to use paint).
You can use this to make a BaseButton http://developer.blackberry.com/bbos/java/documentation/tutorial_create_custom_button_1969896_11.html
BaseButton.java:
public abstract class BaseButton extends Field
{
// flags to indicate the current visual state
protected boolean _visible = true;
protected boolean _active;
protected boolean _focus;
protected boolean drawfocus = false;
private int touch_top = 0;
private int touch_right = 0;
private int touch_bottom = 0;
private int touch_left = 0;
protected boolean fire_on_click = true; // false fires on unclick
public BaseButton()
{
this(0);
}
public BaseButton(long style)
{
super((style & Field.NON_FOCUSABLE) == Field.NON_FOCUSABLE ? style : style | Field.FOCUSABLE);
}
/**
* Sets the radius around the button to trigger touch events.
* <p>
* (0,0,0,0) by default.
* </p>
*/
public void setTouchRadius(int top, int right, int bottom, int left)
{
touch_top = top;
touch_right = right;
touch_bottom = bottom;
touch_left = left;
}
protected void onFocus(int direction)
{
_focus = true;
invalidate();
super.onFocus(direction);
}
protected void onUnfocus()
{
if (_active || _focus)
{
_focus = false;
_active = false;
invalidate();
}
super.onUnfocus();
}
public void set_visible(boolean visible)
{
_visible = visible;
invalidate();
}
public boolean is_visible()
{
return _visible;
}
protected void drawFocus(Graphics g, boolean on)
{
if (drawfocus) super.drawFocus(g, on);
}
protected void layout(int width, int height)
{
setExtent(Math.min(width, getPreferredWidth()), Math.min(height, getPreferredHeight()));
}
protected boolean keyUp(int keycode, int time)
{
if (Keypad.map(Keypad.key(keycode), Keypad.status(keycode)) == Characters.ENTER)
{
_active = false;
invalidate();
return true;
}
return false;
}
protected boolean keyDown(int keycode, int time)
{
if (Keypad.map(Keypad.key(keycode), Keypad.status(keycode)) == Characters.ENTER)
{
_active = true;
invalidate();
}
return super.keyDown(keycode, time);
}
protected boolean keyChar(char character, int status, int time)
{
if (character == Characters.ENTER)
{
clickButton();
return true;
}
return super.keyChar(character, status, time);
}
protected boolean navigationClick(int status, int time)
{
if (status != 0)
{ // non-touch event
_active = true;
invalidate();
if (fire_on_click) clickButton();
}
return true;
}
protected boolean trackwheelClick(int status, int time)
{
if (status != 0)
{ // non-touch event
_active = true;
invalidate();
if (fire_on_click) clickButton();
}
return true;
}
protected boolean navigationUnclick(int status, int time)
{
if (status != 0)
{ // non-touch event
_active = false;
invalidate();
if (!fire_on_click) clickButton();
}
return true;
}
protected boolean trackwheelUnclick(int status, int time)
{
if (status != 0)
{ // non-touch event
_active = false;
invalidate();
if (!fire_on_click) clickButton();
}
return true;
}
protected boolean invokeAction(int action)
{
switch (action)
{
case ACTION_INVOKE :
{
clickButton();
return true;
}
}
return super.invokeAction(action);
}
protected boolean touchEvent(TouchEvent message)
{
boolean isOutOfBounds = touchEventOutOfBounds(message);
switch (message.getEvent())
{
case TouchEvent.CLICK :
if (!_active)
{
_active = true;
invalidate();
}
if (!isOutOfBounds)
{
if (fire_on_click) clickButton();
return true;
}
case TouchEvent.DOWN :
if (!isOutOfBounds)
{
if (!_active)
{
_active = true;
invalidate();
}
return true;
}
return false;
case TouchEvent.UNCLICK :
if (_active)
{
_active = false;
invalidate();
}
if (!isOutOfBounds)
{
if (!fire_on_click) clickButton();
return true;
}
case TouchEvent.UP :
if (_active)
{
_active = false;
invalidate();
}
default :
return false;
}
}
private boolean touchEventOutOfBounds(TouchEvent message)
{
int x = message.getX(1);
int y = message.getY(1);
return (x < 0 - touch_left || y < 0 - touch_top || x > getWidth() + touch_right || y > getHeight() + touch_bottom);
}
public void setDirty(boolean dirty)
{
}
public void setMuddy(boolean muddy)
{
}
public void clickButton()
{
if (_visible) fieldChangeNotify(0);
}
}
ImageSubtitleButton.java:
public class ImageSubtitleButton extends BaseButton
{
private static final int FOCUS_THINKNESS = 2;
String title;
Bitmap image_default;
int height;
public ImageSubtitleButton(String title, String image_default)
{
this.image_default = Bitmap.getBitmapResource(image_default);
setTitle(title);
}
public void setTitle(String title)
{
this.title = title;
height = image_default.getHeight() + getFont().getHeight() + (FOCUS_THINKNESS * 2);
updateLayout();
invalidate();
}
public int getPreferredWidth()
{
return Math.max(getFont().getAdvance(title), image_default.getWidth());
}
public int getPreferredHeight()
{
return height;
}
protected void paint(Graphics graphics)
{
int x = (getWidth() - image_default.getWidth()) / 2;
int y = 0;
graphics.drawBitmap(x, y, image_default.getWidth(), image_default.getHeight(), image_default, 0, 0);
if (_focus)
{
graphics.setColor(Color.BLUE); // your focus colour
for (int i = 0; i < FOCUS_THINKNESS; i++)
{
graphics.drawRect(x + i, y + i, image_default.getWidth() - (i * 2), image_default.getHeight() - (i * 2));
}
}
graphics.setColor(Color.BLACK);
y = image_default.getHeight();
graphics.drawText(title, x, y);
}
}
Now you can add these to your screen as follows:
StaggedListView listview = new StaggedListView(2);
ImageSubtitleButton button = new ImageSubtitleButton("test", "test.png");
listview.add(button);
add(listview);
You'll need to set the preferred width and height of the ImageSubtitleButton to keep it uniform, as in the example image you posted.
Apologies, I don't have time to create a full answer, but I personally would not use the HFM/VFM combination to do this. Instead use one Manager that provides the Grid. If you are using a late enough level OS you have GridFieldManager that does this,
GridFieldManager
but I have had mixed experiences with that Manager. So I generally use this Manager:
TableLayoutManager
I hope this gets you going.