Changing the binded value of a label? - java

This code binds a Label to an updating SimpleIntegerPropertyValue which is counting down from 10 - 1.
view.OVERALL_PROGRESS_LABEL.textProperty().bind(timeSeconds.divide(100).asString());
How can I bind specific values depending on what currently this timeSeconds value is? For instance if the value of timeSeconds > 500 then display "Greater" otherwise display "Less".
I have tried binding a method which returns an ObservableValue but it is not function correctly. (just manipulating the numbers to see if there is a change)
private void someMethod(){
view.OVERALL_PROGRESS_LABEL.textProperty().bind(test2());
}
private ObservableValue<? extends String> test2() {
ObservableValue<String> test;
if (timeSeconds.getValue() < 500){
test = timeSeconds.multiply(1000).asString();
} else {
test = timeSeconds.divide(1000).asString();
}
return test;
}

You can use Bindings to create bindings based on condition.
view.OVERALL_PROGRESS_LABEL.textProperty().bind(Bindings.when(timeSeconds.
greaterThan(500)).then("Greater").otherwise("Less"));

Related

Rewrite labels using methods

So to be more specific I want to be able to do "changeLabel(lableHere, textHere);"
but I don't know how to specify the label.
to simplify my request I want to go from
public static void method1(String TextToSet){
if(variable.isVisible()==false){
variable.setVisible(true);
variable.setText(TextToSet);
}else if(variable.isVisible()==true){
variable.setVisible(false);
}
}
public static void method2(String TextToSet){
if(variable2.isVisible()==false){
variable2.setVisible(true);
variable2.setText(TextToSet);
}else if(variable2.isVisible()==true){
variable2.setVisible(false);
}
}
or what I'm using currently:
public static void method(String VariableIWantToChange, String TextToSet){
if(VariableIWantToChange.equalsIgnoreCase("Variable1"){
if(Variable1.isVisible()==false){
Variable1.setVisible(true);
Variable1.setText(TextToSet);
}
else if(Variable1.isVisible()==true){
Variable1.setVisible(false);
}
}
if(VariableIWantToChange.equalIgnoreCase("Variable2"){
if(Variable2.isVisible()==false){
Variable2.setVisible(true);
Variable2.setText(TextToSet);
}
else if(Variable2.isVisible()==true){
Variable2.setVisible(false);
}
}
}
(that method becomes really long after just a few variables)
and instead, have one method that is something like this
public static void method( Labeltochange ,String TextToSet){
if(Labeltochange.isVisible()==false){
Labeltochange.setVisible(true);
Labeltochange.setText(TextToSet);
}
}
that allows you to change label by doing method(LabelToChange, TextToSet);
The real requirement here: you want to map a name (String) name to some UI element (maybe a JLabel, maybe a JButton, who cares).
The answer: use a n appropriate data structure, like a Map<String, JComponent>.
Simply fill that map at some point in time:
Map<String, JComponent> componentsByName = new HashMap<>(); ...
componentsByName.put("label1", labelA);
So, later on, you simply do a componentsByName.get() to retrieve ... a component by its "name". To then call whatever method you want to invoke on that object.
In other words: you absolutely should not get into the business of doing such if/else chains. Every time your UI gets a new thing, or an element gets removed, you have to update code in a zillion of places.

Work with Value from PropertyValueFactory in TableView - JavaFx

In my JavaFx project I have a scene with a few views. For the footer of the window I have a class extending from TableView:
public class FooterView extends TableView<Area> implements ViewTemplate {...}
This displays a Table with some data from a .csv-file.
When it comes to assigning the value of the specific presentationmodel property to the cells I do it like that:
TableColumn<Area,Double> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(new PropertyValueFactory<>("powerPerArea"));
this.setItems(filePM.getAreas()); //filePm is my filehandler
this.getColumns().addAll(powerColumn, other_columns);
getAreas() looks like this:
public List<Area> readCantonsFromFile() {
try (Stream<String> stream = getStreamOfLines(AREA_FILE_NAME)) {
return stream.skip(1)
.map(l -> new Area(l.split(DELIMITER, 12)))
.collect(Collectors.toList());
}
}
In the constructor of Area i set the properties. One of the properties is the mentioned powerPerArea
private final DoubleProperty powerPerArea = new SimpleDoubleProperty();
...
public void setPowerPerCanton(double powerPerCanton) {
this.powerPerCanton.set(powerPerCanton);
}
My question: Is there a way to change the value in the FooterView before the value is displayed? I tried something like this:
powerColumn.setCellValueFactory(Math.round(new PropertyValueFactory<>("powerPerArea")));
But it seems that I mixed up DoubleProperty, Double and ObservableDouble. Can I even modify the value in here?
The problem: I can not round the value in the setter because I add a double value in a loop through this function:
public void addPowerPerArea(double power){
double sum = getPowerPerCanton() + power;
setPowerPerCanton(sum);
}
And rounding the value in here would give me a wrong result. (rounding not precise enough). I need to do it in the end when all sums are added
You should use the cellValueFactory to determine which data are displayed in the cells: in this case the data returned by your PropertyValueFactory is the actual double value returned from powerPerAreaProperty().get(), which is exactly what you want.
If you want to control how the data are displayed, you should use a cellFactory. So to display the data in a particular format, including limiting the number of decimal places, you can do:
powerColumn.setCellFactory(tc -> new TableCell<Area, Double>() {
#Override
protected void updateItem(Double power, boolean empty) {
super.updateItem(power, empty);
if (empty) {
setText(null);
} else {
setText(String.format("%.0f", power.doubleValue()));
}
}
});
The point here is that you should not modify the data based on how you want to display it; the purpose of having both cellValueFactory and cellFactory is to separate the display of the data from the actual data itself.
An alternative to returning custom cells from a cellFactory would be to use a custom cellValueFactory to return the property formatted as string:
TableColumn<Area, String> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(cd -> cd.getValue().powerPerAreaProperty().asString(""%.0f""));
I see two ways to do this. You could:
use the setCellFactory method and in the updateItem method you format it. Should look something like this, haven't tested
powerColumn.setCellFactory(column -> {
return new TableCell<Area, Double>() {
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(Math.round(item), empty);
}
};
});
OR: You could make another property of your Area class that is bound to the existing powerOfArea property but returns the value rounded. I am sure that is somehow possible, you could just override some functions of a readOnlyDoubleProperty but there should be a better way. Maybe via DoubleBindings.

Unable to write an algorithm for filtering out items in a RecyclerView based on a long saved with each item

I have several items in a RecyclerView and each item has a long value saved with it. I'm using FastAdapter as the adapter for my RecyclerView.
Suppose there are 7 items in the RecyclerView with the long values: 11122, 12321, -98811, 8870, -88009, 3398, and -22113.
So, what I want to do is, I want to filter the items based on the above given long values using this logic:
if (l <= 1000) {
// show items with long value <=1000
} else if (l > 1000) {
// show items with long value >1000
}
I tried various things, but nothing worked out.
UPDATE 1: Items here are a sort of different data stored in CardView and then shown in RecyclerView. Each card contains different data, one of which are the above given long values. I want to filter the data based on these long values stored in each card based on the logic given above.
Please help me with this issue and suggest some algorithm or code with which I can achieve this.
With the amount of information given I can only suppose l is a foreign selector value which controls the items to be displayed inside the RecyclerView. Comment below if this is not the case, I will try to correct my answer.
I recommend implementing a custom ViewAdapter, sending in the list of items and the selector variable l using respective methods:
public class ItemsAdapter extends
RecyclerView.Adapter<ItemsAdapter.ItemViewHolder> {
private List<Long> mItemList;
private List<Long> mDisplayItems;
private boolean mAboveThousand = true;
public void setItemList(List<Long> list) {
mItemList = list;
updateDisplayItems();
}
public void setSelectionType(boolean aboveThousand) {
mAboveThousand = aboveThousand;
updateDisplayItems();
}
private updateDisplayItems() {
mDisplayItems.clear();
for(Long item: mItemList) {
if(/*check your contition*/) {
mDisplayItems.add(item);
}
}
notifyDataSetChanged(); //important
}
...
// Rest of implementation
}
Also, I have never used FastAdapter, but I suppose there must be some methods to override if you extend its class.
Update
Since, you are facing problems understanding the basics of using a ViewAdapter, I would recommend learning and implementing a custom ViewAdapter before using any library. Here's a extensive tutorial for how to implement ViewAdapter for RecyclerView.
Now, after you have implemented the ViewAdapter you can use my piece of code to filter out cards. Basically, what the code is doing is saving a list of all the required data inside mItemList, while mDisplayList is a list storing the items to be displayed, which is updated every-time mAboveThousand, which stores the user preference of above or below 1000, is set. Now this mDisplayList must be used to inflate data inside the RecyclerView.
Even your very basic code there would work. You can count the number of items in that range and return the number in that range. I suggest you try to do this without FastAdapter because the core concept of parsing the data based on a filter value is rightly perfectly solid. You can iterate the loop and count them, and you can iterate the loop and return the nth item.
If you do want to keep using FastAdapter, it has a built-in filter functionality (see point number 5 in the README of the project. Note that the filter method should be called after withFilterPredicate and not before as shown there).
EDIT - after you pointed out that I misunderstood you before - here is my updated proposed instructions:
You need to resolve the logics of which set you want to display (using the checkboxes in the dialog you mentioned in the comment) and pass that information onto the filter, for example:
boolean displayUnderThreshold = //put the logic here - true if you want <1000
fastAdapter.filter(Boolean.toString(displayUnderThreshold));
And where you set the adapter (before the above line is called) have:
final long threshold = 1000;
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<GRModeClass>() {
#Override
public boolean filter(GRModeClass item, CharSequence constraint) {
boolean displayUnderThreshold = new Boolean(constraint.toString());
return (displayUnderThreshold ^ (item.l<threshold)); //false to remove from list
}
});
Old answer From when I thought you wanted to filter the items according to their ms long values, using an external l long indicator:
In your code, assuming your app does get to the if you mentioned in the question when it should - remove the fastItemAdapter.clear(); and instead of the for loop with the if inside it write
fastItemAdapter.filter(Long.toString(l));
and somewhere before that, preferably where you set the adapter (most likely in the onCreate of MainActivity) add the following:
final long threshold = 1000;
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<GRModeClass>() {
#Override
public boolean filter(GRModeClass item, CharSequence constraint) {
long indicator = new Long(constraint.toString());
return (item.ms<threshold && indicator>=threshold) || (item.ms>=threshold && indicator<threshold) ;
}
});
(Assuming here that GRModeClass is your items' class and that the long ms is the long you referred to that should determine whether the )
I guess your class is like
public Class ListItem {
// .. Some other attributes
public long l;
}
Now I hope you've some function which is called when you're putting a filter in your RecyclerView. Let the function name is toggleFilter.
public void toggleFilter(long l) {
if(l <= 1000) {
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<Item>() {
#Override
public boolean filter(ListItem item, CharSequence constraint) {
if(item.l <= 1000) return true;
else return false;
}
});
} else if (l > 1000) {
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<Item>() {
#Override
public boolean filter(ListItem item, CharSequence constraint) {
if(item.l > 1000) return true;
else return false;
}
});
}
// Finally call notifyDataSetChanged
fastAdapter.notifyDataSetChanged();
}
You can filter while fetching from firebase.
l <= 1000
firebaseDatabase.child(key).orderByChild("long_value_key_in_firebase").endAt(1000);
l > 1000
firebaseDatabase.child(key).orderByChild("long_value_key_in_firebase").startAt(1000);

javafx bidirectional change notification won't work?

consider the following situation:
I am binding the string-property of a TextField to a float-property:
TextField txtField;
SimpleFloatProperty floatprop;
void bind(){
txtField.textProperty().bindBidiretional(floatprop, NumberFormat.getInstance);
}
Furthermore, I want to check whether the value is positive and I want to reset the value if necessary:
floatprop.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
SimpleFloatProperty prop = (SimpleFloatProperty)ov;
if(t.floatValue() <= 0)
prop.set(t1.floatValue());
else
prop.set(t.floatValue());
}
});
That does not work. No matter what I type into my textfield, the value is not checked, i.e negative values are not reset.
Edit: With a litte bit of distance, I also see why. On setting prop, the changed-event will be triggered for a handy endless recursion. How can I solve this issue?
For me, it worked to do this check at the beginning of changed:
if (t != null && t.equals(t1))
return;
Strangely, this way sometimes fails as well. Then, there is no other way than using a flag, like this:
MyListener implements ChangeListener<...>{
boolean fired;
public void changed(...){
if(fired)
return;
fired = true;
// Change the variable...
fired = false;
}
}
I don't like the latter way, but it looks like there is no alternative.

Howto write custom checks/validation for the play-framework

I try to write checks for the play-framework and see two different possibilities. I described both and want to know if my understanding is correct (So it's more a tutorial than a question, specially because I didn't get any response that I missed something).
So what possibilities exists.
The simple way: Extending the class Check:
Advantages: Easier to write, easier to read
Disadvantages: You can't parametrized the check, you can only define the message.
The advanced way: Writing an check based on OVal AbstractAnnotationCheck.
Advantages: You can parametrized the check and have a simpler to use annotation
Disadvantages: A little bit more complicated.
Before we have a look on the implementation I want to explain the messages. You can always set the message directly or use a key to refer the message in a message-properties. The last one is the cleaner and recommended way. Every validation get a least 1 parameter: The name of the property which isn't valid. So validation or check specific parameters are always referred with %i$s where i>1. The format of the message string should follows the rules of Formatter but I'm unsure if all features are supported. As far as I know only %s, %d and %f is supported togeter with positioning. So %[argument_index$][flags]conversion where conversion could only be s,d or f.
Lets have a look on two examples:
The simple way I used in my module for optimistic locking:
/**
* Check with proof if the version of the current edited object is lesser
* than the version in db.
* Messagecode: optimisticLocking.modelHasChanged
* Parameter: 1 the request URL.
* Example-Message: The object was changed. Reload and do your changes again.
*
*/
static class OptimisticLockingCheck extends Check {
/**
* {#inheritDoc}
*/
#Override
public boolean isSatisfied(Object model, Object optimisiticLockingViolatedValue) {
//The comparision of version was made in the setter. Here
//we only have to check the flag.
if (((VersionedModel) model).optimisiticLockingViolated) {
final Request request = Request.current();
//The following doesn't work in 1.0 but in 1.1 see https://bugs.launchpad.net/play/+bug/634719
//http://play.lighthouseapp.com/projects/57987-play-framework/tickets/116
//setMessage(checkWithCheck.getMessage(), request != null ? request.url : "");
setMessage("optimisticLocking.modelHasChanged", request != null ? request.url : "");
}
return !((VersionedModel) model).optimisiticLockingViolated;
}
}
You use this Check with the annotation #CheckWith(value=OptimisticLockingCheck.class, message="optimisticLocking.modelHasChanged")
So lets have a closer look how it works. The only thing we have to do is to extends the class play.data.validation.Check and overwrite the isSatisfied method. There you get your model and the value of the properties. All you have to do is to return true if everything is OK or false otherwise. In our case we want to set the current url as a parameter. This
can be easily done by calling setMessage(). We give the message or the message key which is defined in the messages properties and the parameters. Remember we only give 1 parameter but referred as with %2$s, because the first parameter is always the name of the property.
Now the complex way based on the Range-check of play:
First we need to define an Annotation
/**
* This field must be lower than and greater than.
* Message key: validation.range
* $1: field name
* $2: min reference value
* $3: max reference value
*/
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Constraint(checkWith = RangeCheck.class)
public #interface Range {
String message() default RangeCheck.mes;
double min() default Double.MIN_VALUE;
double max() default Double.MAX_VALUE;
}
and then the Check
#SuppressWarnings("serial")
public class RangeCheck extends AbstractAnnotationCheck<Range> {
final static String mes = "validation.range";
double min;
double max;
#Override
public void configure(Range range) {
this.min = range.min();
this.max = range.max();
setMessage(range.message());
}
public boolean isSatisfied(Object validatedObject, Object value, OValContext context, Validator validator) {
requireMessageVariablesRecreation();
if (value == null) {
return true;
}
if (value instanceof String) {
try {
double v = Double.parseDouble(value.toString());
return v >= min && v <= max;
} catch (Exception e) {
return false;
}
}
if (value instanceof Number) {
try {
return ((Number) value).doubleValue() >= min && ((Number) value).doubleValue() <= max;
} catch (Exception e) {
return false;
}
}
return false;
}
#Override
public Map<String, String> createMessageVariables() {
Map<String, String> messageVariables = new TreeMap<String, String>();
messageVariables.put("2-min", Double.toString(min));
messageVariables.put("3-max", Double.toString(max));
return messageVariables;
}
}
OK I think the annotation don't must be explained. Lets have look on the check. In this case it's extends net.sf.oval.configuration.annotation.AbstractAnnotationCheck. We have to write a configure-method where we get the annotation and can copy the parameters. Then we have to define our check. Which is analog to the implementation of the other check. So we only write our condition and return true or false, except one special line! If we used a parametrized message, we must call requireMessageVariablesRecreation(); in our method.
At least we must override the method createMessageVariables. Here we have to get a littlebit play-knowlegde (all the other stuff is described here). You put your messages into an map with a key and value, but play only takes the values (see ValidCheck.java in framework code). So it will be referenced by position. This is the reason I changed the implementation of the RangeCheck using TreeMap instead of HashMap. Furthermore I let the keys start with the index which they can referred.
So I hope this makes it more clear how to write custom validations/checks for play. I hope the description is correct. Therefor the question is my understanding correct?
At least your first example appears to be on the correct path. You can compare it to the documentation provided below, but I'd assume from the complexity of your example that you've already referred to it.
http://www.playframework.org/documentation/1.1/validation#custom
I don't know enough about the play framework to comment on the second example.

Categories

Resources