I'm trying to use a ListView as an Editor for Strings, that come out of a custom data model. I use TextFieldListCells with an appropriate StringConverter for the cells.
There is an add button next to the ListView that calls this method on action:
#FXML
private void addElement() {
WordListItem newItem = new WordListItem(-1, "");
wordListItems.add(newItem);
wordListView.setEditable(true);
wordListView.getSelectionModel().select(wordListItems.indexOf(newItem));
wordListView.edit(wordListItems.indexOf(newItem));
wordListView.setEditable(false);
}
Where wordListView is the ListView and wordListItems is the ObservableList containing the data for the wordListView.
This does work, except for when the list is empty (not null), and I couldn't quite explain why, so I inspected the Java source code for help.
Here's what I found out so far: the edit(int) call on ListView changes the ListViews internal editIndex value, which is supposed to call the EDIT_START Event. The editIndex is an ReadOnlyIntegerWrapper in which I found some weird code that I can't quite understand and I'm not sure if thats actually producing a bug or I just can't see why they did it:
#Override
protected void fireValueChangedEvent() {
super.fireValueChangedEvent();
if (readOnlyProperty != null) {
readOnlyProperty.fireValueChangedEvent();
}
}
This method is called whenever the editIndex property of ListView is changed. The problem: readOnlyProperty is null, because it's not set anywhere. The only place I could find where it got set is in the getter:
public ReadOnlyIntegerProperty getReadOnlyProperty() {
if (readOnlyProperty == null) {
readOnlyProperty = new ReadOnlyPropertyImpl();
}
return readOnlyProperty;
}
(ReadOnlyIntegerImpl is an inner private class and readOnlyProperty is it's type)
Now to my actual question: Is this a bug or am I overseeing something? Is there a reason why I can't add and edit a newly created Element in my list like that when it's empty, or is it really just this getter not being called yet?
The source code you found just is code for lazy initializing the property.
Unless new value is assigned to the property or the property itself is requested, null can be used as the property to avoid unnecessary creation of property objects. This is not an issue here.
The issue seems to be the ListView cells not being updated before edit is called. This happens during layout, so "manually" calling layout before starting the edit should work:
private void addElement() {
WordListItem newItem = new WordListItem(-1, "");
wordListItems.add(newItem);
wordListView.setEditable(true);
wordListView.layout();
wordListView.edit(wordListItems.size()-1);
wordListView.setEditable(false);
}
Related
I'm currently using osmdroid to display current positioning.
Based on the following example i tried to optimize the system a little bit by not constructing the ItemizedOverlay<OverlayItem> and ArrayList<OverlayItem> each time my location is changed, but construct them only once in the constructor, and later on simply add points to my ArrayList variable.
Here's how it looks now:
private void InitializeMarkersOverlay() {
mOverlayItemArrayList = new ArrayList<OverlayItem>();
ItemizedOverlay<OverlayItem> locationOverlay =
new ItemizedIconOverlay<OverlayItem>(this, mOverlayItemArrayList, null);
mMapView.getOverlays().add(locationOverlay);
}
and when a new location arrives:
private void AddPointToOverlay(GeoPoint gPt, boolean bShouldClearList) {
OverlayItem overlayItem = new OverlayItem("", "", gPt);
Drawable markerDrawable = ContextCompat.getDrawable(this, R.drawable.pin);
overlayItem.setMarker(markerDrawable);
// first time initializer
if(bShouldClearList) {
mOverlayItemArrayList.clear();
}
mOverlayItemArrayList.add(overlayItem);
}
Since my mMapView already has a pointer to mOverlayItemArrayList i was hoping that my mapview's layer would be automatically notified regarding the change. but nothing actually happens. Only by recreating the objects, i get to see the pin.
Adding to the list does not work because ItemizedIconOverlay need to do some operations on addition. You can check source code for ItemizedIconOverlay.
You can see there is call to populate() in addItem method (and all other methods which are manipulating with items).
public boolean addItem(final Item item) {
final boolean result = mItemList.add(item);
populate();
return result;
}
But populate() is an implementation detail and is marked as protected so you cannot call it directly.
Correct solution would be:
Don't keep reference to the list but to ItemizedIconOverlay
instance.
Use mLocationOverlay.addItem(overlayItem)
You may need to call mapView.invalidate() after adding new point.
I got it working by accessing the overlay directly from the mapview object, not sure why exactly, as i was hoping mMapView.getOverlays() would hold a reference to the ItemizedIconOverlay and its itimized array
if(mMapView.getOverlays().size() > 0) {
((ItemizedIconOverlay<OverlayItem>)mMapView.getOverlays().get(0)).removeAllItems();
((ItemizedIconOverlay<OverlayItem>)mMapView.getOverlays().get(0)).addItem(overlayItem);
}
}
I'm having trouble figuring out why my render method isn't being called. Here is my custom cell that extends AbstractCell, broken down to its simplest form.
public class FormHistoryCell<T> extends AbstractCell<T> {
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, T value, SafeHtmlBuilder sb) {
System.out.println("Rendering customer cell...");
if (value == null) {
return;
}
}
}
Here is the snipet in my code which creates an instance of "FormHistoryCell" and attempts to add it to a CellList.
#UiFactory
CellList<FormHistoryCell> initList() {
FormHistoryCell formHistoryCell = new FormHistoryCell();
CellList historyList = new CellList<FormHistoryCell>(formHistoryCell);
return historyList;
}
I have tried different things like adding a constructor that takes a String argument, etc. The constructor is called, but the render method is not. Looking at that Abstract class it extends, it seems the render method is called within the "setValue" method, but didn't see where that is called in other custom cell extensions whose render methods seem to be getting called just fine. I'm sure I'm missing something obvious here but can't figure out what. Please help.
Based on the code you provided, there is no reason for a browser to call the render method of your cell. You simply passed a reference to an object FormHistoryCell to your CellList. The render method is only needed when a browser has to display a cell and its content. This happens when you add data to your CellList, as #outellou suggested.
I am worried that I might end up with a lot of "undead" Objects inside my Listener List
that still get notified even though they could simply be removed.
Assume the following classes:
This is my ListView, a UI component where I use the addItem method to populate the list with objects.
After a while I might call clear to remove every Item in the List:
//Displays some Model objects
ListView
{
//Creates a new Cell via createCell and adds the Cell to the ItemList
public void addItem(MyModelObject obj) { ... }
//Simply cleares the ItemList
public void clear() { ... }
...
//Creates a new Cell to be added to the ListView
private ListCell createCell(MyModelObject obj)
{
//Create the Controller for the Cell and return the Cell
return new ListCellController(obj).getCellUI();
}
}
This is the ListCellController seen in the createCell method above. It holds the reference to a Model Object and is responsible for setting the correct contents in the ListCell it controls.
The Controller sets itself as a Listener in the Model Object:
//Handles the Content that is displayed in a ListCell
ListCellController implements MyModelObjectListener
{
private final ListCell _ui = ...
public ListCellController(MyModelObject obj)
{
obj.addListener(this); //Get informed if Model Object changes
_ui.setText(obj.getName()); //Control what is displayed in the Cell
}
//Returns the UI Object that is controlled by this Controller
public ListCell getCellUI() { return _ui; }
...
}
This is the Model Object, it does some stuff and occasionally notifies its listeners if it changed.
//My Model Object that informs listeners if it changed
MyModelObject
{
private List<MyModelObjectListener _listeners = ...;
...
public void addListener(MyModelObjectListener listener) { ... }
}
Now, what happens if I add some Items to the ListView and then clear it? The ListView never holds any references to Controller objects, only to ListCells. If the ListView is cleared, all the references to ListCells are discarded.
The ListCells do not know if they are currently displayed or already discarded and of course the Controller cannot know either.
I assume, that now, even though the ListView is empty, the Model Object still holds the references to the Controllers in the _listeners List. Because of that, the Controllers are not garbage-collected and thus, the ListCells are neither.
Does that mean that, if I add and remove a lot of Items from my ListView I will end up with a huge List of Listeners? How can I avoid this If I do not know if a ListCell is displayed or not?
TL;DR:
Will my _listeners List prevent garbage-collection of Controllers and ListCells?
I'm sorry, but I'm not understanding well what is your problem, but for what I know about garbage collection, even if two object refer to each other, if none of them is referenced in runtime, they will be deleted anyway.
So, if your problem is about this, if you have at least one listener referenced in runtime, it will not be garbage collected, therefore, you would have to remove it manually.
I hope I could be of any help.
I'm passing a listview in to an onselect but theres a couple of ways it's called from different listviews. So i'm trying to work out which listview is being clicked.
I thought I could do the following however the string thats returned is like com.myapp.tool/id/32423423c (type thing) instead of lvAssets.
Here is what I've got:
#Override
public void onNumberRowSelect(ListView listview, clsNameID stat) {
if(listview.getAdapter().toString().equals("lvGenericAssets")){
} else if(listview.getAdapter().toString().equals("lvAssets")){
} else {
Functions.ShowToolTip(getApplicationContext(),
listview.getAdapter().toString());
}
}
As Emil Adz said in first, you can get the id of your list by calling list.getId();
Then use String idList = getResources().getResourceEntryName(id); and you will be able to get the name of the id you have given to your list
Why wont you just use: list.getId(); if you defined it in the XML file then you should define there an id for you ListView.
If you are doing this from code then you can use the list.setId(); to first set it's id.
Another thing you can do is to add a Tag to your listView: list.setTag("list1");
and latter on distinct this listView using the Tag: list.getTag();
I have a :
Client Class
ListView
TextField
I need to populate my ListView in order to form a table:
WORKING CODE:
clientModel = new LoadableDetachableModel() {
#Override
protected Object load() {
return Client.getClientListByCompanyName(searchClientInput.getValue());
}
};
searchClientInput.setModel(new Model<String>());
searchClientInput.add(new AjaxFormComponentUpdatingBehavior("onkeyup") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(clientListViewContainer);
}
});
clientListView = new ListView<Client>(CLIENT_ROW_LIST_ID, clientModel) {
#Override
protected void populateItem(ListItem<Client> item) {
Client client = item.getModelObject();
item.add(new Label(CLIENT_ROW_COMPANY_CNPJ_ID, client.getCompanyName()));
item.add(new Label(CLIENT_ROW_COMPANY_NAME_ID, client.getCompanyCnpj()));
}
};
clientListViewContainer.setOutputMarkupId(true);
clientListViewContainer.add(clientListView);
add(clientListViewContainer);
Now, in my HTML, I have a TextField. Whenever an user types something in this TextField, a select will be made in the database with whatever he typed. So for each word, a select is made, and the table needs to be updated. I am guessing I will need to use AJAX and possibly a Model. I'm kind of lost about how I can do this, if someone can provide me examples I would be very grateful.
EDIT: New code that is throwing exception: Last cause: Attempt to set model object on null model of component: searchClientForm:searchClientInput
EDIT 2: Ok so the exception was that my TextField didn't had a model to bind data to. So what I did was: searchClientInput.setModel(new Model<String>());
I also had a problem with the event. Using onkeydown was working, but not as intended. I had Company Name 1-4. If I typed Company Name 1, I would need to press one key again so the table would get updated. With onkeyup this don't happens. Thanks for the help.
You could give the ListView a LoadableDetachableModel which provides the selected clients matching your TextField's value.
Use an AjaxFormComponentUpdatingBehavior on your TextField which add a parent of the ListView to the request target (don't forget #setOutputMarkupId().
I believe the best way to perform what you want (which is repainting a table/list at each input change --> DB access) is with a DataView and a DataProvider.
A DataView is just like the ListView component except it uses an IDataProvider to get the data you want to present. You are able to implement the DataProvider so it accesses your DB, and you can add restrictions (where clauses) to the DataProvider.
[this is more like pseudo-code]
public final class MyDataProvider<T> extends SortableDataProvider<T> {
// ...
Set filters;
// filters is the set where the restrictions you want to apply are stored
...
#Override
public Iterator<T> iterator(int first, int count) {
// DAO (Data Access Object) access to DB
// ...
return dao.findByRestrictions(filters).iterator();
}
...
}
Now on the ajax event on your input component you are able to update the filter being used in the DataProvider, and in the the next repaint of the DataView, the provider will "pull" the data matching the restrictions defined in the filter.
Hope it helps. Best regards.