I'm developing a mobile app using j2me and lwuit.
There is a lcdui DateField (act as date picker) in j2me. Like that there is any component or item in lwuit.
How to implement the date picker (Similar to lcdui DateField) in lwuit.
The calendar (in lwuit) object is not user friendly. If phone screen size is small then it will not be correctly shown. In normal j2me (lcdui) the datefield has very good look. I want to create a component/item like that in lwuit (using lwuit in j2me).
You can use lcdui DateField in lwuit. I do it by this way:
import java.util.Calendar;
import java.util.Date;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.DateField;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import com.sun.lwuit.Button;
import com.sun.lwuit.Display;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.plaf.UIManager;
public class DatePicker extends Button implements ActionListener {
private static final String OK = "ok";
private static final String CANCEL = "cancel";
private Date date;
public DatePicker() {
setUIID("TextArea");
addActionListener(this);
}
public Date getDate() {
return date;
}
public void actionPerformed(ActionEvent evt) {
final Form dateForm = new Form();
final DateField dateField = new DateField(null, DateField.DATE);
if (date != null) {
dateField.setDate(date);
}
dateForm.append(dateField);
final javax.microedition.lcdui.Command acceptCommand = new javax.microedition.lcdui.Command(UIManager.getInstance().localize(OK, OK),
javax.microedition.lcdui.Command.OK, 0);
final javax.microedition.lcdui.Command cancelCommand = new javax.microedition.lcdui.Command(UIManager.getInstance().localize(CANCEL, CANCEL), javax.microedition.lcdui.Command.CANCEL, 0);
dateForm.addCommand(acceptCommand);
dateForm.addCommand(cancelCommand);
CommandListener commandListener = new CommandListener() {
public void commandAction(javax.microedition.lcdui.Command command, Displayable displayable) {
if (command == acceptCommand && dateField.getDate() != null) {
DatePicker.this.date = dateField.getDate();
DatePicker.this.setText(DatePicker.toString(DatePicker.this.date));
}
Display.init(Application.getInstance().midlet); // You have to save your midlet
Application.getInstance().mainForm.show(); // and the last lwuit Form
}
};
dateForm.setCommandListener(commandListener);
javax.microedition.lcdui.Display.getDisplay(Application.getInstance().midlet).setCurrent(dateForm); // Application.getInstance().midlet - your j2me application midlet
}
}
So now you can use it like lwuit Component, but on the actionPerformed it will open lcdui native Form with DateField on the top.
I have developed a calender extending component and it work fine in LWUIT and the code I used as follows
public class Calendar extends Container {
private ComboBox month;
private ComboBox year;
private MonthView mv;
private static final String[] MONTHS = new String[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
private static final String[] DAYS = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
private static final String[] LABELS = {"Su", "M", "Tu", "W", "Th", "F", "Sa"};
static final long MINUTE = 1000 * 60;
static final long HOUR = MINUTE * 60;
static final long DAY = HOUR * 24;
static final long WEEK = DAY * 7;
private EventDispatcher dispatcher = new EventDispatcher();
private EventDispatcher dataChangeListeners = new EventDispatcher();
private long[] dates = new long[42];
private boolean changesSelectedDateEnabled = true;
private TimeZone tmz;
private static ComboListCellRenderer comboListCellRenderer = new ComboListCellRenderer();;
private int[] bgColorArray = new int[]{0x2E9AFE, 0x39FEB6, 0x39FEB6};
/**
* Creates a new instance of Calendar set to the given date based on time
* since epoch (the java.util.Date convention)
*
* #param time time since epoch
*/
public Calendar(int[] bgColorArray, ListCellRenderer listCellRenderer, long time) {
this(bgColorArray, listCellRenderer, time, java.util.TimeZone.getDefault());
}
/**
* Constructs a calendar with the current date and time
*/
public Calendar(int[] bgColorArray, ListCellRenderer listCellRenderer) {
this(bgColorArray, listCellRenderer, System.currentTimeMillis());
}
/**
* Constructs a calendar with the current date and time
*/
public Calendar() {
this(null, comboListCellRenderer, System.currentTimeMillis());
}
/**
* Creates a new instance of Calendar set to the given date based on time
* since epoch (the java.util.Date convention)
*
* #param time time since epoch
* #param tmz a reference timezone
*/
public Calendar(int[] bgColorArray, ListCellRenderer listCellRenderer, long time, TimeZone tmz) {
super(new BorderLayout());
if(bgColorArray != null)
this.bgColorArray = bgColorArray;
this.tmz = tmz;
setUIID("Calendar");
Container upper = new Container(new FlowLayout(Component.CENTER));
month = new ComboBox();
year = new ComboBox();
mv = new MonthView(time);
Vector months = new Vector();
for (int i = 0; i < MONTHS.length; i++) {
months.addElement("" + getLocalizedMonth(i));
}
ListModel monthsModel = new DefaultListModel(months);
int selected = months.indexOf(getLocalizedMonth(mv.getMonth()));
month.setModel(monthsModel);
month.setSelectedIndex(selected);
month.addActionListener(mv);
if(listCellRenderer != null){
month.setListCellRenderer(listCellRenderer);
year.setListCellRenderer(listCellRenderer);
}
month.getSelectedStyle().setBgColor(bgColorArray[1]);
month.getUnselectedStyle().setBgColor(bgColorArray[0]);
month.getPressedStyle().setBgColor(bgColorArray[2]);
year.getSelectedStyle().setBgColor(bgColorArray[1]);
year.getUnselectedStyle().setBgColor(bgColorArray[0]);
year.getPressedStyle().setBgColor(bgColorArray[2]);
/* month.getSelectedStyle().setBgColor(0xff00000);
year.getSelectedStyle().setBgColor(0xff00000);
ComboListCellRenderer comboListCellRenderer = new ComboListCellRenderer();
*/
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.setTime(new java.util.Date(time));
month.getStyle().setBgTransparency(0);
int y = cal.get(java.util.Calendar.YEAR);
Vector years = new Vector();
for (int i = 2100; i > 1900; i--) {
years.addElement("" + i);
}
ListModel yearModel = new DefaultListModel(years);
selected = years.indexOf("" + y);
year.setModel(yearModel);
year.setSelectedIndex(selected);
year.getStyle().setBgTransparency(0);
year.addActionListener(mv);
Container cnt = new Container(new BoxLayout(BoxLayout.X_AXIS));
cnt.setRTL(false);
Container dateCnt = new Container(new BoxLayout(BoxLayout.X_AXIS));
dateCnt.setUIID("CalendarDate");
dateCnt.addComponent(month);
dateCnt.addComponent(year);
cnt.addComponent(dateCnt);
upper.addComponent(cnt);
addComponent(BorderLayout.NORTH, upper);
addComponent(BorderLayout.CENTER, mv);
}
/**
* Returns the time for the current calendar.
*
* #return the time for the current calendar.
*/
public long getSelectedDay() {
return mv.getSelectedDay();
}
private String getLocalizedMonth(int i) {
Hashtable t = UIManager.getInstance().getResourceBundle();
String text = MONTHS[i];
if (t != null) {
Object o = t.get("Calendar." + text);
if (o != null) {
text = (String) o;
}
}
return text;
}
void componentChanged() {
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.set(java.util.Calendar.YEAR, mv.getYear());
cal.set(java.util.Calendar.MONTH, mv.getMonth());
cal.set(java.util.Calendar.DAY_OF_MONTH, mv.getDayOfMonth());
month.getParent().revalidate();
}
/**
* Return the date object matching the current selection
*
* #return the date object matching the current selection
*/
public Date getDate() {
return new Date(mv.getSelectedDay());
}
/**
* Sets the current date in the view and the selected date to be the same.
*
* #param d new date
*/
public void setDate(Date d) {
mv.setSelectedDay(d.getTime());
mv.setCurrentDay(mv.currentDay, true);
componentChanged();
}
/**
* This method sets the Calendar selected day
* #param d the selected day
*/
public void setSelectedDate(Date d){
mv.setSelectedDay(d.getTime());
}
/**
* Sets the Calendar view on the given date, only the the month and year
* are being considered.
*
* #param d the date to set the calendar view on.
*/
public void setCurrentDate(Date d){
mv.setCurrentDay(d.getTime(), true);
componentChanged();
}
/**
* Sets the Calendar timezone, if not specified Calendar will use the
* default timezone
* #param tmz the timezone
*/
public void setTimeZone(TimeZone tmz){
this.tmz = tmz;
}
/**
* Gets the Calendar timezone
*
* #return Calendar TimeZone
*/
public TimeZone getTimeZone(){
return tmz;
}
/**
* Sets the selected style of the month view component within the calendar
*
* #param s style for the month view
*/
public void setMonthViewSelectedStyle(Style s) {
mv.setSelectedStyle(s);
}
/**
* Sets the un selected style of the month view component within the calendar
*
* #param s style for the month view
*/
public void setMonthViewUnSelectedStyle(Style s) {
mv.setUnselectedStyle(s);
}
/**
* Gets the selected style of the month view component within the calendar
*
* #return the style of the month view
*/
public Style getMonthViewSelectedStyle() {
return mv.getSelectedStyle();
}
/**
* Gets the un selected style of the month view component within the calendar
*
* #return the style of the month view
*/
public Style getMonthViewUnSelectedStyle() {
return mv.getUnselectedStyle();
}
/**
* Fires when a change is made to the month view of this component
*
* #param l listener to add
*/
public void addActionListener(ActionListener l) {
mv.addActionListener(l);
}
/**
* Fires when a change is made to the month view of this component
*
* #param l listener to remove
*/
public void removeActionListener(ActionListener l) {
mv.removeActionListener(l);
}
/**
* Allows tracking selection changes in the calendar in real time
*
* #param l listener to add
*/
public void addDataChangeListener(DataChangedListener l) {
mv.addDataChangeListener(l);
}
/**
* Allows tracking selection changes in the calendar in real time
*
* #param l listener to remove
*/
public void removeDataChangeListener(DataChangedListener l) {
mv.removeDataChangeListener(l);
}
/**
* This flag determines if selected date can be changed by selecting an
* alternative date
*
* #param changesSelectedDateEnabled if true pressing on a date will cause
* the selected date to be changed to the pressed one
*/
public void setChangesSelectedDateEnabled(boolean changesSelectedDateEnabled) {
this.changesSelectedDateEnabled = changesSelectedDateEnabled;
}
/**
* This flag determines if selected date can be changed by selecting an
* alternative date
*
* #return true if enabled
*/
public boolean isChangesSelectedDateEnabled() {
return changesSelectedDateEnabled;
}
/**
* This method creates the Day Button Component for the Month View
*
* #return a Button that corresponds to the Days Components
*/
protected Button createDay() {
Button day = new Button();
day.setAlignment(CENTER);
day.setUIID("CalendarDay");
day.setEndsWith3Points(false);
day.setTickerEnabled(false);
return day;
}
/**
* This method creates the Day title Component for the Month View
*
* #param day the relevant day values are 0-6 where 0 is sunday.
* #return a Label that corresponds to the relevant Day
*/
protected Label createDayTitle(int day) {
String value = UIManager.getInstance().localize("Calendar." + DAYS[day], LABELS[day]);
Label dayh = new Label(value, "CalendarTitle");
dayh.setEndsWith3Points(false);
dayh.setTickerEnabled(false);
return dayh;
}
/**
* This method updates the Button day.
*
* #param dayButton the button to be updated
* #param day the new button day
*/
protected void updateButtonDayDate(Button dayButton, int currentMonth, int day) {
dayButton.setText("" + day);
}
class MonthView extends Container implements ActionListener{
private long currentDay;
private Button[] buttons = new Button[42];
private Button selected;
private long selectedDay = -1;
public MonthView(long time) {
super(new GridLayout(7, 7));
setUIID("MonthView");
for (int iter = 0; iter < DAYS.length; iter++) {
addComponent(createDayTitle(iter));
}
for (int iter = 0; iter < buttons.length; iter++) {
buttons[iter] = createDay();
addComponent(buttons[iter]);
if (iter <= 7) {
buttons[iter].setNextFocusUp(year);
}
buttons[iter].addActionListener(this);
}
setCurrentDay(time);
}
public void setCurrentDay(long day){
setCurrentDay(day, false);
}
private void setCurrentDay(long day, boolean force) {
repaint();
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.setTime(new Date(currentDay));
cal.set(java.util.Calendar.HOUR, 1);
cal.set(java.util.Calendar.HOUR_OF_DAY, 1);
cal.set(java.util.Calendar.MINUTE, 0);
cal.set(java.util.Calendar.SECOND, 0);
cal.set(java.util.Calendar.MILLISECOND, 0);
int yearOld = cal.get(java.util.Calendar.YEAR);
int monthOld = cal.get(java.util.Calendar.MONTH);
int dayOld = cal.get(java.util.Calendar.DAY_OF_MONTH);
cal.setTime(new Date(day));
cal.set(java.util.Calendar.HOUR, 1);
cal.set(java.util.Calendar.HOUR_OF_DAY, 1);
cal.set(java.util.Calendar.MINUTE, 0);
cal.set(java.util.Calendar.SECOND, 0);
cal.set(java.util.Calendar.MILLISECOND, 0);
int yearNew = cal.get(java.util.Calendar.YEAR);
int monthNew = cal.get(java.util.Calendar.MONTH);
int dayNew = cal.get(java.util.Calendar.DAY_OF_MONTH);
year.setSelectedItem("" + yearNew);
month.setSelectedIndex(monthNew);
if (yearNew != yearOld || monthNew != monthOld || dayNew != dayOld || force) {
currentDay = cal.getTime().getTime();
if(selectedDay == -1){
selectedDay = currentDay;
}
int month = cal.get(java.util.Calendar.MONTH);
cal.set(java.util.Calendar.DAY_OF_MONTH, 1);
long startDate = cal.getTime().getTime();
int dow = cal.get(java.util.Calendar.DAY_OF_WEEK);
cal.setTime(new Date(cal.getTime().getTime() - DAY));
cal.set(java.util.Calendar.HOUR, 1);
cal.set(java.util.Calendar.HOUR_OF_DAY, 1);
cal.set(java.util.Calendar.MINUTE, 0);
cal.set(java.util.Calendar.SECOND, 0);
cal.set(java.util.Calendar.MILLISECOND, 0);
int lastDay = cal.get(java.util.Calendar.DAY_OF_MONTH);
int i = 0;
if(dow > java.util.Calendar.SUNDAY){
//last day of previous month
while (dow > java.util.Calendar.SUNDAY) {
cal.setTime(new Date(cal.getTime().getTime() - DAY));
dow = cal.get(java.util.Calendar.DAY_OF_WEEK);
}
int previousMonthSunday = cal.get(java.util.Calendar.DAY_OF_MONTH);
for (; i <= lastDay - previousMonthSunday; i++) {
buttons[i].setUIID("CalendarDay");
buttons[i].setEnabled(false);
buttons[i].setText("" + (previousMonthSunday + i));
}
}
//last day of current month
cal.set(java.util.Calendar.MONTH, (month + 1) % 12);
cal.set(java.util.Calendar.DAY_OF_MONTH, 1);
cal.setTime(new Date(cal.getTime().getTime() - DAY));
lastDay = cal.get(java.util.Calendar.DAY_OF_MONTH);
int j = i;
for (; j < buttons.length && (j - i + 1) <= lastDay; j++) {
buttons[j].setEnabled(true);
dates[j] = startDate;
if(dates[j] == selectedDay){
buttons[j].setUIID("CalendarSelectedDay");
selected = buttons[j];
}else{
buttons[j].setUIID("CalendarDay");
}
buttons[j].getSelectedStyle().setBgColor(bgColorArray[1]);
buttons[j].getUnselectedStyle().setBgColor(bgColorArray[0]);
buttons[j].getPressedStyle().setBgColor(bgColorArray[2]);
buttons[j].getStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL));
buttons[j].getSelectedStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL));
buttons[j].getUnselectedStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL));
buttons[j].getPressedStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL));
updateButtonDayDate(buttons[j], month, j - i + 1);
startDate += DAY;
}
int d = 1;
for (; j < buttons.length; j++) {
buttons[j].setUIID("CalendarDay");
buttons[j].setEnabled(false);
buttons[j].setText("" + d++);
buttons[j].getStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL));
buttons[j].getSelectedStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL));
buttons[j].getUnselectedStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_SMALL));
buttons[j].getPressedStyle().setFont(Font.createSystemFont(
Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL));
}
}
}
public int getDayOfMonth() {
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.setTime(new Date(currentDay));
return cal.get(java.util.Calendar.DAY_OF_MONTH);
}
public int getMonth() {
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.setTime(new Date(currentDay));
return cal.get(java.util.Calendar.MONTH);
}
public void incrementMonth() {
int month = getMonth();
month++;
int year = getYear();
if (month > java.util.Calendar.DECEMBER) {
month = java.util.Calendar.JANUARY;
year++;
}
setMonth(year, month);
}
private long getSelectedDay() {
return selectedDay;
}
public void setSelectedDay(long selectedDay){
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.setTime(new Date(selectedDay));
cal.set(java.util.Calendar.HOUR, 1);
cal.set(java.util.Calendar.HOUR_OF_DAY, 1);
cal.set(java.util.Calendar.MINUTE, 0);
cal.set(java.util.Calendar.SECOND, 0);
cal.set(java.util.Calendar.MILLISECOND, 0);
this.selectedDay = cal.getTime().getTime();
}
private void setMonth(int year, int month) {
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.setTimeZone(TimeZone.getDefault());
cal.set(java.util.Calendar.MONTH, month);
cal.set(java.util.Calendar.DAY_OF_MONTH, 1);
cal.set(java.util.Calendar.YEAR, year);
Date date = cal.getTime();
long d = date.getTime();
// if this is past the last day of the month (e.g. going from January 31st
// to Febuary) we need to decrement the day until the month is correct
while (cal.get(java.util.Calendar.MONTH) != month) {
d -= DAY;
cal.setTime(new Date(d));
}
setCurrentDay(d);
}
public void decrementMonth() {
int month = getMonth();
month--;
int year = getYear();
if (month < java.util.Calendar.JANUARY) {
month = java.util.Calendar.DECEMBER;
year--;
}
setMonth(year, month);
}
public int getYear() {
java.util.Calendar cal = java.util.Calendar.getInstance(tmz);
cal.setTime(new Date(currentDay));
return cal.get(java.util.Calendar.YEAR);
}
public void addActionListener(ActionListener l) {
dispatcher.addListener(l);
}
public void removeActionListener(ActionListener l) {
dispatcher.removeListener(l);
}
/**
* Allows tracking selection changes in the calendar in real time
*
* #param l listener to add
*/
public void addDataChangeListener(DataChangedListener l) {
dataChangeListeners.addListener(l);
}
/**
* Allows tracking selection changes in the calendar in real time
*
* #param l listener to remove
*/
public void removeDataChangeListener(DataChangedListener l) {
dataChangeListeners.removeListener(l);
}
protected void fireActionEvent() {
componentChanged();
super.fireActionEvent();
dispatcher.fireActionEvent(new ActionEvent(Calendar.this));
}
public void actionPerformed(ActionEvent evt) {
Object src = evt.getSource();
if(src instanceof ComboBox){
setMonth(Integer.parseInt((String)year.getSelectedItem()),
month.getSelectedIndex());
componentChanged();
return;
}
if(changesSelectedDateEnabled){
System.out.println("ttttttttttttttttttttttttttttt");
for (int iter = 0; iter < buttons.length; iter++) {
if (src == buttons[iter]) {
selected.setUIID("CalendarDay");
buttons[iter].setUIID("CalendarSelectedDay");
selectedDay = dates[iter];
selected.getSelectedStyle().setBgColor(bgColorArray[1]);
selected.getUnselectedStyle().setBgColor(bgColorArray[0]);
selected.getPressedStyle().setBgColor(bgColorArray[2]);
selected = buttons[iter];
selected.getSelectedStyle().setBgColor(bgColorArray[1]);
selected.getUnselectedStyle().setBgColor(bgColorArray[0]);
selected.getPressedStyle().setBgColor(bgColorArray[2]);
fireActionEvent();
if (!getComponentForm().isSingleFocusMode()) {
setHandlesInput(false);
}
return;
}
}
}
}
}
}
Related
I have a Java code producing bunch of workbooks and would like to copy all of them into one workbook.
The original answer to this question was posted on:
How to copy a sheet between Excel workbooks in Java
My original idea was to use the cloneSheet method of XSSFWorkbook to do the trick, I was successful in copying the Relations and Drawings however failed to copy the data itself. Why? Because the XSSFSheet's method write and read are protected, I did come up with my own version of XSSFSheet by extending it and making the two methods write and read public, but that would mean I will have to copy and edit every source file using XSSFSheet replacing it with my version of XSSFSheet that would mean too much coding I didn't have that kind of time for the project.
The original answer to this question was posted on:
How to copy a sheet between Excel workbooks in Java
However, my project requirement were different, so I improvised the answer.
I added a method called combine which accepts target workbook, and source workbook.
The methods loops through all the worksheets in the source workbook and adds them to target workbook. The original answer lacked copying of relations and drawing.
Also the workbooks I was to copy from lacked Footer, neede Gridlines to be turned off, and fit to page. So here is my solution
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLDocumentPart.RelationPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.POILogger;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
import com.hcsc.eas.framework.services.logging.Logger;
public class XSSFWorkbookHelper {
static protected Logger logger = Logger.getLogger(XSSFWorkbookHelper.class.getName());
public static void combine(XSSFWorkbook tgt, XSSFWorkbook src) throws InvalidFormatException {
// begin sheets loop
boolean first=true;
String firstSheetName = null;
XSSFSheet tgtSheet = null;
for (int i = 0; i < src.getNumberOfSheets(); i++) {
XSSFSheet srcSheet = src.getSheetAt(i);
String sheetName = srcSheet.getSheetName().replaceAll("_", "-");
if (first) {
firstSheetName = srcSheet.getSheetName();
if (sheetName.equals(firstSheetName)) sheetName = sheetName + "_";
first = false;
}
tgtSheet = tgt.createSheet(sheetName);
copyRelations(tgtSheet, srcSheet);
copySheets(tgtSheet, srcSheet);
} // end sheets loop
tgtSheet = tgt.getSheet(firstSheetName);
if(tgtSheet != null) {
tgt.removeSheetAt(tgt.getSheetIndex(tgtSheet));
}
}
private static void copyRelations(XSSFSheet tgtSheet,XSSFSheet srcSheet) {
// copy sheet's relations
List<RelationPart> rels = srcSheet.getRelationParts();
// if the sheet being cloned has a drawing then rememebr it and re-create it too
XSSFDrawing dg = null;
for(RelationPart rp : rels) {
POIXMLDocumentPart r = rp.getDocumentPart();
// do not copy the drawing relationship, it will be re-created
if(r instanceof XSSFDrawing) {
dg = (XSSFDrawing)r;
continue;
}
addRelation(rp, tgtSheet);
}
try {
for(PackageRelationship pr : srcSheet.getPackagePart().getRelationships()) {
if (pr.getTargetMode() == TargetMode.EXTERNAL) {
tgtSheet.getPackagePart().addExternalRelationship
(pr.getTargetURI().toASCIIString(), pr.getRelationshipType(), pr.getId());
}
}
} catch (InvalidFormatException e) {
throw new POIXMLException("Failed to clone sheet", e);
}
CTWorksheet ct = tgtSheet.getCTWorksheet();
if(ct.isSetLegacyDrawing()) {
logger.warn(POILogger.WARN + "Cloning sheets with comments is not yet supported.");
ct.unsetLegacyDrawing();
}
if (ct.isSetPageSetup()) {
logger.warn(POILogger.WARN + "Cloning sheets with page setup is not yet supported.");
ct.unsetPageSetup();
}
tgtSheet.setSelected(false);
// clone the sheet drawing alongs with its relationships
if (dg != null) {
if(ct.isSetDrawing()) {
// unset the existing reference to the drawing,
// so that subsequent call of tgtSheet.createDrawingPatriarch() will create a new one
ct.unsetDrawing();
}
XSSFDrawing clonedDg = tgtSheet.createDrawingPatriarch();
// copy drawing contents
clonedDg.getCTDrawing().set(dg.getCTDrawing());
clonedDg = tgtSheet.createDrawingPatriarch();
// Clone drawing relations
List<RelationPart> srcRels = srcSheet.createDrawingPatriarch().getRelationParts();
for (RelationPart rp : srcRels) {
addRelation(rp, clonedDg);
}
}
}
private static void addRelation(RelationPart rp, POIXMLDocumentPart target) {
PackageRelationship rel = rp.getRelationship();
if (rel.getTargetMode() == TargetMode.EXTERNAL) {
target.getPackagePart().addRelationship(rel.getTargetURI(), rel.getTargetMode(), rel.getRelationshipType(),
rel.getId());
} else {
XSSFRelation xssfRel = XSSFRelation.getInstance(rel.getRelationshipType());
if (xssfRel == null) {
// Don't copy all relations blindly, but only the ones we know
// about
throw new POIXMLException(
"Can't clone sheet - unknown relation type found: " + rel.getRelationshipType());
}
target.addRelation(rel.getId(), xssfRel, rp.getDocumentPart());
}
}
/**
* #param newSheet
* the sheet to create from the copy.
* #param sheet
* the sheet to copy.
*/
public static void copySheets(XSSFSheet newSheet, XSSFSheet sheet) {
copySheets(newSheet, sheet, true);
}
/**
* #param newSheet
* the sheet to create from the copy.
* #param sheet
* the sheet to copy.
* #param copyStyle
* true copy the style.
*/
public static void copySheets(XSSFSheet newSheet, XSSFSheet sheet, boolean copyStyle) {
setupSheet(newSheet);
int maxColumnNum = 0;
Map<Integer, CellStyle> styleMap = (copyStyle) ? new HashMap<Integer, CellStyle>() : null;
for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
XSSFRow srcRow = sheet.getRow(i);
XSSFRow destRow = newSheet.createRow(i);
if (srcRow != null) {
copyRow(sheet, newSheet, srcRow, destRow, styleMap);
if (srcRow.getLastCellNum() > maxColumnNum) {
maxColumnNum = srcRow.getLastCellNum();
}
}
}
for (int i = 0; i <= maxColumnNum; i++) {
newSheet.setColumnWidth(i, sheet.getColumnWidth(i));
}
}
public static void setupSheet(XSSFSheet newSheet) {
newSheet.getFooter().setCenter("&7 _x0000_ A Division of Health Care Service Corporation, a Mutual Legal Reserve Company, \n _x0000_ an Independent Licensee of the Blue Cross and Blue Shield Association");
newSheet.setDisplayGridlines(false);
newSheet.setMargin(Sheet.RightMargin, 0.5 /* inches */ );
newSheet.setMargin(Sheet.LeftMargin, 0.5 /* inches */ );
newSheet.setMargin(Sheet.TopMargin, 0.5 /* inches */ );
newSheet.setMargin(Sheet.BottomMargin, 0.5 /* inches */ );
newSheet.setFitToPage(true);
}
/**
* #param srcSheet
* the sheet to copy.
* #param destSheet
* the sheet to create.
* #param srcRow
* the row to copy.
* #param destRow
* the row to create.
* #param styleMap
* -
*/
public static void copyRow(XSSFSheet srcSheet, XSSFSheet destSheet, XSSFRow srcRow, XSSFRow destRow,
Map<Integer, CellStyle> styleMap) {
try {
// manage a list of merged zone in order to not insert two times a
// merged zone
Set<CellRangeAddressWrapper> mergedRegions = new TreeSet<CellRangeAddressWrapper>();
destRow.setHeight(srcRow.getHeight());
// reckoning delta rows
int deltaRows = destRow.getRowNum() - srcRow.getRowNum();
// pour chaque row
for (int j = srcRow.getFirstCellNum(); j <= srcRow.getLastCellNum(); j++) {
XSSFCell oldCell = srcRow.getCell(j); // ancienne cell
XSSFCell newCell = destRow.getCell(j); // new cell
if (oldCell != null) {
if (newCell == null) {
newCell = destRow.createCell(j);
}
// copy chaque cell
copyCell(oldCell, newCell, styleMap);
// copy les informations de fusion entre les cellules
// System.out.println("row num: " + srcRow.getRowNum() + " ,
// col: " + (short)oldCell.getColumnIndex());
CellRangeAddress mergedRegion = getMergedRegion(srcSheet, srcRow.getRowNum(),
(short) oldCell.getColumnIndex());
if (mergedRegion != null) {
// System.out.println("Selected merged region: " +
// mergedRegion.toString());
CellRangeAddress newMergedRegion = new CellRangeAddress(mergedRegion.getFirstRow() + deltaRows,
mergedRegion.getLastRow() + deltaRows, mergedRegion.getFirstColumn(),
mergedRegion.getLastColumn());
// System.out.println("New merged region: " +
// newMergedRegion.toString());
CellRangeAddressWrapper wrapper = new CellRangeAddressWrapper(newMergedRegion);
if (isNewMergedRegion(wrapper, mergedRegions)) {
mergedRegions.add(wrapper);
destSheet.addMergedRegion(wrapper.range);
}
}
}
}
} catch (Exception e) {
//e.printStackTrace();
logger.warn(POILogger.WARN + "merge area failure, happens when a merge area overlaps.");
}
}
/**
* #param oldCell
* #param newCell
* #param styleMap
*/
public static void copyCell(XSSFCell oldCell, XSSFCell newCell, Map<Integer, CellStyle> styleMap) {
if (styleMap != null) {
if (oldCell.getSheet().getWorkbook() == newCell.getSheet().getWorkbook()) {
newCell.setCellStyle(oldCell.getCellStyle());
} else {
int stHashCode = oldCell.getCellStyle().hashCode();
CellStyle newCellStyle = styleMap.get(stHashCode);
if (newCellStyle == null) {
newCellStyle = newCell.getSheet().getWorkbook().createCellStyle();
newCellStyle.cloneStyleFrom(oldCell.getCellStyle());
styleMap.put(stHashCode, newCellStyle);
}
newCell.setCellStyle(newCellStyle);
}
}
switch (oldCell.getCellType()) {
case XSSFCell.CELL_TYPE_STRING:
newCell.setCellValue(oldCell.getStringCellValue());
break;
case XSSFCell.CELL_TYPE_NUMERIC:
newCell.setCellValue(oldCell.getNumericCellValue());
break;
case XSSFCell.CELL_TYPE_BLANK:
newCell.setCellType(XSSFCell.CELL_TYPE_BLANK);
break;
case XSSFCell.CELL_TYPE_BOOLEAN:
newCell.setCellValue(oldCell.getBooleanCellValue());
break;
case XSSFCell.CELL_TYPE_ERROR:
newCell.setCellErrorValue(oldCell.getErrorCellValue());
break;
case XSSFCell.CELL_TYPE_FORMULA:
newCell.setCellFormula(oldCell.getCellFormula());
break;
default:
break;
}
}
/**
* Retrieves cell merge information in the source sheet
* to apply them to the destination sheet ... Get all zones
* merged in the source sheet and look for each of them if she
* find in the current row that we are dealing. If yes, return the object
* CellRangeAddress.
*
* #param sheet
* the sheet containing the data.
* #param rowNum
* the num of the row to copy.
* #param cellNum
* the num of the cell to copy.
* #return the CellRangeAddress created.
*/
public static CellRangeAddress getMergedRegion(XSSFSheet sheet, int rowNum, short cellNum) {
for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
CellRangeAddress merged = sheet.getMergedRegion(i);
if (merged.isInRange(rowNum, cellNum)) {
return merged;
}
}
return null;
}
/**
* Check that the merged region has been created in the destination sheet.
*
* #param newMergedRegion
* the merged region to copy or not in the destination sheet.
* #param mergedRegions
* the list containing all the merged region.
* #return true if the merged region is already in the list or not.
*/
private static boolean isNewMergedRegion(CellRangeAddressWrapper newMergedRegion,
Set<CellRangeAddressWrapper> mergedRegions) {
return !mergedRegions.contains(newMergedRegion);
}
}
class CellRangeAddressWrapper implements Comparable<CellRangeAddressWrapper> {
public CellRangeAddress range;
/**
* #param theRange
* the CellRangeAddress object to wrap.
*/
public CellRangeAddressWrapper(CellRangeAddress theRange) {
this.range = theRange;
}
/**
* #param o
* the object to compare.
* #return -1 the current instance is prior to the object in parameter, 0:
* equal, 1: after...
*/
public int compareTo(CellRangeAddressWrapper o) {
if (range.getFirstColumn() < o.range.getFirstColumn() && range.getFirstRow() < o.range.getFirstRow()) {
return -1;
} else if (range.getFirstColumn() == o.range.getFirstColumn() && range.getFirstRow() == o.range.getFirstRow()) {
return 0;
} else {
return 1;
}
}
}
I would like to implement pan on my plot without zooming, by setting a origin value smaller than may lower boundaries on domain axis.
My code so far :
this.panZoom = PanZoom.attach(plot);
this.panZoom.setPan(PanZoom.Pan.HORIZONTAL);
this.panZoom.setZoom(null);
this.panZoom.setDelegate(this);
this.plot.setUserDomainOrigin(0);
this.plot.setDomainBoundaries(5, 20);
this.plot.setDomainStep(StepMode.INCREMENT_BY_VAL, 2);
By doing this, my plot starts well at 5 but won't move when I scroll..
I'm migrating my project to androidPlot 1.2.2 and it was working with 0.9.7
Thanks!
This appears to be a limitation in Androidplot 1.2.2 (Will be fixed in the next release - tracked by this bug report.)
For now you can add this implementation of PanZoom in your project:
package com.androidplot.xy;
import android.graphics.RectF;
import android.graphics.PointF;
import android.util.*;
import android.view.*;
import com.androidplot.*;
import java.util.*;
/**
* Enables basic pan/zoom touch behavior for an {#link XYPlot}.
* By default boundaries set on the associated plot will define the scroll/zoom extents as well as
* initial state of the plot's visible area. If you wish to specify a scrollable / zoomable area
* that is greater than or less than the plot's boundaries, use
* {#link #setDomainBoundaries(Number, Number)} and
* {#link #setRangeBoundaries(Number, Number)}
* TODO: zoom using dynamic center point
* TODO: stretch both mode
*/
public class PanZoom implements View.OnTouchListener {
protected static final float MIN_DIST_2_FING = 5f;
protected static final int FIRST_FINGER = 0;
protected static final int SECOND_FINGER = 1;
private XYPlot plot;
private Pan pan;
private Zoom zoom;
private boolean isEnabled = true;
private DragState dragState = DragState.NONE;
RectRegion limits = new RectRegion();
RectRegion previousLimits = new RectRegion();
private PointF firstFingerPos;
// rectangle created by the space between two fingers
private RectF fingersRect;
private View.OnTouchListener delegate;
// Definition of the touch states
protected enum DragState {
NONE,
ONE_FINGER,
TWO_FINGERS
}
public enum Pan {
NONE,
HORIZONTAL,
VERTICAL,
BOTH
}
public enum Zoom {
/**
* Comletely disable panning
*/
NONE,
/**
* Zoom on the horizontal axis only
*/
STRETCH_HORIZONTAL,
/**
* Zoom on the vertical axis only
*/
STRETCH_VERTICAL,
/**
* Zoom on the vertical axis by the vertical distance between each finger, while zooming
* on the horizontal axis by the horizantal distance between each finger.
*/
STRETCH_BOTH,
/**
* Zoom each axis by the same amount, specifically the total distance between each finger.
*/
SCALE
}
protected PanZoom(XYPlot plot, Pan pan, Zoom zoom) {
this.plot = plot;
this.pan = pan;
this.zoom = zoom;
}
/**
* Convenience method for enabling pan/zoom behavior on an instance of {#link XYPlot}, using
* a default behavior of {#link Pan#BOTH} and {#link Zoom#SCALE}.
* Use {#link PanZoom#attach(XYPlot, Pan, Zoom)} for finer grain control of this behavior.
* #param plot
* #return
*/
public static PanZoom attach(XYPlot plot) {
return attach(plot, Pan.BOTH, Zoom.SCALE);
}
public static PanZoom attach(XYPlot plot, Pan pan, Zoom zoom) {
PanZoom pz = new PanZoom(plot, pan, zoom);
plot.setOnTouchListener(pz);
return pz;
}
public boolean isEnabled() {
return isEnabled;
}
public void setEnabled(boolean enabled) {
isEnabled = enabled;
}
/**
* Set the boundaries by which domain pan/zoom calculations will abide; differs from an {#link XYPlot}'s boundaries
* in that those boundaries define the plot's starting state.
* #param lowerBoundary
* #param upperBoundary
*/
public void setDomainBoundaries(final Number lowerBoundary, final Number upperBoundary) {
limits.setMinX(lowerBoundary);
limits.setMaxX(upperBoundary);
}
/**
* Sets the range boundaries by which pan/zoom calculations will abide.
* #param lowerBoundary
* #param upperBoundary
*/
public void setRangeBoundaries(final Number lowerBoundary, final Number upperBoundary) {
limits.setMinY(lowerBoundary);
limits.setMaxY(upperBoundary);
}
#Override
public boolean onTouch(final View view, final MotionEvent event) {
boolean isConsumed = false;
if (delegate != null) {
isConsumed = delegate.onTouch(view, event);
}
if (isEnabled() && !isConsumed) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: // start gesture
firstFingerPos = new PointF(event.getX(), event.getY());
Log.d("PanZoom", "ONE_FINGER set");
dragState = DragState.ONE_FINGER;
break;
case MotionEvent.ACTION_POINTER_DOWN: // second finger
{
fingersRect = fingerDistance(event);
Log.d("PanZoom", "ACTION_POINTER_DOWN - distance: " + fingersRect.width());
// the distance check is done to avoid false alarms
if (fingersRect.width() > MIN_DIST_2_FING || fingersRect.width() < -MIN_DIST_2_FING) {
Log.d("PanZoom", "TWO_FINGERS set");
dragState = DragState.TWO_FINGERS;
}
break;
}
case MotionEvent.ACTION_POINTER_UP: // end zoom
dragState = DragState.NONE;
break;
case MotionEvent.ACTION_MOVE:
if (dragState == DragState.ONE_FINGER) {
Log.d("PanZoom", "ACTION_MOVE - one finger");
pan(event);
} else if (dragState == DragState.TWO_FINGERS) {
Log.d("PanZoom", "ACTION_MOVE - two fingers");
zoom(event);
}
break;
}
}
// we're forced to consume the event here as not consuming it will prevent future calls:
return true;
}
/**
* Calculates the distance between two finger motion events.
* #param firstFingerX
* #param firstFingerY
* #param secondFingerX
* #param secondFingerY
* #return
*/
protected RectF fingerDistance(float firstFingerX, float firstFingerY, float secondFingerX, float secondFingerY) {
final float left = firstFingerX > secondFingerX ? secondFingerX : firstFingerX;
final float right = firstFingerX > secondFingerX ? firstFingerX : secondFingerX;
final float top = firstFingerY > secondFingerY ? secondFingerY : firstFingerY;
final float bottom = firstFingerY > secondFingerY ? firstFingerY : secondFingerY;
return new RectF(left, top, right, bottom);
}
/**
* Calculates the distance between two finger motion events.
* #param evt
* #return
*/
protected RectF fingerDistance(final MotionEvent evt) {
return fingerDistance(
evt.getX(FIRST_FINGER),
evt.getY(FIRST_FINGER),
evt.getX(SECOND_FINGER),
evt.getY(SECOND_FINGER));
}
protected Number getMinXLimit() {
if (limits.getMinX() == null) {
limits.setMinX(plot.getBounds().getMinX().floatValue());
previousLimits.setMinX(limits.getMinX());
}
return limits.getMinX();
}
protected Number getMaxXLimit() {
if (limits.getMaxX() == null) {
limits.setMaxX(plot.getBounds().getMaxX().floatValue());
previousLimits.setMaxX(limits.getMaxX());
}
return limits.getMaxX();
}
protected Number getMinYLimit() {
if (limits.getMinY() == null) {
limits.setMinY(plot.getBounds().getMinY().floatValue());
previousLimits.setMinY(limits.getMinY());
}
return limits.getMinY();
}
protected Number getMaxYLimit() {
if (limits.getMaxY() == null) {
limits.setMaxY(plot.getBounds().getMaxY().floatValue());
previousLimits.setMaxY(limits.getMaxY());
}
return limits.getMaxY();
}
protected Number getLastMinX() {
if (previousLimits.getMinX() == null) {
previousLimits.setMinX(plot.getBounds().getMinX().floatValue());
}
return previousLimits.getMinX();
}
protected Number getLastMaxX() {
if (previousLimits.getMaxX() == null) {
previousLimits.setMaxX(plot.getBounds().getMaxX().floatValue());
}
return previousLimits.getMaxX();
}
protected Number getLastMinY() {
if (previousLimits.getMinY() == null) {
previousLimits.setMinY(plot.getBounds().getMinY().floatValue());
}
return previousLimits.getMinY();
}
private Number getLastMaxY() {
if (previousLimits.getMaxY() == null) {
previousLimits.setMaxY(plot.getBounds().getMaxY().floatValue());
}
return previousLimits.getMaxY();
}
protected void pan(final MotionEvent motionEvent) {
if (pan == Pan.NONE) {
return;
}
final PointF oldFirstFinger = firstFingerPos; //save old position of finger
firstFingerPos = new PointF(motionEvent.getX(), motionEvent.getY()); //update finger position
Region newBounds = new Region();
if (EnumSet.of(Pan.HORIZONTAL, Pan.BOTH).contains(pan)) {
calculatePan(oldFirstFinger, newBounds, true);
plot.setDomainBoundaries(newBounds.getMin(), newBounds.getMax(), BoundaryMode.FIXED);
previousLimits.setMinX(newBounds.getMin());
previousLimits.setMaxX(newBounds.getMax());
}
if (EnumSet.of(Pan.VERTICAL, Pan.BOTH).contains(pan)) {
calculatePan(oldFirstFinger, newBounds, false);
plot.setRangeBoundaries(newBounds.getMin(), newBounds.getMax(), BoundaryMode.FIXED);
previousLimits.setMinY(newBounds.getMin());
previousLimits.setMaxY(newBounds.getMax());
}
plot.redraw();
}
protected void calculatePan(final PointF oldFirstFinger, Region bounds, final boolean horizontal) {
final float offset;
// multiply the absolute finger movement for a factor.
// the factor is dependent on the calculated min and max
if (horizontal) {
bounds.setMin(getLastMinX());
bounds.setMax(getLastMaxX());
offset = (oldFirstFinger.x - firstFingerPos.x) *
((bounds.getMax().floatValue() - bounds.getMin().floatValue()) / plot.getWidth());
} else {
bounds.setMin(getLastMinY());
bounds.setMax(getLastMaxY());
offset = -(oldFirstFinger.y - firstFingerPos.y) *
((bounds.getMax().floatValue() - bounds.getMin().floatValue()) / plot.getHeight());
}
// move the calculated offset
bounds.setMin(bounds.getMin().floatValue() + offset);
bounds.setMax(bounds.getMax().floatValue() + offset);
//get the distance between max and min
final float diff = bounds.length().floatValue();
//check if we reached the limit of panning
if (horizontal) {
if (bounds.getMin().floatValue() < getMinXLimit().floatValue()) {
bounds.setMin(getMinXLimit());
bounds.setMax(bounds.getMin().floatValue() + diff);
}
if (bounds.getMax().floatValue() > getMaxXLimit().floatValue()) {
bounds.setMax(getMaxXLimit());
bounds.setMin(bounds.getMax().floatValue() - diff);
}
} else {
if (bounds.getMin().floatValue() < getMinYLimit().floatValue()) {
bounds.setMin(getMinYLimit());
bounds.setMax(bounds.getMin().floatValue() + diff);
}
if (bounds.getMax().floatValue() > getMaxYLimit().floatValue()) {
bounds.setMax(getMaxYLimit());
bounds.setMin(bounds.getMax().floatValue() - diff);
}
}
}
protected boolean isValidScale(float scale) {
if (Float.isInfinite(scale) || Float.isNaN(scale) || scale > -0.001 && scale < 0.001) {
return false;
}
return true;
}
protected void zoom(final MotionEvent motionEvent) {
if (zoom == Zoom.NONE) {
return;
}
final RectF oldFingersRect = fingersRect;
final RectF newFingersRect = fingerDistance(motionEvent);
fingersRect = newFingersRect;
RectF newRect = new RectF();
float scaleX = 1;
float scaleY = 1;
switch (zoom) {
case STRETCH_HORIZONTAL:
scaleX = oldFingersRect.width() / fingersRect.width();
if (!isValidScale(scaleX)) {
return;
}
break;
case STRETCH_VERTICAL:
scaleY = oldFingersRect.height() / fingersRect.height();
if (!isValidScale(scaleY)) {
return;
}
break;
case STRETCH_BOTH:
scaleX = oldFingersRect.width() / fingersRect.width();
scaleY = oldFingersRect.height() / fingersRect.height();
if (!isValidScale(scaleX) || !isValidScale(scaleY)) {
return;
}
break;
case SCALE:
float sc1 = (float) Math.hypot(oldFingersRect.height(), oldFingersRect.width());
float sc2 = (float) Math.hypot(fingersRect.height(), fingersRect.width());
float sc = sc1 / sc2;
scaleX = sc;
scaleY = sc;
if (!isValidScale(scaleX) || !isValidScale(scaleY)) {
return;
}
break;
}
if (EnumSet.of(
Zoom.STRETCH_HORIZONTAL,
Zoom.STRETCH_BOTH,
Zoom.SCALE).contains(zoom)) {
calculateZoom(newRect, scaleX, true);
plot.setDomainBoundaries(newRect.left, newRect.right, BoundaryMode.FIXED);
previousLimits.setMinX(newRect.left);
previousLimits.setMaxX(newRect.right);
}
if (EnumSet.of(
Zoom.STRETCH_VERTICAL,
Zoom.STRETCH_BOTH,
Zoom.SCALE).contains(zoom)) {
calculateZoom(newRect, scaleY, false);
plot.setRangeBoundaries(newRect.top, newRect.bottom, BoundaryMode.FIXED);
previousLimits.setMinY(newRect.top);
previousLimits.setMaxY(newRect.bottom);
}
plot.redraw();
}
protected void calculateZoom(RectF newRect, float scale, boolean isHorizontal) {
final float calcMax;
final float span;
if (isHorizontal) {
calcMax = getLastMaxX().floatValue();
span = calcMax - getLastMinX().floatValue();
} else {
calcMax = getLastMaxY().floatValue();
span = calcMax - getLastMinY().floatValue();
}
final float midPoint = calcMax - (span / 2.0f);
final float offset = span * scale / 2.0f;
if (isHorizontal) {
newRect.left = midPoint - offset;
newRect.right = midPoint + offset;
if (newRect.left < getMinXLimit().floatValue()) {
newRect.left = getMinXLimit().floatValue();
}
if (newRect.right > getMaxXLimit().floatValue()) {
newRect.right = getMaxXLimit().floatValue();
}
} else {
newRect.top = midPoint - offset;
newRect.bottom = midPoint + offset;
if (newRect.top < getMinYLimit().floatValue()) {
newRect.top = getMinYLimit().floatValue();
}
if (newRect.bottom > getMaxYLimit().floatValue()) {
newRect.bottom = getMaxYLimit().floatValue();
}
}
}
public Pan getPan() {
return pan;
}
public void setPan(Pan pan) {
this.pan = pan;
}
public Zoom getZoom() {
return zoom;
}
public void setZoom(Zoom zoom) {
this.zoom = zoom;
}
public View.OnTouchListener getDelegate() {
return delegate;
}
/**
* Set a delegate to receive onTouch calls before this class does. If the delegate wishes
* to consume the event, it should return true, otherwise it should return false. Returning
* false will not prevent future onTouch events from filtering through the delegate as it normally
* would when attaching directly to an instance of {#link View}.
* #param delegate
*/
public void setDelegate(View.OnTouchListener delegate) {
this.delegate = delegate;
}
public void reset() {
this.previousLimits = new RectRegion();
this.firstFingerPos = null;
this.fingersRect = null;
}
}
Don't forget to change the class name to avoid a collision with the existing PanZoom implementation.
If you want to constrain the starting window bounds to a sub-section of your data, set those boundaries on the XYPlot instance as normal. Then, configure the PanZoom instance with the absolute boundaries for panning and zooming, typically the min/max values of the series attached to your plot.
Also, if you need to change the visible window after your initial setup in onCreate (result of a user pressing a reset button, etc.) you'll also need to invoke PanZoom.reset() to wipe out it's internal state. This step may not be necessary in the official release implementation.
As from this very good post here
Logarithmic scale in Java FX 2
I have changed this class to get log scale on Y axis, and it works fine. The only problem I have is that there are very few horizontal grid lines and scale always start ranges from 0 or near zero.
Here is what I get
I would like to have tick values grid also in the min and max range of my data serie, in this case min = 19,35 max = 20,35; as of now all 10 horizontal grid lines are all plotted outside this range.
How to accomplish this?
Thanks all, here is my log code for Y axis
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.chart.ValueAxis;
//http://blog.dooapp.com/logarithmic-scale-strikes-back-in-javafx-20
public class LogarithmicAxis extends ValueAxis<Number> {
//Create our LogarithmicAxis class that extends ValueAxis<Number> and define two properties that will represent the log lower and upper bounds of our axis.
private final DoubleProperty logUpperBound = new SimpleDoubleProperty();
private final DoubleProperty logLowerBound = new SimpleDoubleProperty();
//
//we bind our properties with the default bounds of the value axis. But before, we should verify the given range according to the mathematic logarithmic interval definition.
public LogarithmicAxis() {
super(1, 100);
bindLogBoundsToDefaultBounds();
}
public LogarithmicAxis(double lowerBound, double upperBound) {
super(lowerBound, upperBound);
try {
validateBounds(lowerBound, upperBound);
bindLogBoundsToDefaultBounds();
} catch (IllegalLogarithmicRangeException e) {
}
}
/**
* Bind our logarithmic bounds with the super class bounds, consider the base 10 logarithmic scale.
*/
private void bindLogBoundsToDefaultBounds() {
logLowerBound.bind(new DoubleBinding() {
{
super.bind(lowerBoundProperty());
}
#Override
protected double computeValue() {
return Math.log10(lowerBoundProperty().get());
}
});
logUpperBound.bind(new DoubleBinding() {
{
super.bind(upperBoundProperty());
}
#Override
protected double computeValue() {
return Math.log10(upperBoundProperty().get());
}
});
}
/**
* Validate the bounds by throwing an exception if the values are not conform to the mathematics log interval:
* ]0,Double.MAX_VALUE]
*
* #param lowerBound
* #param upperBound
* #throws IllegalLogarithmicRangeException
*/
private void validateBounds(double lowerBound, double upperBound) throws IllegalLogarithmicRangeException {
if (lowerBound < 0 || upperBound < 0 || lowerBound > upperBound) {
throw new IllegalLogarithmicRangeException(
"The logarithmic range should be include to ]0,Double.MAX_VALUE] and the lowerBound should be less than the upperBound");
}
}
//Now we have to implement all abstract methods of the ValueAxis class.
//The first one, calculateMinorTickMarks is used to get the list of minor tick marks position that you want to display on the axis. You could find my definition below. It's based on the number of minor tick and the logarithmic formula.
#Override
protected List<Number> calculateMinorTickMarks() {
Number[] range = getRange();
List<Number> minorTickMarksPositions = new ArrayList<>();
if (range != null) {
Number lowerBound = range[0];
Number upperBound = range[1];
double logUpperBound = Math.log10(upperBound.doubleValue());
double logLowerBound = Math.log10(lowerBound.doubleValue());
int minorTickMarkCount = getMinorTickCount();
for (double i = logLowerBound; i <= logUpperBound; i += 1) {
for (double j = 0; j <= 10; j += (1. / minorTickMarkCount)) {
double value = j * Math.pow(10, i);
minorTickMarksPositions.add(value);
}
}
}
return minorTickMarksPositions;
}
//Then, the calculateTickValues method is used to calculate a list of all the data values for each tick mark in range, represented by the second parameter. The formula is the same than previously but here we want to display one tick each power of 10.
#Override
protected List<Number> calculateTickValues(double length, Object range) {
List<Number> tickPositions = new ArrayList<Number>();
if (range != null) {
Number lowerBound = ((Number[]) range)[0];
Number upperBound = ((Number[]) range)[1];
double logLowerBound = Math.log10(lowerBound.doubleValue());
double logUpperBound = Math.log10(upperBound.doubleValue());
System.out.println("lower bound is: " + lowerBound.doubleValue());
for (double i = logLowerBound; i <= logUpperBound; i += 1) {
for (double j = 1; j <= 10; j++) {
double value = (j * Math.pow(10, i));
tickPositions.add(value);
}
}
}
return tickPositions;
}
//The getRange provides the current range of the axis. A basic implementation is to return an array of the lowerBound and upperBound properties defined into the ValueAxis class.
#Override
protected Number[] getRange() {
return new Number[] { lowerBoundProperty().get(), upperBoundProperty().get() };
}
//The getTickMarkLabel is only used to convert the number value to a string that will be displayed under the tickMark. Here I choose to use a number formatter.
#Override
protected String getTickMarkLabel(Number value) {
NumberFormat formatter = NumberFormat.getInstance();
formatter.setMaximumIntegerDigits(6);
formatter.setMinimumIntegerDigits(1);
return formatter.format(value);
}
//The method setRange is used to update the range when data are added into the chart. There is two possibilities, the axis is animated or not. The simplest case is to set the lower and upper bound properties directly with the new values.
#Override
protected void setRange(Object range, boolean animate) {
if (range != null) {
Number lowerBound = ((Number[]) range)[0];
Number upperBound = ((Number[]) range)[1];
try {
validateBounds(lowerBound.doubleValue(), upperBound.doubleValue());
} catch (IllegalLogarithmicRangeException e) {
}
lowerBoundProperty().set(lowerBound.doubleValue());
upperBoundProperty().set(upperBound.doubleValue());
}
}
//We are almost done but we forgot to override 2 important methods that are used to perform the matching between data and the axis (and the reverse).
#Override
public Number getValueForDisplay(double displayPosition) {
double delta = logUpperBound.get() - logLowerBound.get();
if (getSide().isVertical()) {
return Math.pow(10, (((displayPosition - getHeight()) / -getHeight()) * delta) + logLowerBound.get());
} else {
return Math.pow(10, (((displayPosition / getWidth()) * delta) + logLowerBound.get()));
}
}
#Override
public double getDisplayPosition(Number value) {
double delta = logUpperBound.get() - logLowerBound.get();
double deltaV = Math.log10(value.doubleValue()) - logLowerBound.get();
if (getSide().isVertical()) {
return (1. - ((deltaV) / delta)) * getHeight();
} else {
return ((deltaV) / delta) * getWidth();
}
}
/**
* Exception to be thrown when a bound value isn't supported by the logarithmic axis<br>
*
*
* #author Kevin Senechal mailto: kevin.senechal#dooapp.com
*
*/
public class IllegalLogarithmicRangeException extends Exception {
/**
* #param string
*/
public IllegalLogarithmicRangeException(String message) {
super(message);
}
}
}
We too had these problems with the suggested implementation of logarithmicaxis, here is the complete code with fixes that worked for us..
import com.sun.javafx.charts.ChartLayoutAnimator;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.chart.ValueAxis;
import javafx.util.Duration;
//http://blog.dooapp.com/logarithmic-scale-strikes-back-in-javafx-20
//Edited by Vadim Levit & Benny Lutati for usage in AgentZero ( https://code.google.com/p/azapi-test/ )
public class LogarithmicNumberAxis extends ValueAxis<Number> {
private Object currentAnimationID;
private final ChartLayoutAnimator animator = new ChartLayoutAnimator(this);
//Create our LogarithmicAxis class that extends ValueAxis<Number> and define two properties that will represent the log lower and upper bounds of our axis.
private final DoubleProperty logUpperBound = new SimpleDoubleProperty();
private final DoubleProperty logLowerBound = new SimpleDoubleProperty();
//
//we bind our properties with the default bounds of the value axis. But before, we should verify the given range according to the mathematic logarithmic interval definition.
public LogarithmicNumberAxis() {
super(1, 10000000);
bindLogBoundsToDefaultBounds();
}
public LogarithmicNumberAxis(double lowerBound, double upperBound) {
super(lowerBound, upperBound);
validateBounds(lowerBound, upperBound);
bindLogBoundsToDefaultBounds();
}
public void setLogarithmizedUpperBound(double d) {
double nd = Math.pow(10, Math.ceil(Math.log10(d)));
setUpperBound(nd == d ? nd * 10 : nd);
}
/**
* Bind our logarithmic bounds with the super class bounds, consider the
* base 10 logarithmic scale.
*/
private void bindLogBoundsToDefaultBounds() {
logLowerBound.bind(new DoubleBinding() {
{
super.bind(lowerBoundProperty());
}
#Override
protected double computeValue() {
return Math.log10(lowerBoundProperty().get());
}
});
logUpperBound.bind(new DoubleBinding() {
{
super.bind(upperBoundProperty());
}
#Override
protected double computeValue() {
return Math.log10(upperBoundProperty().get());
}
});
}
/**
* Validate the bounds by throwing an exception if the values are not
* conform to the mathematics log interval: ]0,Double.MAX_VALUE]
*
* #param lowerBound
* #param upperBound
* #throws IllegalLogarithmicRangeException
*/
private void validateBounds(double lowerBound, double upperBound) throws IllegalLogarithmicRangeException {
if (lowerBound < 0 || upperBound < 0 || lowerBound > upperBound) {
throw new IllegalLogarithmicRangeException(
"The logarithmic range should be in [0,Double.MAX_VALUE] and the lowerBound should be less than the upperBound");
}
}
//Now we have to implement all abstract methods of the ValueAxis class.
//The first one, calculateMinorTickMarks is used to get the list of minor tick marks position that you want to display on the axis. You could find my definition below. It's based on the number of minor tick and the logarithmic formula.
#Override
protected List<Number> calculateMinorTickMarks() {
List<Number> minorTickMarksPositions = new ArrayList<>();
return minorTickMarksPositions;
}
//Then, the calculateTickValues method is used to calculate a list of all the data values for each tick mark in range, represented by the second parameter. The formula is the same than previously but here we want to display one tick each power of 10.
#Override
protected List<Number> calculateTickValues(double length, Object range) {
LinkedList<Number> tickPositions = new LinkedList<>();
if (range != null) {
double lowerBound = ((double[]) range)[0];
double upperBound = ((double[]) range)[1];
for (double i = Math.log10(lowerBound); i <= Math.log10(upperBound); i++) {
tickPositions.add(Math.pow(10, i));
}
if (!tickPositions.isEmpty()) {
if (tickPositions.getLast().doubleValue() != upperBound) {
tickPositions.add(upperBound);
}
}
}
return tickPositions;
}
/**
* The getRange provides the current range of the axis. A basic
* implementation is to return an array of the lowerBound and upperBound
* properties defined into the ValueAxis class.
*
* #return
*/
#Override
protected double[] getRange() {
return new double[]{
getLowerBound(),
getUpperBound()
};
}
/**
* The getTickMarkLabel is only used to convert the number value to a string
* that will be displayed under the tickMark. Here I choose to use a number
* formatter.
*
* #param value
* #return
*/
#Override
protected String getTickMarkLabel(Number value) {
NumberFormat formatter = NumberFormat.getInstance();
formatter.setMaximumIntegerDigits(10);
formatter.setMinimumIntegerDigits(1);
return formatter.format(value);
}
/**
* The method setRange is used to update the range when data are added into
* the chart. There is two possibilities, the axis is animated or not. The
* simplest case is to set the lower and upper bound properties directly
* with the new values.
*
* #param range
* #param animate
*/
#Override
protected void setRange(Object range, boolean animate) {
if (range != null) {
final double[] rangeProps = (double[]) range;
final double lowerBound = rangeProps[0];
final double upperBound = rangeProps[1];
final double oldLowerBound = getLowerBound();
setLowerBound(lowerBound);
setUpperBound(upperBound);
if (animate) {
animator.stop(currentAnimationID);
currentAnimationID = animator.animate(
new KeyFrame(Duration.ZERO,
new KeyValue(currentLowerBound, oldLowerBound)
),
new KeyFrame(Duration.millis(700),
new KeyValue(currentLowerBound, lowerBound)
)
);
} else {
currentLowerBound.set(lowerBound);
}
}
}
/**
* We are almost done but we forgot to override 2 important methods that are
* used to perform the matching between data and the axis (and the reverse).
*
* #param displayPosition
* #return
*/
#Override
public Number getValueForDisplay(double displayPosition) {
double delta = logUpperBound.get() - logLowerBound.get();
if (getSide().isVertical()) {
return Math.pow(10, (((displayPosition - getHeight()) / -getHeight()) * delta) + logLowerBound.get());
} else {
return Math.pow(10, (((displayPosition / getWidth()) * delta) + logLowerBound.get()));
}
}
#Override
public double getDisplayPosition(Number value) {
double delta = logUpperBound.get() - logLowerBound.get();
double deltaV = Math.log10(value.doubleValue()) - logLowerBound.get();
if (getSide().isVertical()) {
return (1. - ((deltaV) / delta)) * getHeight();
} else {
return ((deltaV) / delta) * getWidth();
}
}
/**
* Exception to be thrown when a bound value isn't supported by the
* logarithmic axis<br>
*
*
* #author Kevin Senechal mailto: kevin.senechal#dooapp.com
*
*/
public class IllegalLogarithmicRangeException extends RuntimeException {
/**
* #param string
*/
public IllegalLogarithmicRangeException(String message) {
super(message);
}
}
}
I think your problem is this:
super(1, 100);
From the documentation:
Create a non-auto-ranging ValueAxis with the given upper & lower bound
Try using a constructor without parameters, which will make the boundaries auto-ranging.
You should end up with a constructor looking like this:
public LogarithmicAxis() {
// was: super(1, 100);
super();
bindLogBoundsToDefaultBounds();
}
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;
}
}
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.