JavaFX Property's invalidate() method not called with binding - javafx

I'm trying to make a custom control in JavaFX using some binding feature. This is my problem: I have a class with a DoubleProperty that I use to calculate the position of an element inside my custom control. Here is the code:
public class CustomControl extends Region {
private DoubleProperty positionProperty;
public CustomControl() {
positionProperty= new DoublePropertyBase(0.0) {
#Override public Object getBean() { return CustomControl.this; }
#Override public String getName() { return "position"; }
#Override protected void invalidated() { updatePostion(); }
};
}
public DoubleProperty positionProperty() { return positionProperty; }
public double getPosition() { return positionProperty.get(); }
public void setPosition(double value) { positionProperty.set(value); }
private void updatePosition() {
double value = doubleProperty.get();
//compute the new position using value
}
}
In my application i have two CustomControls and i want that when i call the method setPosition() on the first of them, the second one updates the position of its component as well. To do so I binded the positionProperty of the two CustomControls like this:
CustomControl control1 = new CustomControl();
CustomControl control2 = new CustomControl();
control2.positionProperty.bind(control1.positionProperty);
Then when I call for example
control1.setPosition(50.0);
only the position of the component of control1 is updated, indeed when i call the method setPosition(), the method invalidated() of the positionProperty of control1 is actually called, but not the one of the positionProperty of contol2 as I espected. How shoul i achieve what i want? Thank you!
PS: I also noticed that using the method bindBidirectional() instead of bind() works, but shouldn't it work using only bind() too?
EDIT: example code is available here: https://luca_bertolini#bitbucket.org/luca_bertolini/customcontrolexample.git

JavaFX uses lazy evaluation for all bindings, which means that when a change occurs on your out.positionProperty object, the new value is not immediately taken into account. This happens later, if and only when the value is subsequently requested.
Try this:
out.positionProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(final Observable observable) {
// System.out.println("It must go now.");
}
});
and you will see that this time your code works as you want.

Related

Make TvInputService display an overlay

I'm trying to write a most simple TvInputService, that will display an overlay using TIF Companion Library.
The Live Channels application already recognizes my channel Simple Channel declared in my EpgSyncJobService subclass.
My Program is displayed in the Live Channels application's EPG as currently presented one.
However, all I can see is the blue spinner, as if the channel didn't "tune".
What am I doing wrong?
public class MyTvInputService extends BaseTvInputService {
#Override
public final BaseTvInputService.Session onCreateSession(String inputId) {
BaseTvInputService.Session session = new MyTvInputSession(this, inputId);
session.setOverlayViewEnabled(true);
return super.sessionCreated(session);
}
class MyTvInputSession extends BaseTvInputService.Session {
public MyTvInputSession(Context context, String inputId) {
super(context, inputId);
}
#Override
public View onCreateOverlayView() {
mTextView = new TextView(MyTvInputService.this);
mTextView.setText("This is an example overlay");
mTextView.setTextColor(Color.RED);
mTextView.setVisibility(View.VISIBLE);
mTextView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
return mTextView;
}
#Override
public boolean onPlayProgram(Program program, long startPosMs) {
return true;
}
#Override
public boolean onPlayRecordedProgram(RecordedProgram recordedProgram) {
return true;
}
#Override
public void onSetCaptionEnabled(boolean enable) {}
#Override
public TvPlayer getTvPlayer() { return null; }
}
private TextView mTextView;
}
According to TvInputService.Session.notifyVideoAvailable() documentation:
The TV input service must call this method as soon as the content
rendered onto its surface is ready for viewing. This method must be
called each time onTune(Uri) is called.
So it is enough to call notifyVideoAvailable() in BaseTvInputService.Session.onTune(Uri) method override, like so:
#Override
public boolean onTune(Uri channelUri) {
notifyVideoAvailable();
return true;
}
I must say, that using the TV Companion Library's BaseTvInputService makes finding such issues harder than when using bare TvInputService, as an onTune() method is non-abstract in BaseTvInputService.Session.

Xamarin forms - Passing data between pages & views

I have a xamarin forms app.
There are 2 classes with data, one of the pages is filling the data.
The problem is: I'm creating new view, that should use data from both classes.
The only way i'm familiar with is to set a class as a bindingContext to pass data between pages, and it's working fine with ONE class, because apparently there couldn't be 2 bindingContext at the same time.
EXAMPLE:
1st class (all the classes are filled on the previous page. just accept that they are filled)
public class Buildings : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _id;
public string Id
{
get { return _id; }
set
{
_id = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Id"));
}
}
}
2nd class
public class Flats : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _num;
public string Num
{
get { return _num; }
set
{
_num = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Num"));
}
}
}
new view:
public partial class HouseView
{
private Flats _flats;
private Buildings _buildings;
public HouseView()
{
InitializeComponent();
}
private void HouseView_OnBindingContextChanged(object sender, EventArgs e)
{
var building = BindingContext as Building;
//var flat = BindingContext as Flat;
//_flat = flat;
_building = building;
var buildingInfo = await Rest.GetHouseInfo(_building.Id, _flat.Num); //function that will return info on a current house;
// rest code
}
}
Maybe there is no need for binding context, because i'm just passing the parameters, not changing them in a view? I guess the solution can be pretty simple, and i cant figure it out....
What you are missing is understanding the concept of ViewModel, and it's relation with the views.. In this case what you need is a 3rd class (ViewModel) that handles your 2 previous class:
public class HouseViewModel : INotifyPropertyChanged
{
public Flats Flats { get; set; }
private Buildings Buildings { get; set; }
}
Also using OnBindingContextChanged is just messy and will take some performance from your app .. try to prepare your data before on your VM, so the view knows as little as possible in how to get/handle data.
There is simple way to transfer data between pages in Xamarin forms.
Add new class to the main project called Transporter.cs, and this class should be static.
Inside this class, add the variables to transfer data between other pages; then you can simply access any variable by using Transporter.Variable.
Example:
public static Transporter
{
public static string x;
}
> Now, in each page, you can simply access (set or get) the value:
Transporter.x=MyName.Text;
>In another page:
MySecondName.Text=Transporter.x;
Note: MyName is an entry field in the first page, and MySecondName is an entry field in the second page.
Also, you can define any type of variables like (Lists, int, object... etc).

Javafx and the Observer pattern - updating a UI

I am trying to implement the Observer pattern in a JavaFx application. I've never asked a question here but this is driving me a bit crazy.
Essentially I'm trying to use the Observer pattern to monitor a class that's parsing a file of phone numbers, and update the UI automatically as the file is parsed.
Before I get to my questions, here is my code:
Abstract class Observer.java
public abstract class Observer
{
public PhoneBook numbers;
public abstract void update();
}
I have a class that implements this:
public class PhoneBookObserver extends Observer {
public PhoneBookObserver(PhoneBook numbers)
{
this.numbers = numbers;
this.numbers.attach(this);
}
#Override
public void update()
{
System.out.println(""NUMBER - : " + numbers.GetNumbers());
}
}
In the class doing the parsing, I've created a new PhoneBookObserver
public PhoneBook ParsePhoneBook()
{
PhoneBook nums= new PhoneBook();
PhoneBookObserver p = new PhoneBookObserver(nums);
// ... Parsing of file - works fine
return nums;
}
Currently this runs and my println from update() in PhoneBookObserver is output.
My questions are:
Can the update method of PhoneBookObserver update my UI for me? How would it access JavaFx elements in my controller?
Can I just make my controller an observer, override update() and use that to update my UI elements from within my controller? Is that bad?
To directly answer your question, I would probably implement the Observer as an inner class in the controller. Then it has access to everything in the controller.
Assuming here PhoneBook defines a method of the form
public List<PhoneNumber> getPhoneNumbers() ;
then you could do:
public class Controller {
#FXML
private ListView<PhoneNumber> phoneNumberList ;
private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere
public void initialize() {
numbers.attach(new PhoneBookObserver(numbers));
// ...
}
private class PhoneBookObserver extends Observer {
PhoneBookObserver(PhoneBook numbers) {
this.numbers = numbers ;
}
#Override
public void update() {
phoneNumberList.getItems().setAll(numbers.getPhoneNumbers());
}
}
}
Note that in
public abstract class Observer
{
public PhoneBook numbers;
public abstract void update();
}
the field numbers really serves no purpose, as the only method doesn't use it. So you could remove it (subclasses can define such a field if they need). Then you may as well make it an interface, and since it only has one method, it's a #FunctionalInterface:
#FunctionalInterface
public interface Observer {
public void update() ;
}
and now it can be implemented with a lambda expression, so the implementation is so thin that you basically stop having any issues with "accessing the UI":
public class Controller {
#FXML
private ListView<PhoneNumber> phoneNumberList ;
private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere
public void initialize() {
numbers.attach(() -> phoneNumberList.getItems().setAll(numbers.getPhoneNumbers());
// ...
}
}
Finally, note that JavaFX Properties and observable lists basically already provide an implementation of the observer pattern, so you're pretty much reinventing the wheel here. You could just have
public class PhoneBook {
private final ObservableList<PhoneNumber> numbers;
public ObservableList<PhoneNumber> getPhoneNumbers() {
return numbers ;
}
}
and then
public class Controller {
#FXML
private ListView<PhoneNumber> phoneNumberList ;
private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere
public void initialize() {
phoneNumberList.setItems(numbers.getPhoneNumbers());
}
}
and the list view will observe the (already-observable) list of numbers for you. There is no real need for your Observer or PhoneBookObserver.

Add action to a cancel button in an editable grid in vaadin

I'm working with an editable Grid with Vaadin 7. When a row is in edit mode, it shows two buttons: save and cancel.
(Just in case, the image was taken from here Book of Vaadin)
With the:
grid.getEditorFieldGroup().addCommitHandler(new CommitHandler() {
private static final long serialVersionUID = 1L;
#SuppressWarnings("unchecked")
#Override
public void preCommit(CommitEvent commitEvent) throws CommitException{}
#Override
public void postCommit(CommitEvent commitEvent) throws CommitException{}
});
I can do something in the save action.
But, can I do something like that with the cancel action?
Thank you.
This is a serious deficiency of the component. According to the forum, they're working on it, but for the time being it seems that the most straightforward way is to extend the Grid component and override the doCancelEditor method. Here's a snippet:
public class MyGrid extends Grid {
protected Object newRow;
#Override
protected void doCancelEditor() {
super.doCancelEditor();
getContainerDataSource().removeItem(newRow);
setEditorEnabled(false);
}
public void setNewRow(Object newRow) {
this.newRow = newRow;
}
Note that you have to tell the MyGrid object when you create the row. Also, note that you're extending the server side, so you don't have to alter the client (widget code), but you do need to refer to the new component in your UI design.
Actually, saveEditor() should be also overridden, as doCancelEditor() seems to be invoked on save action, too. My code:
public class MyGrid extends Grid {
private boolean addingMode = false;
private JPAContainer<SomeEntity> container;
private Object recentlyAddedItemID;
public MyGrid(Indexed indexed) {
container = indexed;
}
#Override
protected void doCancelEditor() {
Object id = getEditedItemId();
super.doCancelEditor();
if (addingMode) {
getContainerDataSource().removeItem(id);
recentlyAddedItemID = null;
}
addingMode = false;
}
#Override
public void saveEditor() throws FieldGroup.CommitException {
if (addingMode) recentlyAddedItemID = getEditedItemId();
addingMode = false;
super.saveEditor();
}
public Object getRecentlyAddedItemID() {
return recentlyAddedItemID;
}
public void addNewElement(SomeEntity entity) {
addingMode = true;
editItem(container.addEntity(entity));
}
}
MyGrid grid = new MyGrid(JPAContainerFactory.make(SomeEntity.class, entityManager));
grid.addNewElement(new SomeEntity());
/*
if we want to know the new items's ID (actually the new primary key
in case of JPAContainer), we can check it by:
*/
Object id = grid.getRecentlyAddedItemID();
/*
returns null if editor was cancelled and finally nothing new was persisted
*/

Toolbar with SearchView temporary filtering RecyclerView

I need to implement search functionality inside my android app which uses toolbar, SlidingTabLayout and ViewPager that holds fragments. Inside each fragment there is a RecyclerView with list of items.
RecyclerView data is static defined in separate class (DataAccess.java) and those lists are updated and RecyclerView gets refreshed just by calling (without passing new data)
mRecyclerView.getAdapter().notifyDataSetChanged();
Is there any simple way to temporary filter RecyclerView without changing the data and after the user presses return button inside Toolbar to remove the filter and show inital list.
Before pressing Search icon inside toolbar menu:
So when the user is typing "Josip..." the filter will be active
and after he presses the X button in SearchView the user will get the same data as before without filter.
#Override
public boolean onQueryTextChange(String newText) {
// filter data (temporary remove all items from DataAccess.list that don't .startsWith(newText)
}
#Override
public boolean onQueryTextSubmit(String query)
// Doesn't help if I revert deleted items here
}
#Override
public boolean onQueryTextSubmit(String query){
((ItemAdapter) myRecList.getAdapter()).setFilter(query)
}
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {
private List<String> visibleObjects;
private List<String> allObjects;
.....
public void flushFilter(){
visibleObjects=new ArrayList<>();
visibleObjects.addAll(allObjects);
notifyDataSetChanged();
}
public void setFilter(String queryText) {
visibleObjects = new ArrayList<>();
constraint = constraint.toString().toLowerCase();
for (String item: allObjects) {
if (item.toLowerCase().contains(queryText))
visibleObjects.add(item);
}
notifyDataSetChanged();
}
}
I wanted to add as comment but due to less reputation...I am answering post.
This method works fine if (item.toLowerCase().contains(queryText)) but what to do if match is not found in first iteration.then it will go in else part without looping throughout allObjects list...
for (RouteByATMList.Route_ATM item: Main_ATMItemList)
{
if (item.ATMNumber.contains(queryText)) {
visibleObjects.add(item);
}else {
Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
break;
}
}
I got the answer from my superior ,hope it helps.
public void setFilter(String queryText) {
visibleObjects = new ArrayList<>();
for (RouteByATMList.Route_ATM item: Main_ATMItemList)
{
if (item.ATMNumber.contains(queryText))
{
visibleObjects.add(item);
}
}
if(visibleObjects.size()==0){
Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
}
notifyDataSetChanged();
Log.e("dataset changed","dataset changed");
}
I don't oppose the given and accepted answer. There is a room for possible performance pitfalls. One should make use of Filterabe interface. Having this implemented will behave as ol'good ListView that did the filtering asynchronously. Then all you need is to write your own Filter and instantiate it in the overridden getFilter() method;
In my case I used Filter to sort an adapter of many (yeah, many many) items. It was junky sorting it on UI-thread.
So I had this abstract class
public abstract class BaseFilterableRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> implements Filterable {
private Context mContext;
public BaseFilterableRecyclerViewAdapter(Context context) {
this.mContext = context;
}
public abstract void sort(SortingFilter.Sort sortingStrategy);
//...Other methods
}
And the class that inherit:
public class ItemAdapter extends BaseFilterableRecyclerViewAdapter<RecyclerView.ViewHolder>{
//... RecyclerView methods
#Override
public Filter getFilter() {
return new SortingFilter(mData) {
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.values != null) {
int last = mData.size();
mData = (List<Product>) results.values;
notifyDataSetChanged();
}
}
};
}
#Override
public void sort(SortingFilter.Sort sortingStrategy) {
getFilter().filter(sortingStrategy.toString());
}
}

Resources