RxAndroid onNext being called multiple times - retrofit2

I am calling an api via retrofit with RxAndroid. Here's how the method is defined
#GET("product")
Observable<BaseResponse<List<Product>>> getProducts(#Query("lang") String lang,
#Query("category_id") String category_id,
#Query("start") String start,
#Query("count") String count);
This api returns the list of Product. I wanted to process each Product object inside list to check if that product exists in local db & set the boolean in product object. So I did it like this,
public Observable<List<Product>> getProductList(String catId, int start, int count) {
final List<Product> products = new ArrayList<>();
return RestClient.callApiWrapper(mContext, true, null,
RestClient.get().getProducts("en", catId, "" + start, "" + count))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.flatMapIterable(new Func1<BaseResponse<List<Product>>, Iterable<Product>>() {
#Override
public Iterable<Product> call(BaseResponse<List<Product>> listBaseResponse) {
Log.e("TAG", "flatMapIterable called");
return listBaseResponse.getData();
}
})
.map(new Func1<Product, List<Product>>() {
#Override
public List<Product> call(Product product) {
Log.e("TAG", "map called => " + products.size());
products.add(checkIfProductAddedToCart(product));
return products;
}
});
}
I have subscribed to getProductList where I need the processed list in this way:
mProductListDataModel.getProductList(mCategoryId, mStartOffset, mCount)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Product>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Product> products) {
Log.e("TAG", "onNext => " + products.size());
if(mOnProductListLoaded != null)
mOnProductListLoaded.onProductListLoaded(products);
}
});
Now the problem is, the API actually returns list of 5 products, but onNext is being called 5 times each time with increasing size upto 40 products. Please let me know where I am doing wrong.

I have changed few things for now, which are as below which works for me:
changed getProductList to return Product instead of List<Product>
so it looks like
public Observable<Product> getProductList(String catId, int start, int count) {
//final List<Product> products = new ArrayList<>();
return RestClient.callApiWrapper(mContext, true, null,
RestClient.get().getProducts("en", catId, "" + start, "" + count))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<BaseResponse<List<Product>>, Observable<Product>>() {
#Override
public Observable<Product> call(BaseResponse<List<Product>> listBaseResponse) {
return Observable.from(listBaseResponse.getData());
}
})
.map(new Func1<Product, Product>() {
#Override
public Product call(Product product) {
//Log.e("TAG", "map called => " + products.size());
//products.add(checkIfProductAddedToCart(product));
//return products;
return checkIfProductAddedToCart(product);
}
});
}
and changed following where this observable was subscribed:
final List<Product> products = new ArrayList<>();
mProductListDataModel.getProductList(mCategoryId, mStartOffset, mCount)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Product>() {
#Override
public void onCompleted() {
Log.e("TAG", "onCompleted => " + products.size());
if (mOnProductListLoaded != null)
mOnProductListLoaded.onProductListLoaded(products);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Product product) {
products.add(product);
}
});
However, I am still not sure if this is the right way of doing this. Please let me know if this is not the right way to do so.

Related

I Want To Itemonclicklister in Fragment on Spinner

This Is Main Fragment
Fragment:
private void getStock() {
dialog.show();
Retrofit retrofit = RetrofitClient.getRetrofitInstance();
apiInterface api = retrofit.create(apiInterface.class);
Call<List<Blocks>>call = api.getVaccineBlocks();
call.enqueue(new Callback<List<Blocks>>() {
#Override
public void onResponse(Call<List<Blocks>>call, Response<List<Blocks>> response) {
if (response.code() == 200) {
block = response.body();
spinnerada();
dialog.cancel();
}else{
dialog.cancel();
}
}
#Override
public void onFailure(Call<List<Blocks>> call, Throwable t) {
dialog.cancel();
}
});
}
private void spinnerada() {
String[] s = new String[block.size()];
for (int i = 0; i < block.size(); i++) {
s[i] = block.get(i).getBlockName();
final ArrayAdapter a = new ArrayAdapter(getContext(), android.R.layout.simple_spinner_item, s);
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//Setting the ArrayAdapter data on the Spinner
spinner.setAdapter(a);
}
}
This Is Blocks Model
model:
package com.smmtn.book.models;
import java.io.Serializable;
public class Blocks implements Serializable {
public String id;
public String blockName;
public String blockSlug;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBlockName() {
return blockName;
}
public void setBlockName(String blockName) {
this.blockName = blockName;
}
public String getBlockSlug() {
return blockSlug;
}
public void setBlockSlug(String blockSlug) {
this.blockSlug = blockSlug;
}
}
here i need onitemclick with blockslug please any one can help, am new to android so i need some example.when on click i want take blockslug and load another method with that blockslug,like will get data from u "http://example.com/block/"+blockslug
i want to get blockslug from selected block
i hope guys i will get help
and sorry for my bad English,
First of all, you need to implement setOnItemSelectedListener. Refer to this https://stackoverflow.com/a/20151596/9346054
Once you selected the item, you can call them by making a new method. Example like below
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
Toast.makeText(parent.getContext(),
"OnItemSelectedListener : " + parent.getItemAtPosition(pos).toString(),
Toast.LENGTH_SHORT).show();
final String itemSelected = parent.getItemAtPosition(pos).toString();
showBlockSlug(itemSelected);
}
And then, at the method showBlockSlug() , you can call Retrofit.
private void showBlockSlug(final String blockslug){
final String url = "http://example.com/block/"+ blockslug;
//Do your stuff...
}

Trouble with logic flow for filtering search method on CS50 Pokedex

The app compiles fine but initially it shows nothing in the list. When I use the search bar, it doesn't display my filtered information and when I get rid of search, the entire list is finally display. Any help would be really appreciated, this is my first time ever coding in Java.
Here is my adapter code.
public class PokedexAdapter extends RecyclerView.Adapter<PokedexAdapter.PokedexViewHolder> implements Filterable {
public static class PokedexViewHolder extends RecyclerView.ViewHolder {
public LinearLayout containerView;
public TextView textView;
PokedexViewHolder(View view) {
super(view);
containerView = view.findViewById(R.id.pokedex_row);
textView = view.findViewById(R.id.pokedex_row_text_view);
containerView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Pokemon current = (Pokemon) containerView.getTag();
Intent intent = new Intent(v.getContext(), PokemonActivity.class);
intent.putExtra("name", current.getName());
intent.putExtra("url", current.getUrl());
v.getContext().startActivity(intent);
}
});
}
}
private List<Pokemon> pokemon = new ArrayList<>();
private RequestQueue requestQueue;
private List<Pokemon> filteredPokemon = new ArrayList<>();
PokedexAdapter(Context context) {
requestQueue = Volley.newRequestQueue(context);
loadPokemon();
}
public void loadPokemon() {
String url = "https://pokeapi.co/api/v2/pokemon?limit=365";
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray results = response.getJSONArray("results");
for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);
String name = result.getString("name");
pokemon.add(new Pokemon(
name.substring(0, 1).toUpperCase() + name.substring(1),
result.getString("url")
));
}
notifyDataSetChanged();
} catch (JSONException e) {
Log.e("cs50", "Json error", e);
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("cs50", "Pokemon list error");
}
});
requestQueue.add(request);
}
#NonNull
#Override
public PokedexViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.pokedex_row, parent, false);
return new PokedexViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull PokedexViewHolder viewholder, int position){
Pokemon results = pokemon.get(position);
viewholder.textView.setText(results.getName());
viewholder.containerView.setTag(results);
}
#Override
public int getItemCount() {
return filteredPokemon.size();
}
#Override
public Filter getFilter() {
return new PokemonFilter();
}
private class PokemonFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
// implement your search here
FilterResults results = new FilterResults();
if (constraint == null || constraint.length() == 0) {
//No filter implemented return whole list
results.values = pokemon;
results.count = pokemon.size();
}
else {
List<Pokemon> filtered = new ArrayList<>();
for (Pokemon pokemon : filtered) {
if (pokemon.getName().toUpperCase().startsWith(constraint.toString())) {
filtered.add(pokemon);
}
}
results.values = filtered;
results.count = filtered.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredPokemon = (List<Pokemon>) results.values;
notifyDataSetChanged();
}
}
}
I really am not sure what is going on and given my knowledge of the subject, you could really help with understanding the logic better. Please let me know if there is any other information you would like from me about the code.
You might have already solved it and finished the Android tracks but this was the only thing I changed and it seemed to work after that.
for (Pokemon pokemon : pokemon) {
if (pokemon.getName().toUpperCase().startsWith(constraint.toString().toUpperCase()) {
filtered.add(pokemon);
}
}

JavaFX: Custom traversing with key combinations

I have a table with one editing combobox and three editable text fields,
editable fields create like this:
public class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
textField.requestFocus();
textField.selectAll();
}
});
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
} else if (t.getCode() == KeyCode.TAB) {
commitEdit(textField.getText());
TableColumn nextColumn = getNextColumn(!t.isShiftDown());
if (nextColumn != null) {
getTableView().edit(getTableRow().getIndex(), nextColumn);
}
}
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue && textField != null) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
/**
*
* #param forward true gets the column to the right, false the column to the left of the current column
* #return
*/
private TableColumn<Person, ?> getNextColumn(boolean forward) {
List<TableColumn<Person, ?>> columns = new ArrayList<>();
for (TableColumn<Person, ?> column : getTableView().getColumns()) {
columns.addAll(getLeaves(column));
}
//There is no other column that supports editing.
if (columns.size() < 2) {
return null;
}
int currentIndex = columns.indexOf(getTableColumn());
int nextIndex = currentIndex;
if (forward) {
nextIndex++;
if (nextIndex > columns.size() - 1) {
nextIndex = 0;
}
} else {
nextIndex--;
if (nextIndex < 0) {
nextIndex = columns.size() - 1;
}
}
return columns.get(nextIndex);
}
private List<TableColumn<Person, ?>> getLeaves(TableColumn<Person, ?> root) {
List<TableColumn<Person, ?>> columns = new ArrayList<>();
if (root.getColumns().isEmpty()) {
//We only want the leaves that are editable.
if (root.isEditable()) {
columns.add(root);
}
return columns;
} else {
for (TableColumn<Person, ?> column : root.getColumns()) {
columns.addAll(getLeaves(column));
}
return columns;
}
}
}
And editable combo column create like this:
public static void createEditingComboColumn(final TableColumn<DUMMY_PurchaseOrderLine, String> Column, final ObservableList<String>comboData, final ObservableList<DUMMY_PurchaseOrderLine> Pdata) {
Column.setCellFactory(new Callback<TableColumn<DUMMY_PurchaseOrderLine,String>,TableCell<DUMMY_PurchaseOrderLine,String>>(){
#Override
public TableCell<DUMMY_PurchaseOrderLine, String> call(TableColumn<DUMMY_PurchaseOrderLine, String> p) {
final TableCell<DUMMY_PurchaseOrderLine, String> cell = new TableCell<DUMMY_PurchaseOrderLine, String>(){
#Override
public void updateItem(String item, boolean empty) {
if(item!=null){
final ComboBox editableComboBox = new ComboBox(comboData);
editableComboBox.setEditable(true);
editableComboBox.setMaxWidth(Double.MAX_VALUE);
Platform.runLater(new Runnable() {
#Override
public void run() {
editableComboBox.requestFocus();
}
});
setGraphic(editableComboBox);
}
}
};
cell.addEventFilter(KeyEvent.KEY_PRESSED,new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent t) {
activeRow = cell.getIndex();
if(t.getCode() == KeyCode.TAB||t.getCode() ==KeyCode.RIGHT){
cell.getTableView().edit(cell.getTableRow().getIndex(),cell.getTableView().getColumns().get(1) );
}
}
});
return cell;
}
});
}
Problem: when I press TAB/Right arrow key from any column work properly ,but when I press SHIFT+Tab /left arrow from second column focus goes out. How I focus first column (editing combo) from second column using keyboard?
Thanks....
The condition below in key event handler
if (t.getCode() == KeyCode.TAB)
will handle "TAB" + Any modifier, which means it will handle "Ctrl+TAB", "Alt+TAB", "Shift+Alt+TAB" etc. The same is true for "Right" key. So to handle the "TAB" alone only, the preferred way is to define KeyCodeCombination.
Your posted code is partial, not executable ie. not SSCCE. As I understand your problem lays in key event handling. Because of this I wrote a completely different code but demonstrates the explanation mentioned above. See example, and try to traverse forward with key combination different from "TAB" or "RIGHT". Try with "Ctrl+TAB" or "Alt+RIGHT". It will not work for them, as expected.
public class TraverseDemo extends Application {
#Override
public void start(Stage primaryStage) {
final TextField textField1 = new TextField();
final TextField textField2 = new TextField();
final TextField textField3 = new TextField();
// Diasble all traversals since we will manage them manually, for only textField2.
textField1.setFocusTraversable(false);
textField2.setFocusTraversable(false);
textField3.setFocusTraversable(false);
// Define key combinations for traversals.
final KeyCombination kcTab = KeyCodeCombination.valueOf("TAB");
final KeyCombination kcShiftTab = KeyCodeCombination.valueOf("Shift+TAB");
final KeyCombination kcRight = KeyCodeCombination.valueOf("RIGHT");
final KeyCombination kcLeft = KeyCodeCombination.valueOf("LEFT");
// Default initial focused textfield
requestFocus(textField2);
textField2.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (kcTab.match(t) || kcRight.match(t)) {
System.out.println("traverse forward");
requestFocus(textField3);
} else if (kcShiftTab.match(t) || kcLeft.match(t)) {
System.out.println("traverse backward");
requestFocus(textField1);
}
}
});
StackPane root = new StackPane();
root.getChildren().add(HBoxBuilder.create().spacing(10).children(textField1, textField2, textField3).build());
Scene scene = new Scene(root, 300, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
private void requestFocus(final Node node) {
Platform.runLater(new Runnable() {
#Override
public void run() {
node.requestFocus();
}
});
}
public static void main(String[] args) {
launch(args);
}
}

javafx background tasks: call service in another service

I Would like to execute a background task which executes multiple background tasks. What i am actually trying to do is execute a background process which executes some code for every Object in a list, and does it within a fixed thread pool. So, for example, i have 100 users in the list and i am executing code for each of them concurrently but no more than 5 at the same time.
Therefore i am using two service/task pairs: One service/task for executing on the whole list of users, this service uses a fixed thread pool as its executor and executes a series of second service/task pairs for every user in the list.
like in the following example:
class MainService extends Service<List<User>> {
private List<User> users;
public MainService(List<User> users) { this.users=users; }
protected Task<List<User>> createTask(){
return new MainTask(this.users)
}
}
class Maintask extends Task<List<User>> {
private List<User> users;
private Executor executor;
public MainTask(List<User> users) {
this.users=users;
this.executor=Executors.newFixedThreadPool(5);
}
protected List<User> call() throws Exception {
for (User user : this.users) {
System.out.println("Starting single service");
SingleService service=new SingleService(user)
service.setExecutor(this.executor);
service.start();
System.out.println("Single service started");
}
}
}
class SingleService extends Service<User> {
private User user;
public SingleService(User user) { this.user=user; }
protected Task<User> createTask() {
return new SingleTask(this.user)
}
}
class SingleTask extends Task<User> {
private User user;
public SingleTask(User user) { this.user=user; }
protected User call() throws Exception() {
// Do some work
}
}
The code executes up to the moment when first "starting single service" is printed, the "single service started" message is not being printed at all. As far as i see the SingleService is started, but its createTask() is not being executed at all. Am i making some mistake here?
Well, I am trying to do the same thing, in different context... launch multiple services from a single main Service.
I've overridden all methods of Service, and this is my printout:
Service must only be used from the FX Application Thread {from onFailed() method}
Service does not run on FX thread, but can only be called from the FX thread.
Therefore, all Services and Tasks called within a Service or Task will not be executed.
This is my work around:
public class FXComponentImporter extends Service<Void> implements JarImporter {
//Scanner<T> = Service<List<Class<T>>>
private Scanner<Node> nodeScanner = null;
private Scanner<Paint> paintScanner = null;
private Scanner<Animation> animationScanner = null;
private static int nodeCount = 0, paintCount = 0, animationCount = 0;
private final ObservableMap<String, Class<?>> foundComponents = FXCollections.observableHashMap();
public FXComponentImporter() {
this(null);
}
public FXComponentImporter(File file) {
if (file != null) {
try {
this.nodeScanner = new Scanner<>(file, Node.class);
this.paintScanner = new Scanner<>(file, Paint.class);
this.animationScanner = new Scanner<>(file, Animation.class);
} catch (IOException ex) {
Logger.getLogger(FXComponentImporter.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
File f = importJar();
try {
this.nodeScanner = new Scanner<>(f, Node.class);
this.paintScanner = new Scanner<>(f, Paint.class);
this.animationScanner = new Scanner<>(f, Animation.class);
} catch (IOException ex) {
Logger.getLogger(FXComponentImporter.class.getName()).log(Level.SEVERE, null, ex);
}
}
this.scanningDone.bind(this.nodeScanningDone.and(this.paintScanningDone.and(this.animationScanningDone)));
this.scanningDone.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
if(scanningDone.isBound()){
scanningDone.unbind();
}
start();
scanningDone.removeListener(this);
}
}
});
startScanning();
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
Map<String, Class<?>> map = new HashMap<>();
List<Class<Node>> nodes = new ArrayList<>();
List<Class<Paint>> paints = new ArrayList<>();
List<Class<Animation>> anims = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(1);
//Platform needed due to Service only accessed from FX thread
Platform.runLater(() -> {
try {
//FX Stuff done here
nodes.addAll(nodeScanner.getMatchingClasses());
paints.addAll(paintScanner.getMatchingClasses());
anims.addAll(animationScanner.getMatchingClasses());
} finally {
latch.countDown();
}
});
latch.await();
this.updateMessage("Scanning for Nodes ... ");
nodes.stream().forEach(n -> {
if(n != null){
map.putIfAbsent(n.getSimpleName(), n);
}
nodeCount++;
});
this.updateMessage("Found : " + nodeCount + " Nodes ... ");
this.updateMessage("Scanning for Paints ... ");
paints.stream().forEach(p -> {
if(p != null){
map.putIfAbsent(p.getSimpleName(), p);
}
paintCount++;
});
this.updateMessage("Found : " + paintCount + " Paints ... ");
this.updateMessage("Scanning for Animations ... ");
anims.stream().forEach(a -> {
if(a != null){
map.putIfAbsent(a.getSimpleName(), a);
}
animationCount++;
});
this.updateMessage("Found : " + animationCount + " Animations ... ");
foundComponents.putAll(map);
return null;
}
};
}
#Override
protected void executeTask(Task<Void> task) {
super.executeTask(task);
System.out.println(getClass().getSimpleName() + " is Executing " + task.getTitle());
}
#Override
protected void cancelled() {
super.cancelled();
System.out.println(getClass().getSimpleName() + " was Cancelled ... ");
}
#Override
protected void running() {
super.running();
System.out.println(getClass().getSimpleName() + " is Running ... ");
}
#Override
protected void ready() {
super.ready();
System.out.println(getClass().getSimpleName() + " is Ready! ... ");
}
#Override
protected void scheduled() {
super.scheduled();
System.out.println(getClass().getSimpleName() + " is Scheduled ... ");
}
#Override
protected void failed() {
super.failed();
System.out.println(getException().getMessage());
}
#Override
protected void succeeded() {
super.succeeded();
System.out.println("Importing Succeeded ... with: " + foundComponents.entrySet().size() + " results.\n");
foundComponents.forEach((s, c) -> {
System.out.println(c.getSuperclass().getSimpleName() + " >> " + s + " : " + c.getSimpleName());
});
}
#Override
public void restart() {
super.restart();
System.out.println(getClass().getSimpleName() + " is Restarting ... ");
}
private void startScanning() {
nodeScanner.stateProperty().addListener(nsl);
paintScanner.stateProperty().addListener(psl);
animationScanner.stateProperty().addListener(asl);
nodeScanner.start();
paintScanner.start();
animationScanner.start();
}
private final BooleanProperty scanningDone = new SimpleBooleanProperty(false);
private final BooleanProperty nodeScanningDone = new SimpleBooleanProperty(false);
private final BooleanProperty paintScanningDone = new SimpleBooleanProperty(false);
private final BooleanProperty animationScanningDone = new SimpleBooleanProperty(false);
private final ChangeListener nsl = new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
if (newValue.equals(State.SUCCEEDED)) {
nodeScanningDone.set(true);
nodeScanner.stateProperty().removeListener(this);
}
}
};
private final ChangeListener psl = new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
if (newValue.equals(State.SUCCEEDED)) {
paintScanningDone.set(true);
paintScanner.stateProperty().removeListener(this);
}
}
};
private final ChangeListener asl = new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
if (newValue.equals(State.SUCCEEDED)) {
animationScanningDone.set(true);
animationScanner.stateProperty().removeListener(this);
}
}
};
public ObservableMap<String, Class<?>> getFoundComponents() {
return foundComponents;
}
}
and my Interface if you wanna try it out:
public interface JarImporter {
public static File defaultDirectory = new File(System.getProperty("user.home"));
public static final FileChooser.ExtensionFilter classfilter = new FileChooser.ExtensionFilter("Jar files", "*.jar");
static FileChooser defaultFileChooser(){
FileChooser fc = new FileChooser();
fc.getExtensionFilters().add(classfilter);
fc.setInitialDirectory(defaultDirectory);
return fc;
}
public default File importJar(){
File jar = defaultFileChooser().showOpenDialog(null);
if(jar != null){
return jar;
}else{
return null;
}
}
}
Hope this helps.. though just looked at Question date, and was a while ago...

How to update TableView Row using javaFx

I'm trying to make some downloads and show the progress inside my table:
to do that I'm using the following classes:
public class DownloadDataTable {
private SimpleDoubleProperty progress;
private SimpleStringProperty type;
private SimpleStringProperty status;
public DownloadDataTable(double progress, String type, String status) {
this.progress = new SimpleDoubleProperty(progress);
this.type = new SimpleStringProperty(type);
this.status = new SimpleStringProperty(status);
}
public double getProgress() {
return progress.get();
}
public void setProgress(double progress) {
this.progress.set(progress);
}
public String getType() {
String retorno;
if (type==null){
retorno="";
}else{
retorno=type.get();
}
return retorno;
}
public void setType (String type) {
this.type.set(type);
}
public String getStatus(){
String retorno;
if (status==null){
retorno="";
} else{
retorno=status.get();
}
return retorno;
}
public void setStatus(String status){
this.status.set(status);
}
}
and to create the TitledPane, tableview and column tables I'm doing this:
public void addDownloadToTitledPane(DownloadContent downloadContent) {
MetaDados metaDado = downloadContent.getMetaDado();
String title = metaDado.getName();
if (title.length() > 60) {
title = title.substring(0, 57) + "...";
}
TableView downloadTable = new TableView();
TableColumn<DownloadDataTable, Double> progress = new TableColumn<>("progress");
progress.setCellFactory(new Callback<TableColumn<DownloadDataTable, Double>, TableCell<DownloadDataTable, Double>>() {
#Override
public TableCell<DownloadDataTable, Double> call(TableColumn<DownloadDataTable, Double> p) {
final ProgressBar progressBar = new ProgressBar(-1);
final TableCell cell = new TableCell<DownloadDataTable, Double>() {
#Override
protected void updateItem(Double t, boolean bln) {
super.updateItem(t, bln);
if (bln) {
setText(null);
setGraphic(null);
} else {
progressBar.setProgress(t);
progressBar.prefWidthProperty().bind(this.widthProperty());
setGraphic(progressBar);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
progress.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, Double>("progress"));
progress.setText("Progresso");
TableColumn<DownloadDataTable, String> type = new TableColumn<>("type");
type.setCellFactory(new Callback<TableColumn<DownloadDataTable, String>, TableCell<DownloadDataTable, String>>() {
#Override
public TableCell<DownloadDataTable, String> call(TableColumn<DownloadDataTable, String> p) {
TableCell cell = new TableCell<DownloadDataTable, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
type.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, String>("type"));
type.setText("Tipo");
TableColumn<DownloadDataTable, String> status = new TableColumn<>("status");
status.setCellFactory(new Callback<TableColumn<DownloadDataTable, String>, TableCell<DownloadDataTable, String>>() {
#Override
public TableCell<DownloadDataTable, String> call(TableColumn<DownloadDataTable, String> p) {
TableCell cell = new TableCell<DownloadDataTable, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
status.setCellValueFactory(new PropertyValueFactory<DownloadDataTable, String>("status"));
status.setText("Status");
downloadTable.getColumns().addAll(progress, type, status);
List<PendingComponents> pendingComponents = downloadContent.getPendingComponents();
ObservableList<DownloadDataTable> data = FXCollections.observableArrayList();
for (PendingComponents pendingComponent : pendingComponents) {
String typeComponent;
if (pendingComponent.getType().equalsIgnoreCase(Constants.HTML)) {
typeComponent = "Conteúdo Principal";
} else {
typeComponent = "Pacote de Imagens";
}
data.add(new DownloadDataTable(-1, typeComponent, "Preparando download"));
}
downloadTable.setItems(data);
downloadTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TitledPane downloadPane = new TitledPane(title, downloadTable);
downloadPane.setId(metaDado.getOfflineUuid());
vBoxDownload.getChildren().add(downloadPane);
}
Until here everything seems to works fine, but when I try to recover my table and update the data, my table is not updated. I've debbuged and everything seems to work, even the data value is changed, but my table still without update. See my code:
private void publishProgress(final String msg) {
Platform.runLater(new Runnable() {
#Override
public void run() {
TitledPane titledPane = (TitledPane) controller.vBoxDownload.lookup("#"+metaDado.getOfflineUuid());
TableView table = (TableView) titledPane.getContent();
DownloadDataTable data = (DownloadDataTable) table.getItems().get(0);
data.setProgress(100);
data.setStatus(msg);
}
});
}
If I try to remove and add my row it doesn't work, but if I just add another row with the new values, I got a old row with the same value and a new row with new values. I can't figure out what am I doing wrong, someone can help me??
You shouldn't need to add/remove the row to get the table to update when the progress value changes.
The problem is that you're not making the progress property accessible to the TableView. This causes the progress.setCellValueFactory(...) call to wrap your getProgress() value in a new ObservableObjectWrapper. This allows the value to display in the TableView, but it won't notify the table when the value is changed.
Add the following to your DownloadDataTable class, and your table will update when the value changes:
public SimpleDoubleProperty progressProperty() {
return this.progress;
}
public SimpleStringProperty typeProperty() {
return this.type;
}
public SimpleStringProperty statusProperty() {
return this.status;
}

Resources