RecyclerView.Adapter notifyDataSetChanged not working with AsyncTask Callback - callback

I am sure it's just a simple fault, but I'm not able to solve it.
My RecyclerView.Adapter loads its data with help of an AsyncTask (LoadAllPersonsFromDb) out of a SQLite DB. The response is handled by a callback interface (ILoadPersonFromDb.onFindAll).
Here is the code of the Adapter:
public class ListViewAdapter extends RecyclerView.Adapter<ListViewViewholder> implements LoadAllPersonsFromDb.ILoadPersonFromDb {
private int layout;
private List<Person> persons;
private Context context;
private AdapterDataSetListener adapterDataSetListener;
public ListViewAdapter(int layout, Context context,
AdapterDataSetListener adapterDataSetListener) {
this.layout = layout;
persons = new ArrayList<>();
this.context = context;
this.adapterDataSetListener = adapterDataSetListener;
new LoadAllPersonsFromDb(context, this).execute();
}
#Override
public ListViewViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new ListViewViewholder(view, context);
}
#Override
public void onBindViewHolder(ListViewViewholder holder, int position) {
holder.assignData(persons.get(position));
}
#Override
public int getItemCount() {
return persons.size();
}
#Override
public void onFindAll(List<Person> persons) {
Log.d("LISTVIEW", "Counted: " + persons.size() + " elements in db");
if (this.persons != null) {
this.persons.clear();
this.persons.addAll(persons);
} else {
this.persons = persons;
}
adapterDataSetListener.onChangeDataSet();
//notifyDataSetChanged();
}
public interface AdapterDataSetListener {
void onChangeDataSet();
}
}
As you can see, I tried more than one way to get it running. The simple notifyDataSetChanged did not do anything, so I made another interface which is used to delegate the ui information to the relating fragment. Following code documents this interface which is implemented in the relating fragment:
#Override
public void onChangeDataSet() {
Log.d("Callback", "called");
listViewAdapter.notifyDataSetChanged();
/*
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
listViewAdapter.notifyDataSetChanged();
}
});
*/
}
Here I also tried to put it on the MainUiThread but nothing works. I'm just not able to see where my problem is. Hopefully any of you guys can give me a hint.
The logging works, which is the prove for the working callbacks.
Thank you in advance.
PS: If you need any more code, just tell me and I will provide it.

instead of using the interface-llistener pattern, try this
#Override
public void onFindAll(List<Person> persons) {
Log.d("LISTVIEW", "Counted: " + persons.size() + " elements in db");
if (this.persons != null) {
this.persons.clear();
this.persons.addAll(persons);
} else {
this.persons = persons;
}
refereshAdapter(persons);
}
public void refereshAdapter(List<Person> persons){
listViewAdapter.clear();
listViewAdapter.addAll(persons);
listViewAdapter.notifyDataSetChanged();
}

To tell the background, I used RecyclerView in Version 23.1.1 because the latest 23.2.0 had some weird behaviour in holding a huge space for each card.
//Update: the problem with the space between cards, was because of a failure of myself in the layout file (match_parent instead of wrap_content). -_-
The upshot was using the latest version again and everything worked just fine. I have no idea why, but at the moment I am just happy, that I can go on. This little problem wasted enough time.
Maybe somebody has a similar situation and can use this insight.
Thx anyway #yUdoDis.

Related

propertyChange not called when restoring default values

I am building a preference page extending the FieldEditorPreferencePage class.
This is the code (some obvious code not displayed):
public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
public static final String PREF_KEY_1 = "checkBoxPref";
public static final String PREF_KEY_2 = "filePref";
private FileFieldEditor pathField;
private BooleanFieldEditor yesOrNoField;
private Composite pathFieldParent;
#Override
public void init(IWorkbench workbench) {
setPreferenceStore(new ScopedPreferenceStore(InstanceScope.INSTANCE, Activator.PLUGIN_ID));
}
#Override
protected void createFieldEditors() {
this.yesOrNoField = new BooleanFieldEditor(PREF_KEY_1, "Check this box!", getFieldEditorParent());
this.pathFieldParent = getFieldEditorParent();
this.pathField = new FileFieldEditor(PREF_KEY_2, "Path:", this.pathFieldParent);
addField(this.yesOrNoField);
addField(this.pathField);
boolean isChecked = getPreferenceStore().getBoolean(PREF_KEY_1);
updatePathFieldEnablement(! isChecked);
}
/**
* Updates the fields according to entered values
*/
private void updatePathFieldEnablement(boolean enabled) {
this.pathField.setEnabled(enabled, this.pathFieldParent);
}
#SuppressWarnings("boxing")
#Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(FieldEditor.VALUE) && event.getSource() == this.yesOrNoField) {
updatePathFieldEnablement(! (boolean) event.getNewValue());
}
super.propertyChange(event);
}
}
The propertyChange method is there to enable/disable the FileFieldEditor depending on the BooleanFieldEditor value.
It works OK if I change the BooleanFieldEditor valeu by checking or unchecking it, but the propertyChange is not called when I hit the "Restore default values" button.
Do someone see a reason for that?
OK, I think I've got my response.
I went further in my investigation and I got to this code which seems suspect to me:
In class BooleanFieldEditor :
#Override
protected void doLoadDefault() {
if (checkBox != null) {
boolean value = getPreferenceStore().getDefaultBoolean(getPreferenceName());
checkBox.setSelection(value);
wasSelected = value;
}
}
and in class StringFieldEditor
#Override
protected void doLoadDefault() {
if (textField != null) {
String value = getPreferenceStore().getDefaultString(
getPreferenceName());
textField.setText(value);
}
valueChanged();
}
We can see that the FileFieldEditor (that inherits from StringFieldEditor) launches an PropertyChangeEvent to its listeners (valueChanged();) but not the BooleanFieldEditor. I did not find any code indicating that BooleanFieldEditor are using another mechanism. I think this is a bug in jFace.
To get around this problem, I just had to override the FieldEditorPreferencePage#performDefaults method and the result's fine.

Pushing data from one user to another in Vaadin web app

I get the fact that it might take more than 10 lines of code (hopefully not more than 50), but I was wondering if you could help me anyway.
I'm trying to update one user's UI thread at runtime, based on another user's input. I've created a basic project which implements three predefined users (jim, tom and threeskin). I'd like to send a message from jim to tom and have it appear as a new Label object in tom's UI, without threeskin ever knowing about it, even though they're all logged in. Oh, and jim shouldn't have to refresh his page. The label should just spawn on screen out of it's own accord.
To say that I'd appreciate some help would be the understatement of the decade.
public class User {
public String nume;
public User(String nume) {
super();
this.nume = nume;
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public class Engine implements ServletContextListener {
public static ArrayList<User>userbase;
public void contextDestroyed(ServletContextEvent arg0) { }
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("This code is running at startup");
userbase =new ArrayList<User>();
userbase.add(new User("jim"));userbase.add(new User("tom"));userbase.add(new User("threeskin"));
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public class InfigeUI extends UI {
User us3r;
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = InfigeUI.class)
public static class Servlet extends VaadinServlet {}
protected void init(VaadinRequest request) {
VerticalLayout everything=new VerticalLayout();
setContent(everything);
if (us3r==null){everything.addComponent(auth());}else{everything.addComponent(main());}
}
ComponentContainer auth(){
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
TextField userField=new TextField();
Button login = new Button("Log in");
login.addClickListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
us3r=login(userField.getValue());
if (us3r!=null){
saveValue(InfigeUI.this, us3r);
layout.removeAllComponents();
layout.addComponent(main());
}else{Notification.show("I only know jim, tom and threeskin. Which one are you?");}}
});
layout.addComponent(userField);
layout.addComponent(login);
return layout;
}
User login(String nume){
for (int i=0;i<Engine.userbase.size();i++){
if (nume.equals(Engine.userbase.get(i).nume)){return Engine.userbase.get(i);}
}
return null;
}
static void saveValue(InfigeUI ui,User value){
ui.us3r=value;
ui.getSession().setAttribute("something", value);
VaadinService.getCurrentRequest().getWrappedSession().setAttribute("something", value);
}
ComponentContainer main(){
VerticalLayout vl=new VerticalLayout();
Label label=new Label("This is the post-login screen");
String name=new String(us3r.nume);
Label eticheta=new Label(name);
TextField to=new TextField("Send to");
TextField message=new TextField("Message");
Button sendNow=new Button("Send now!");
vl.addComponent(eticheta);
vl.addComponent(label);
vl.addComponent(eticheta);
vl.addComponent(to);
vl.addComponent(message);
vl.addComponent(sendNow);
return vl ;
}
}
Basically you want three things
UI updates for a user which does no action himself, or in other words a message sent from the server to the browser. To enable this, you need to annotate the UI class using #Push. Otherwise, the update will only be shown when the user does something which causes a server visit, e.g. clicks a button
Some way of sending messages between UI instances (there is one UI instance per user). You can use some message bus implementation for this (CDI, Spring, ...) or you can make a simple on using a static field (static fields are shared between all users). See e.g. https://github.com/Artur-/SimpleChat for one way of doing it. It's also a good idea here to avoid all *.getCurrent methods as they in many cases will refer to another UI than you think (e.g. sender when you are in the receiver code), and you will do something else than you intend.
Safely update a UI when a message arrives. This is done using UI.access, also visible in the chat example.
First of all you need to enable the server push on your project help
based on Vaadin Documentation.
However, below code example will give what you want:
Create an Broadcast Listener Interface:
public interface BroadcastListener {
public void receiveBroadcast(final String message);
}
The Broadcaster Class:
public class Broadcaster {
private static final List<BroadcastListener> listeners = new CopyOnWriteArrayList<BroadcastListener>();
public static void register(BroadcastListener listener) {
listeners.add(listener);
}
public static void unregister(BroadcastListener listener) {
listeners.remove(listener);
}
public static void broadcast(final String message) {
for (BroadcastListener listener : listeners) {
listener.receiveBroadcast(message);
}
}
}
Your UI with Push Enalbed (via Annotation):
#Push
public class BroadcasterUI extends UI implements BroadcastListener {
#Override
protected void init(VaadinRequest request) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
final TextArea message = new TextArea("",
"The system is going down for maintenance in 10 minutes");
layout.addComponent(message);
final Button button = new Button("Broadcast");
layout.addComponent(button);
button.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Broadcaster.broadcast(message.getValue());
}
});
// Register broadcast listener
Broadcaster.register(this);
}
#Override
public void detach() {
Broadcaster.unregister(this);
super.detach();
}
#Override
public void receiveBroadcast(final String message) {
access(new Runnable() {
#Override
public void run() {
Notification n = new Notification("Message received",
message, Type.TRAY_NOTIFICATION);
n.show(getPage());
}
});
}
you can find the full link here.

TableCell focusedProperty listener not called when inheriting from TextFieldTableCell

I want to create a Cell factory that returns a TableCell that behaves exactly like TextFieldTableCell, with the following difference: When it loses focus, it commits the changes.
My code is very simple:
public final class TextFieldCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
#Override
public TableCell<S, T> call(TableColumn<S, T> p) {
class EditingCell extends TextFieldTableCell {
public EditingCell() {
super();
setConverter(new DefaultStringConverter());
focusedProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println("changed!");
System.out.println("getText() = " + getText());
System.out.println("textProperty() = " + textProperty().get());
System.out.println("getItem = " + getItem());
}
});
}
#Override
public void startEdit() {
super.startEdit();
}
#Override
public void cancelEdit() {
super.cancelEdit();
}
}
return new EditingCell();
}
}
As you see I add a change listener in the focusedProperty. The problem is that the change method is not called (nothing is printed).
How can I get the desired behaviour? Thank you.
Basically, you have to register the listener with the textField's (not the cell's) focusedProperty. As the textfield is a private field of super, it's not directly accessible - you have to look it up once after it was added to the cell. That's when an edit was started for the first time:
private TextField myTextField;
#Override
public void startEdit() {
super.startEdit();
if (isEditing() && myTextField == null) {
// most simple case, assuming that there is no graphic other than the field
// TBD: implement the general case: walk the tree and find the field
myTextField = (TextField) getGraphic();
myTextField.focusedProperty().addListener((e, old, nvalue) -> {
if (!nvalue) {
T edited = getConverter().fromString(myTextField.getText());
commitEdit(edited);
}
});
}
}
Some notes:
this is a workaround around an open issue (vote for it!)
since jdk8, it's not entirely functional: won't commit if you click somewhere else inside the table
a recent answer uses a binding approach which might or not be fully functional (didn't test)

GEF + EMF: Why doesn't my editor remove the Figure for a removed object when refreshChildren() is called?

I have implemented a GEF editor for a graph-like EMF model, with a remove command for a certain type of node in the graph. I think I've done all the necessary steps in order to make this set up work (vainolo's blog has been a great help).
However, when I'm deleting a model element, the view doesn't get refreshed, i.e., the figure for the model element isn't removed from the editor view, and I have no idea why. I'd be extremely grateful if somebody could have a look at my sources and point me to any problems (and possibly solutions :)). Many thanks in advance!
Below are what I think are the important classes for this issue. Please do let me know should I add further code/edit the code, etc. (I've left out code that I thought doesn't help, e.g., getters and setters, class variables). Thanks!
DiagramEditPart
public class DiagramEditPart extends AbstractGraphicalEditPart {
public DiagramEditPart(Diagram model) {
this.setModel(model);
adapter = new DiagramAdapter();
}
#Override protected IFigure createFigure() {
Figure figure = new FreeformLayer();
return figure;
}
#Override protected void createEditPolicies() {
installEditPolicy(EditPolicy.LAYOUT_ROLE, new DiagramXYLayoutPolicy());
}
#Override protected List<EObject> getModelChildren() {
List<EObject> allModelObjects = new ArrayList<EObject>();
if (((Diagram) getModel()).getMyNodes() != null)
allModelObjects.addAll(((Diagram) getModel()).getMyNodes());
return allModelObjects;
}
#Override public void activate() {
if(!isActive()) {
((Diagram) getModel()).eAdapters().add(adapter);
}
super.activate();
}
#Override public void deactivate() {
if(isActive()) {
((Diagram) getModel()).eAdapters().remove(adapter);
}
super.deactivate();
}
public class DiagramAdapter implements Adapter {
#Override public void notifyChanged(Notification notification) {
switch (notification.getEventType()) {
case Notification.REMOVE: refreshChildren();
break;
default:
break;
}
}
#Override public Notifier getTarget() {
return (Diagram) getModel();
}
#Override public void setTarget(Notifier newTarget) {
// Do nothing.
}
#Override public boolean isAdapterForType(Object type) {
return type.equals(Diagram.class);
}
}
}
MyNodeEditPart
public class MyNodeEditPart extends AbstractGraphicalEditPart {
public MyNodeEditPart(MyNode model) {
this.setModel(model);
adapter = new MyNodeAdapter();
}
#Override protected IFigure createFigure() {
return new MyNodeFigure();
}
#Override protected void createEditPolicies() {
installEditPolicy(EditPolicy.COMPONENT_ROLE, new MyNodeComponentEditPolicy());
}
#Override protected void refreshVisuals() {
MyNodeFigure figure = (MyNodeFigure) getFigure();
DiagramEditPart parent = (DiagramEditPart) getParent();
Dimension labelSize = figure.getLabel().getPreferredSize();
Rectangle layout = new Rectangle((getParent().getChildren().indexOf(this) * 50),
(getParent().getChildren().indexOf(this) * 50), (labelSize.width + 20),
(labelSize.height + 20));
parent.setLayoutConstraint(this, figure, layout);
}
public List<Edge> getModelSourceConnections() {
if ((MyNode) getModel() != null && ((MyNode) getModel()).getDiagram() != null) {
ArrayList<Edge> sourceConnections = new ArrayList<Edge>();
for (Edge edge : ((MyNode) getModel()).getDiagram().getOutEdges(((MyNode) getModel()).getId())) {
sourceConnections.add(edge);
}
return sourceConnections;
}
return null;
}
// + the same method for targetconnections
#Override public void activate() {
if (!isActive()) {
((MyNode) getModel()).eAdapters().add(adapter);
}
super.activate();
}
#Override public void deactivate() {
if (isActive()) {
((MyNode) getModel()).eAdapters().remove(adapter);
}
super.deactivate();
}
public class MyNodeAdapter implements Adapter {
#Override
public void notifyChanged(Notification notification) {
refreshVisuals();
}
#Override
public Notifier getTarget() {
return (MyNode) getModel();
}
#Override
public void setTarget(Notifier newTarget) {
// Do nothing
}
#Override
public boolean isAdapterForType(Object type) {
return type.equals(MyNode.class);
}
}
}
MyNodeComponentEditPolicy
public class MyNodeComponentEditPolicy extends ComponentEditPolicy {
#Override
protected Command createDeleteCommand(GroupRequest deleteRequest) {
DeleteMyNodeCommand nodeDeleteCommand = new DeleteMyNodeCommand((MyNode) getHost().getModel());
return nodeDeleteCommand;
}
}
DeleteMyNodeCommand
public class DeleteMyNodeCommand extends Command {
public DeleteMyNodeCommand(MyNode model) {
this.node = model;
this.graph = node.getDiagram();
}
#Override public void execute() {
getMyNode().setDiagram(null);
System.out.println("Is the model still present in the graph? " + getGraph().getMyNodes().contains(getMyNode()));
// Returns false, i.e., graph doesn't contain model object at this point!
}
#Override public void undo() {
getMyNode().setDiagram(getGraph());
}
}
EDIT
Re execc's comment: Yes, refreshChildren() is being called. I've tested this by overriding it and adding a simple System.err line, which is being displayed on the console on deletion of a node:
#Override
public void refreshChildren() {
super.refreshChildren();
System.err.println("refreshChildren() IS being called!");
}
EDIT 2
The funny (well...) thing is, when I close the editor and persist the model, then re-open the same file, the node isn't painted anymore, and is not present in the model. But what does this mean? Am I working on a stale model? Or is refreshing/getting the model children not working properly?
EDIT 3
I've just found a peculiar thing, which might explain the isues I have? In the getModelChildren() method I call allModelObjects.addAll(((Diagram) getModel()).getMyNodes());, and getMyNodes() returns an unmodifiable EList. I found out when I tried to do something along the lines of ((Diagram) getModel()).getMyNodes().remove(getMyNode()) in the delete command, and it threw an UnsupportedOperationException... Hm.
EDIT 4
Er, somebody kill me please?
I've double-checked whether I'm handling the same Diagram object at all times, and while doing this I stumbled across a very embarassing thing:
The getModelChildren() method in DiagramEditPart in the last version read approx. like this:
#Override protected List<EObject> getModelChildren() {
List<EObject> allModelObjects = new ArrayList<EObject>();
EList<MyNode> nodes = ((Diagram) getModel()).getMyNodes();
for (MyNode node : nodes) {
if (node.getDiagram() != null); // ### D'Uh! ###
allModelObjects.add(node);
}
return allModelObjects;
}
I'd like to apologize for stealing everyone's time! Your suggestions were very helpful, and indeed helped my to finally track down the bug!
I've also learned a number of lessons, amongst them: Always paste the original code, over-simplifaction may cloak your bugs! And I've learned a lot about EMF, Adapter, and GEF. Still:
There is one semi-colon too many in line 5 of the following part of the code, namely after the if statement: if (node.getDiagram() != null);:
1 #Override protected List<EObject> getModelChildren() {
2 List<EObject> allModelObjects = new ArrayList<EObject>();
3 EList<MyNode> nodes = ((Diagram) getModel()).getMyNodes();
4 for (MyNode node : nodes) {
5 if (node.getDiagram() != null);
6 allModelObjects.add(node);
7 }
8 return allModelObjects;
9 }

GWT new EntityProxy in #OneToOne with another EntityProxy from server

I am just creating a new Proxy:
LayoutExampleRequest r = requestFactory.employeeRequest();
DepartmentProxy d = r.create(DepartmentProxy.class);
r.save(d);
departmentEditor.editProxy(d, r);
Then pass the Proxy and the Request(LayoutExampleRequest ) to my editor
driver.edit(proxy, request);
Until here ! everything works as espected. I can save Department objects with null EmployeeProxy. Now iam getting with a suggest box Proxys of EmployeeProxy from the server.
search = new SuggestBox(new SuggestOracle() {
#Override
public void requestSuggestions(final Request request,final Callback callback) {
System.out.println(request.getQuery());
//ignore less than 3
if(request.getQuery().length() > 3){
requestFactory.employeeRequest().search(request.getQuery()).fire(new Receiver<List<EmployeeProxy>>(){
#Override
public void onSuccess(List<EmployeeProxy> response) {
List<MySuggestion<EmployeeProxy>> suggestions = new ArrayList<MySuggestion<EmployeeProxy>>();
for(EmployeeProxy e:response){
MySuggestion<EmployeeProxy> suggestion = new MySuggestion<EmployeeProxy>();
suggestion.setModel(e,e.getFirstName(),e.getFirstName()+" "+e.getLastName());
suggestions.add(suggestion);
}
callback.onSuggestionsReady(request, new Response(suggestions));
}
});
}
}
});
MySuggestion is a wrapper class to handle the EmployeeProxy.
Now i want to add this EmployeeProxy to my DeparmentProxy since i have a #OneToOne on JPA.
search.addSelectionHandler(new SelectionHandler<SuggestOracle.Suggestion>() {
#Override
public void onSelection(SelectionEvent<Suggestion> event) {
MySuggestion<EmployeeProxy> s = (MySuggestion<EmployeeProxy>)event.getSelectedItem();
proxy.setSupervisor(s.getModel());
}
});
proxy is the EntityProxy for Department (I sent to my editor) driver.edit(proxy, request);
then i fire the driver:
departmentEditor.getDriver().flush().fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
Window.alert("Success");
// refresh the datagrid
Range range = dataGrid.getVisibleRange();
dataGrid.setVisibleRangeAndClearData(range, true); //1st way
// create a new DepartmentProxy to bind to the Editor.
createProxy();
// change button text
updateButton.setText("Save");
}
#Override
public void onConstraintViolation(Set<ConstraintViolation<?>> violations) {
for(ConstraintViolation v :violations){
Window.alert(v.getMessage()+" "+v.getPropertyPath());
}
}
#Override
public void onFailure(ServerFailure error) {
Window.alert(error.getMessage());
}
});
The problem is iam getting ConstraintViolations from the EmployeeProxy, is like the driver atach the EmployeeProxy but with null values.
(Iam validating my Entityes with JSR-330 )
Dont know how to make a relationship with a new Proxy with other taked from the server. in a #OneToOne relationship
Any help would be nice!
Thank you
/* UPDATE */
Something like this but with editor
final LayoutExampleRequest r = requestFactory.employeeRequest();
final DepartmentProxy d = r.create(DepartmentProxy.class);
d.setName("Name");
d.setService(Service.CONTRACT_MANAGMENT);
// get some random employee
requestFactory.employeeRequest().findById(1).fire(new Receiver<EmployeeProxy>() {
#Override
public void onSuccess(EmployeeProxy response) {
d.setSupervisor(response);
r.save(d).fire(new Receiver<DepartmentProxy>() {
#Override
public void onSuccess(DepartmentProxy response) {
Window.alert("Kidding me! why editor cant get it work =p?");
}
});
}
});
The problem was i put on my editor properties of the EmployeeProxy so when a user select the employeproxy would see information about it, so i delete them and then do the same and now works.
Is like GWT when detects properties from another proxy on the editor thinks you will fill it. And the line:
proxy.setSupervisor(s.getModel());
doesn't works.