I have a large 'order form' XPage that displays 99 rows, with 3 text input boxes on each row.
To capture changes, I have placed a call to a SSJS function in the 'onchange' event of each input box.
The call simply sends the product ID, the type of change (which column) and the quantity.
The SSJS function then preserves those changes in a sessionScope variable (java.util.HashMap).
There is no refresh associated with the change.
The changes are processed en masse when the user clicks the 'Submit' button.
That is another SSJS function that simply writes all of the changes to the back-end Domino database.
That all seems to work fine and has done for a couple of years.
However, it seems my users are becoming too efficient with the application, and are typing faster than it can keep up.
My debug code writes each change to the server's console, and I can see where some changes are simply ignored if the user makes changes in quick succession (they simply tab between the input boxes). It's almost as if the server is too busy processing the previous change and skips one to move on to another. At times, whole blocks of changes are missed, and then the application picks back up when it can.
Am I using the wrong technique to capture the changes? Is there something I can do to ensure the application initiates the onchange event each and every time?
I have tested this using IE8/9 & FF24.
I have looked at other such posts that propose using the 'onkeyup' event instead. I don't think that would work in my case, as the users may order double-digit quantities.
Any/all suggestions would be gratefully appreciated!
Terry,
you need to revisit the architecture. If the updates are processed on submit, why bother to send them individually to the server - as Tim nicely pointed out. What I would do:
create 2 Java classes: one "Order" one "LineItem"
Let the Order class implement the Map interface Map
Use the Order class for your repeat control (it will give you the key of each LineItem as the repeat variable)
Bind the fields inside the repeat to Order[RepeatKey].fieldName
Use Order in a object data source
Implement the save method in the Order class and call it in the save method of the object data source
Very rought outline, let me know if you need me to elaborate. The Java Collections Framework is your friend.
It is easier than it looks:
public class LineItem {
private String unid;
private String partno;
private int quantity;
private long unitprice;
/**
* Constructor for new items
*/
public LineItem() {
this.unid = null;
}
/**
* Constructor for existing items
*/
public LineItem(Document doc) {
this.unid = doc.getUniversalId();
// more here
}
/**
* #return the unid
*/
public String getUnid() {
return this.unid;
}
/**
* #return the partno
*/
public String getPartno() {
return this.partno;
}
/**
* #param partno the partno to set
*/
public void setPartno(String partno) {
this.partno = partno;
}
/**
* #return the quantity
*/
public int getQuantity() {
return this.quantity;
}
/**
* #param quantity the quantity to set
*/
public void setQuantity(int quantity) {
this.quantity = quantity;
}
/**
* #return the unitprice
*/
public long getUnitprice() {
return this.unitprice;
}
/**
* #param unitprice the unitprice to set
*/
public void setUnitprice(long unitprice) {
this.unitprice = unitprice;
}
public void save(Database db) {
Document doc = null;
if (this.unid == null) {
doc = db.createDocument();
doc.replaceItem("Form", "LineItem");
}
doc.replaceItem("PartNo", this.partno);
// More here
doc.save();
}
}
and for the Order - presuming you load from a document collection.
public class Order implements Map<String, LineItem> {
// You might want to have a stack here to keep order
private final Map<String, LineItem> backingMap = new LinkedHashMap<String, LineItem>();
private final Set<String> deletedItemKeys = new HashSet<String>();
// The key we use for new items when unid is null
private int lastNewItemNumber = 0;
#Override
public int size() {
return this.backingMap.size();
}
#Override
public boolean isEmpty() {
return this.backingMap.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return this.backingMap.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return this.backingMap.containsValue(value);
}
#Override
public LineItem get(Object key) {
return this.backingMap.get(key);
}
#Override
public LineItem put(String key, LineItem value) {
// Here it gets a little special
// We need to prevent null keys
if (key == null) {
key = String.valueOf(this.lastNewItemNumber);
lastNewItemNumber++;
}
this.deletedItemKeys.remove(key);
return this.backingMap.put(key, value);
}
#Override
public LineItem remove(Object key) {
this.deletedItemKeys.add(key.toString());
return this.backingMap.remove(key);
}
#Override
public void putAll(Map<? extends String, ? extends LineItem> m) {
for (Map.Entry<? extends String, ? extends LineItem> me : m.entrySet()) {
this.put(me.getKey(), me.getValue());
}
}
#Override
public void clear() {
this.deletedItemKeys.addAll(this.backingMap.keySet());
this.backingMap.clear();
}
#Override
public Set<String> keySet() {
return this.backingMap.keySet();
}
#Override
public Collection<LineItem> values() {
return this.backingMap.values();
}
#Override
public Set<java.util.Map.Entry<String, LineItem>> entrySet() {
return this.backingMap.entrySet();
}
public void load(NotesDocumentCollection dc) throws NotesException {
Document doc = dc.getFirstDocument();
Document nextDoc;
while (doc != null) {
nextDoc = dc.getNextDocument(doc);
LineItem li = new LineItem(doc);
this.put(doc.getUniversalId(), li);
doc.recycle();
doc = nextDoc;
}
doc.recyle();
}
public void save(Database db) {
for (LineItem item : this.backingMap.values()) {
item.save(db);
}
// Now kill the left overs - needs error handling
for (String morituri : this.deletedItemKeys) {
Document delDoc = db.getDocumentByUnid(morituri);
if (delDoc != null) {
delDoc.remove(true);
}
}
}
}
Related
I am trying to create an ObservableList() to use with my Tableview. The StringData type is a class containing two SimpleStringProperty var. I want to create this list and bind each variable to an specific position of a List. Something like this:
public class DownloadService implements Runnable {
//List that will be updated
public List<SimpleStringProperty> dList = new ArrayList<SimpleStringProperty>();
public class MainScreenController implements Initializable {
//List that populates TV
private ObservableList<DataString> data = FXCollections.observableArrayList();
//tableview
#FXML
private TableView<DataString> tbl_table;
DownloadService download;
...}
public class DataString{
public final SimpleStringProperty state;
public final SimpleStringProperty sinc;
public SimpleStringProperty stateProperty() {
return state;
}
public void setState(String status) {
state.set(status);
}
public SimpleStringProperty sincProperty() {
return sinc;
}
public void setSinc(String sinc) {
this.sinc.set(sinc);
}
}
On MainScreenController I try to do this:
DataString s = new DataString();
s.state.bind (download.dList.get(data.size()));
s.sinc.bind (download.dList.get(data.size()));
data.add(s);
tbl_table.setItems(data);
However, I cannot update the content of data when I update the list on DownloadService. I believe it should update the value of the column associated with the state and sinc variable everytime DownloadService updated the content of the list in each position. I am doing something wrong or is there another way to bind a StringProperty to a position on the list?
Thanks!
You are binding to the specific object inside the list, not to the position. If using SimpleStringProperty in dList isn't strict requirement, than you can use Bindings.stringValueAt():
StringBinding binding = Bindings.stringValueAt(dList, index);
s.state.bind(binding);
If you really need SimpleStringProperty, you can implement custom StringBinding, something like this:
class CustomStringBinding extends StringBinding {
private ObservableList<SimpleStringProperty> op;
private int index;
public CustomStringBinding(ObservableList<SimpleStringProperty> list, int index) {
this.op = list;
this.index = index;
super.bind(op, op.get(index));
}
#Override
public void dispose() {
super.unbind(op, op.get(index));
}
#Override
protected String computeValue() {
try {
return op.get(index).get();
} catch (IndexOutOfBoundsException ex) {
// log
}
return null;
}
#Override
public ObservableList<?> getDependencies() {
return FXCollections.singletonObservableList(op);
}
}
This is a followup to my previous post at ToolTip Performance in XPages I have got the code to do it written (not tested) so I can't seem to get my Managed Bean to get called properly. My config contians the following:
<managed-bean id="ToolTip">
<managed-bean-name>WFSToolTip</managed-bean-name>
<managed-bean-class>ca.workflo.wfsToolTip.ToolTipText</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
and I have stripped my code down to the bare minimum:
package ca.workflo.wfsToolTip;
public class ToolTipText {
public String getToolTipText(String key){
return key;
}
}
My class is in the build path. I have a simple XPage with one filed on it and a tool tip for that field. The code for the tooltip is:
<xe:tooltip id="tooltip1" for="inputText1">
<xe:this.label>
<![CDATA[#{javascript:WFSToolTip.getToolTipText("More Stuff");}]]>
</xe:this.label>
</xe:tooltip>
When I load the test XPage in the browser I get an error that:
Error while executing JavaScript computed expression
Script interpreter error, line=1, col=12: Error calling method 'getToolTipText(string)' on java class 'ca.workflo.wfsToolTip.ToolTipText'
JavaScript code
1: WFSToolTip.getToolTipText("More Stuff");
I can't figure out why the call to getToolTipText would fail.
Can anyone see where I'm going wrong. This is my first Managed Bean and at the moment it is managing me rather than the other way around.
Thanks.
You need to:
- implement Serializable which boils down to state it and provide a version
- implement Map ... a little more work
Then you use Expression Language instead of SSJS. It would look like #{WFSToolTip["More Stuff"]}
This is how such a class would look like. You need to:
adjust the view name to reflect the name you want
the view needs to be flat, column 1 = tooltip name, column 2 = tooltip text
somewhere (on an admin/config page) you need to call WFSToolTip.clear(); (in SSJS) after you update the values in the configuration.
The example doesn't lazyload since running though a view navigator once is really fast. No point to do all these lookups.
Here you go:
package com.notessensei.xpages;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import lotus.domino.Base;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import com.ibm.xsp.extlib.util.ExtLibUtil;
public class Parameters implements Serializable, Map<String, String> {
private final static String CONFIG_VIEW = "keywords";
private static final long serialVersionUID = 1L;
private final Map<String, String> internalMap = new HashMap<String, String>();
public Parameters() {
this.populateParameters(internalMap);
}
private void populateParameters(Map<String, String> theMap) {
Database d = ExtLibUtil.getCurrentDatabase();
try {
View v = d.getView(CONFIG_VIEW);
ViewEntryCollection vec = v.getAllEntries();
ViewEntry ve = vec.getFirstEntry();
ViewEntry nextVe = null;
while (ve != null) {
nextVe = vec.getNextEntry(ve);
// Load the parameters, column 0 is the key, column 0 the value
Vector colVal = ve.getColumnValues();
theMap.put(colVal.get(0).toString(), colVal.get(1).toString());
// Cleanup
this.shred(ve);
ve = nextVe;
}
// recycle, but not the current database!!!
this.shred(ve, nextVe, vec, v);
} catch (NotesException e) {
e.printStackTrace();
}
}
public void clear() {
this.internalMap.clear();
this.populateParameters(this.internalMap);
}
public boolean containsKey(Object key) {
return this.internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<String, String>> entrySet() {
return this.internalMap.entrySet();
}
public String get(Object key) {
return this.internalMap.get(key);
}
public boolean isEmpty() {
return this.internalMap.isEmpty();
}
public Set<String> keySet() {
return this.internalMap.keySet();
}
public String put(String key, String value) {
return this.internalMap.put(key, value);
}
public void putAll(Map<? extends String, ? extends String> m) {
this.internalMap.putAll(m);
}
public String remove(Object key) {
return this.internalMap.remove(key);
}
public int size() {
return this.internalMap.size();
}
public Collection<String> values() {
return this.internalMap.values();
}
private void shred(Base... morituri) {
for (Base obsoleteObject : morituri) {
if (obsoleteObject != null) {
try {
obsoleteObject.recycle();
} catch (NotesException e) {
// We don't care we want go get
// rid of it anyway
} finally {
obsoleteObject = null;
}
}
}
}
}
The difference to a regular HashMap is only the constructor that populates it. Hope that clarifies it.
I've never seen that id property.. My beans in faces-config look like this:
<managed-bean>
<managed-bean-name>CurrentJob</managed-bean-name>
<managed-bean-class>com.domain.inventory.Job</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Technically managed beans should implement Serializable and have a blank constructor. So you should have something like this inside :
public ToolTipText() {
}
I THINK you can get away without the Serializable for somethings... I always implement though but I'm sure you need the no argument constructor.
Thanks to all that have responded and helped out here especially Stephan Wissel. I thought I would post my version of Stephan's code, pretty much the same. There are issues with making the class an ApplicationScope because you need to shut down the HTTP task to refresh and reload the Class. What I did was added a button to the custom control where I to the view of the tooltips where I do the CRUD stuff and in the button do WFSToolTip().clear() and it rebuilds the map. Pretty neat. My next task for this is try to do the CRUD using JAVA and update the map directly. At the moment though I need to move on to my next task.
My next task revolves around a very similar Class. I have a master database that contains all the basic design and code. Then I have one or more applications that use that code and store the documents in their own database that contains the forms and views for that specific application. In the master I have created one or more application documents. Each of these documents contains the AppName (the key value) then the Map value is an array (Vector) containing the ReplicaID of the Application Database and a few other pieces of information. My class the loads a Map entry for each Application and collects a bunch of other information about the application from several places and stores that in the Map Value. At this point then I can set Database db = thisClass.getDatabase("App Name"). so a single custom control can be used for any/all of the applications. Pretty cool. I think I could get to like this.
Anyway here is the code I'm using for the ToolTips - BTW It has taken an XPage with about 175 fields and 100+ tooltips from being painfully slow to being acceptable. The good thing about it is that the XPage is creating a process profile document and once created it is not frequently modified as an admin action - not an everyday user action.
Please feel free point out error, omitions or suggestions to the code:
package ca.workflo.wfsToolTip;
import lotus.domino.Base;
import lotus.domino.Session;
import lotus.domino.Database;
import lotus.domino.View;
import lotus.domino.NotesException;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import com.ibm.xsp.extlib.util.ExtLibUtil;
public class ToolTipText implements Serializable, Map<String, String> {
private static final long serialVersionUID = 1L;
private Session s;
private String repID;
private Database db;
private Database helpDB;
private View helpView;
private ViewEntry ve;
private ViewEntry tVE;
private ViewEntryCollection veCol;
private final Map<String, String> internalMap = new HashMap<String, String>();
public ToolTipText() {
this.populateMap(internalMap);
}
private void populateMap(Map<String, String> theMap) {
try {
s = ExtLibUtil.getCurrentSession();
db = s.getCurrentDatabase();
repID = db.getProfileDocument("frmConfigProfile", "").getItemValue(
"WFSHelpRepID").firstElement().toString();
helpDB = s.getDbDirectory(null).openDatabaseByReplicaID(repID);
helpView = helpDB.getView("vwWFSToolTipHelp");
veCol = helpView.getAllEntries();
ve = veCol.getFirstEntry();
ViewEntry tVE = null;
while (ve != null) {
tVE = veCol.getNextEntry(ve);
Vector colVal = ve.getColumnValues();
theMap.put(colVal.get(0).toString(), colVal.get(1).toString());
recycleObjects(ve);
ve = tVE;
}
} catch (NotesException e) {
System.out.println(e.toString());
}finally{
recycleObjects(ve, tVE, veCol, helpView, helpDB);
}
}
public void clear() {
this.internalMap.clear();
this.populateMap(this.internalMap);
}
public boolean containsKey(Object key) {
return this.internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<String, String>> entrySet() {
return this.internalMap.entrySet();
}
public String get(Object key) {
try {
if (this.internalMap.containsKey(key)) {
return this.internalMap.get(key);
} else {
return "There is no Tooltip Help for " + key;
}
} catch (Exception e) {
return "error in tooltip get Object ";
}
}
public boolean isEmpty() {
return this.internalMap.isEmpty();
}
public Set<String> keySet() {
return this.internalMap.keySet();
}
public String put(String key, String value) {
return this.internalMap.put(key, value);
}
public void putAll(Map<? extends String, ? extends String> m) {
this.internalMap.putAll(m);
}
public String remove(Object key) {
return this.internalMap.remove(key);
}
public int size() {
return this.internalMap.size();
}
public Collection<String> values() {
return this.internalMap.values();
}
public static void recycleObjects(Object... args) {
for (Object o : args) {
if (o != null) {
if (o instanceof Base) {
try {
((Base) o).recycle();
} catch (Throwable t) {
// who cares?
}
}
}
}
}
}
i have a view pager with 3 fragment one of the fragment
have searchview widget and i get to know that i can,t implement the standard search interface so i implement some thing like this but how to implement Custom Suggestions in SearchView
public class LoaderCursor extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
// Create the list fragment and add it as our sole content.
if (fm.findFragmentById(android.R.id.content) == null) {
CursorLoaderListFragment list = new CursorLoaderListFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
public static class CursorLoaderListFragment extends ListFragment
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
#Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText("No phone numbers");
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
#Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
SearchView sv = new SearchView(getActivity());
sv.setOnQueryTextListener(this);
item.setActionView(sv);
}
public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}
#Override public boolean onQueryTextSubmit(String query) {
// Don't care about this.
return true;
}
#Override public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("FragmentComplexList", "Item clicked: " + id);
}
// These are the Contacts rows that we will retrieve.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
mAdapter.swapCursor(null);
}
}
}
I'm trying to make my code neater by using multiple classes for my applications
form options. Currently I keep getting null pointer exceptions when trying to setCurrent.
Here is my main class the error starts in my command listener when I call the other class.
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class CalFrontEnd extends MIDlet implements CommandListener {
private Display display;
private List list = new List("Please Select a Option", List.IMPLICIT);
private List Blist = new List("Please Select a Browsing Option", List.IMPLICIT);
private Command select = new Command("Select", Command.SCREEN, 1);
private Command exit = new Command("Exit", Command.EXIT, 2);
private Command save = new Command("Save,", Command.SCREEN, 2);
private DateField calendar;
Alert alert;
//
//
//
public CalFrontEnd() {
display = Display.getDisplay(this);
list.append("Select Date", null);
list.append("Add Events", null);
list.append("Remove Events", null);
list.append("Browse Events", null);
list.addCommand(select);
list.addCommand(exit);
list.setCommandListener(this);
}
//
//Start Application Method
//
public void startApp() {
display.setCurrent(list);
}
//
//Pause Application Method
//
public void pauseApp() {
}
//
//Destroy Application Method
//
public void destroyApp(boolean unconditional) {
}
//
//Method creates form which contains calendar
//
/*public void selectDate()
{
calendar = new DateField("Date In :", DateField.DATE, TimeZone.getTimeZone("GMT"));
Form cform = new Form("Calendar");
cform.append(calendar);
cform.addCommand(exit);
display.setCurrent(cform);
}*/
//
//Method creates form which contains adding events
//
public void AddEvents()
{
TextBox aeText = new TextBox("Add Event","", 256, 0);
display.setCurrent(aeText);
}
//
//Method creates form which contains removing events
//
public void RemoveEvents()
{
Form reform = new Form("Remove Event");
reform.append(calendar);
display.setCurrent(reform);
}
//
//Method creates form which contains removing events
//
public void BrowseEvents()
{
Blist.append("Monthly", null);
Blist.append("Daily", null);
Blist.addCommand(select);
Blist.addCommand(exit);
Blist.setCommandListener(this);
display.setCurrent(Blist);
}
//
//but it's better practice to make each form a different class extending CommandListener and it's own commandAction. And leave the display public static in MIDlet class
//...
public void commandAction(Command command, Displayable displayable) {
if (displayable == list) {
if (command == List.SELECT_COMMAND) {
switch (list.getSelectedIndex()) {
case 0: // select date
SelectDate.BuildCalendar(); //Error Here
break;
case 1: //add events
AddEvents();
break;
}
} else if (command == exit) {
destroyApp(false);
notifyDestroyed();
}
}
}
}
And here is the class that is being called.
public class SelectDate
{
private static DateField calendar;
private static Form form = new Form("derb");
private static Command select = new Command("Select", Command.SCREEN, 1);
private static Command exit = new Command("Exit", Command.EXIT, 2);
private static Command save = new Command("Save,", Command.SCREEN, 2);
private static Display display;
public static void BuildCalendar()
{
calendar = new DateField("Date In :", DateField.DATE, TimeZone.getTimeZone("GMT"));
form.append(calendar);
form.addCommand(exit);
display.setCurrent(form);
}
}
The NullPointerException happens because display in SelectDate class is null.
To fix that, you can for example drop it from there and instead, add to method parameters:
// ...
public static void BuildCalendar(Display display) // added parameter
Then, when you invoke above method from CalFrontEnd, pass the instance of display there:
// ...
SelectDate.BuildCalendar(display); //Error will go away
Situation:-
In my code I have to use the LWUIT Component object for the listview controls. The controls are dynamic and hence can be in any number.
Right now I am creating Component objects according to the controls(in numbers) i.e.- for every control to be created first the Component object is creating.
This process slows down the rendering of the listview when the controls are increasing.
Solution:-
If I create the Component object and use it in a loop for all the controls it is taking the reference of the object and hence displays all the listview items(controls) with the same data.
Now I am able to think of one last option of Cloning my object and using it to create the controls.
Problem:-
But I can't find any way in LWUIT by which I can achieve the copying of object.
What can be the alternatives in LWUIT to solve this problem?
P.S.-The listview items are of same type, but with different data.
Use a List component and the Renderer design pattern to create a "rubber stamp" component where you can display a large number of elements easily. See an explanation of this in the Codename One blog.
Create these classes first :
public class ListUtil {
private Vector data = new Vector();
private Content[] contents;
public ListUtil(Vector vData)
{
data = vData;
contents = new Content[vData.size()];
}
public List createList(Display display, CListCell renderer, ActionListener listener)
{
CList theList;
for(int i = 0; i < data.size(); i++)
{
contents[i] = new Content(String.valueOf(data.elementAt(i)));
}
theList = new CList(display, contents, renderer, listener);
return theList;
}
}
public class Content
{
private String row;
public Content(String row)
{
this.row = row;
}
public String getRow()
{
return (row);
}
}
public class CListCell extends Container implements ListCellRenderer {
private Label focus = new Label("");
public CListCell()
{
super();
// create and add the components here among the components which will display data
}
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected)
{
Content entry = null;
if (value instanceof Content)
entry = (Content)value;
componentDisplayingDataAddedIntoThisListCellRenderer.setText(entry.getRow());
return this;
}
public Component getListFocusComponent(List arg0)
{
return focus;
}
}
public class CList extends List {
private Display disp;
public CList(Display display, Object[] data, CListCell renderer, ActionListener actionListener)
{
super(data);
setListCellRenderer(renderer);
setIgnoreFocusComponentWhenUnfocused(true);
addActionListener(actionListener);
setScrollAnimationSpeed(getScrollAnimationSpeed()/4);
disp = display;
}
public void pointerReleased(int x,int y)
{
if (isEnabled() && hasFocus())
super.pointerReleased(x, y);
}
public void keyReleased(int keyCode)
{
if (isEnabled() && hasFocus())
{
if (disp.getGameAction(keyCode) == Display.GAME_FIRE)
pointerReleased(getX(),getY());
else
super.keyReleased(keyCode);
}
}
}
To create your List and add it to a Form :
public class myForm extends Form implements ActionListener
{
private Vector listSource = // your vector of data
private CListCell renderer = new CListCell();
private List theList = (new ListUtil(listSource)).createList(Display.getInstance(),renderer, this);
...
public void actionPerformed(ActionEvent evt)
{
if (evt.getSource() == theList)
doSomething();
}
}