The JavaFX2 tutorial has an Addressbook example which is a table view.
tutorial:
http://docs.oracle.com/javafx/2/fxml_get_started/fxml_tutorial_intermediate.htm#CACFEHBI
enhancement to edit cells:
http://docs.oracle.com/javafx/2/ui_controls/table-view.htm
modification to add checkbox:
http://download.oracle.com/otndocs/products/javafx/2.2/samples/Ensemble/index.html#SAMPLES/Controls/Table/Table Cell Factory
Entering data in the text boxes and Clicking the Add button adds a row to the table.
However, rows cannot be deleted.
I was trying to see if I can add a Delete button in each cell - clicking it will delete the row.
I modified the existing sample code.
Somehow, the buttons are not visible. Any pointers appreciated.
//FXML_tableview.fxml file
<?import javafx.collections.*?>
<?import javafx.geometry.Insets?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.cell.*?>
<?import javafx.scene.layout.*?>
<?import fxmltableview.*?>
<Scene fx:id="root" width="550" height="550"
fx:controller="fxmltableview.FXMLTableViewController"
xmlns:fx="http://javafx.com/fxml">
<GridPane alignment="center" hgap="10" vgap="10">
<padding>
<Insets top="10" right="10" bottom="10" left="10"/>
</padding>
<TableView fx:id="tableView" GridPane.columnIndex="0"
GridPane.rowIndex="1">
</TableView>
<HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="0"
GridPane.rowIndex="2">
<TextField fx:id="firstNameField" promptText="First Name"
prefWidth="90"/>
<TextField fx:id="lastNameField" promptText="Last Name"
prefWidth="90"/>
<TextField fx:id="emailField" promptText="Email"
prefWidth="150"/>
<Button text="Add" onAction="#addPerson"/>
</HBox>
</GridPane>
</Scene>
// FXMLTableView.java
package fxmltableview;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.stage.Stage;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.util.Callback;
public class FXMLTableView extends Application {
static public ObservableList<Person> data;
#FXML Scene root;
#FXML TableView tableView;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("FXML TableView Example");
root = (Scene)FXMLLoader.load(getClass().getResource("fxml_tableview.fxml"));
primaryStage.setScene(root);
data = FXCollections.observableArrayList(
new Person(true, "Jacob", "Smith", "jacob.smith#example.com"),
new Person(false, "Isabella", "Johnson", "isabella.johnson#example.com"),
new Person(true, "Ethan", "Williams", "ethan.williams#example.com"),
new Person(true, "Emma", "Jones", "emma.jones#example.com"),
new Person(false, "Michael", "Brown", "michael.brown#example.com"));
tableView = (TableView) root.lookup("#tableView");
TableColumn delCol = new TableColumn<Person, Boolean>();
delCol.setMinWidth(50);
delCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> p) {
return new DeleteTableCell<Person, Boolean>();
}
});
//"Invited" column
TableColumn invitedCol = new TableColumn<Person, Boolean>();
invitedCol.setText("Invited");
invitedCol.setMinWidth(50);
invitedCol.setCellValueFactory(new PropertyValueFactory("invited"));
invitedCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
public TableCell<Person, Boolean> call(TableColumn<Person, Boolean> p) {
return new CheckBoxTableCell<Person, Boolean>();
}
});
//"First Name" column
TableColumn firstNameCol = new TableColumn();
firstNameCol.setText("First");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
//"Last Name" column
TableColumn lastNameCol = new TableColumn();
lastNameCol.setText("Last");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
//"Email" column
TableColumn emailCol = new TableColumn();
emailCol.setText("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory("email"));
//Set cell factory for cells that allow editing
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
emailCol.setCellFactory(cellFactory);
firstNameCol.setCellFactory(cellFactory);
lastNameCol.setCellFactory(cellFactory);
//Set handler to update ObservableList properties. Applicable if cell is edited
updateObservableListProperties(emailCol, firstNameCol, lastNameCol);
tableView.setItems(data);
//Enabling editing
tableView.setEditable(true);
tableView.getColumns().addAll(invitedCol, firstNameCol, lastNameCol, emailCol, delCol);
// root.getChildren().add(tableView);
primaryStage.show();
}
private void updateObservableListProperties(TableColumn emailCol, TableColumn firstNameCol,
TableColumn lastNameCol) {
//Modifying the email property in the ObservableList
emailCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
#Override public void handle(TableColumn.CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setEmail(t.getNewValue());
}
});
//Modifying the firstName property in the ObservableList
firstNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
#Override public void handle(TableColumn.CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setFirstName(t.getNewValue());
}
});
//Modifying the lastName property in the ObservableList
lastNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
#Override public void handle(TableColumn.CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setLastName(t.getNewValue());
}
});
}
//CheckBoxTableCell for creating a CheckBox in a table cell
public static class CheckBoxTableCell<S, T> extends TableCell<S, T> {
private final CheckBox checkBox;
private ObservableValue<T> ov;
public CheckBoxTableCell() {
this.checkBox = new CheckBox();
this.checkBox.setAlignment(Pos.CENTER);
setAlignment(Pos.CENTER);
setGraphic(checkBox);
}
#Override public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setGraphic(checkBox);
if (ov instanceof BooleanProperty) {
checkBox.selectedProperty().unbindBidirectional((BooleanProperty) ov);
}
ov = getTableColumn().getCellObservableValue(getIndex());
if (ov instanceof BooleanProperty) {
checkBox.selectedProperty().bindBidirectional((BooleanProperty) ov);
}
}
}
}
public static class DeleteTableCell<S, T> extends TableCell<S, T> {
private final Button del;
private ObservableValue<T> ov;
public DeleteTableCell() {
this.del = new Button("X");
del.setStyle("-fx-base: red;");
this.del.setAlignment(Pos.CENTER);
setAlignment(Pos.CENTER);
setGraphic(del);
del.setOnAction(new EventHandler<ActionEvent> () {
#Override
public void handle(ActionEvent t) {
int i= getIndex();
FXMLTableView.data.remove(i);
}
});
}
#Override public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setGraphic(del);
}
}
}
// EditingCell - for editing capability in a TableCell
public static class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
#Override public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#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());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
public static void main(String[] args) {
launch(args);
}
}
// FXMLTableViewController.java
package fxmltableview;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
public class FXMLTableViewController {
#FXML private TableView<Person> tableView;
#FXML private TextField firstNameField;
#FXML private TextField lastNameField;
#FXML private TextField emailField;
#FXML
protected void addPerson(ActionEvent event) {
ObservableList<Person> data = tableView.getItems();
data.add(new Person(false, firstNameField.getText(),
lastNameField.getText(),
emailField.getText()
));
firstNameField.setText("");
lastNameField.setText("");
emailField.setText("");
}
}
// Person.java
package fxmltableview;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
//Person object
public class Person {
private BooleanProperty invited;
private StringProperty firstName;
private StringProperty lastName;
private StringProperty email;
Person(boolean invited, String fName, String lName, String email) {
this.invited = new SimpleBooleanProperty(invited);
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.invited = new SimpleBooleanProperty(invited);
this.invited.addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
System.out.println(firstNameProperty().get() + " invited: " + t1);
}
});
}
public BooleanProperty invitedProperty() { return invited; }
public StringProperty firstNameProperty() { return firstName; }
public StringProperty lastNameProperty() { return lastName; }
public StringProperty emailProperty() { return email; }
public void setLastName(String lastName) { this.lastName.set(lastName); }
public void setFirstName(String firstName) { this.firstName.set(firstName); }
public void setEmail(String email) { this.email.set(email); }
}
The delCol's cell value factory is missing. Add
delCol.setCellValueFactory(new PropertyValueFactory("invited"));
PropertyValueFactory's parameter is not important here.
Related
I have created a movie app the app is almost complete but there is one shortcoming where if users click one particular category they have to see all the details of that particular category. I'm new to android so I don't know how to implement this. one of them told we can use sql fetch but i have used retro fit so i dont want to change my backend from json to sql .
I have tried a similar question but did not work uploading my main activity, adapter model class and JSON file data
Mainacivity.Java
package com.example.movieapp;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.core.view.MenuItemCompat;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.Filter;
import com.example.movieapp.adapter.BannerMoviesPagerAdapter;
import com.example.movieapp.adapter.ItemRecyclerAdapter;
import com.example.movieapp.adapter.MainRecyclerAdapter;
import com.example.movieapp.listener.OnItemClickLIstener;
import com.example.movieapp.model.AllCategory;
import com.example.movieapp.model.BannerMovies;
import com.example.movieapp.model.CategoryItemList;
import com.example.movieapp.retrofit.RetrofitClient;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Retrofit;
public class MainActivity extends AppCompatActivity {
BannerMoviesPagerAdapter bannerMoviesPagerAdapter;
TabLayout IndicatorTab,categoryTab;
ViewPager bannerMoviesViewpager;
List<BannerMovies> homeBannerList;
List<BannerMovies> tvBannerList;
List<BannerMovies> songBannerList;
List<BannerMovies> audioBannerList;
NestedScrollView nestedScrollView;
AppBarLayout appBarLayout;
MainRecyclerAdapter mainRecyclerAdapter;
ItemRecyclerAdapter itemRecyclerAdapter;
RecyclerView mainRecycler;
List<AllCategory> allCategoryList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Adding this line will prevent taking screenshot in your app
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE);
IndicatorTab =findViewById(R.id.tab_indicator);
categoryTab =findViewById(R.id.tabLayout);
nestedScrollView =findViewById(R.id.nested_scroll);
appBarLayout =findViewById(R.id.appbar);
homeBannerList=new ArrayList<>();
tvBannerList=new ArrayList<>();
songBannerList=new ArrayList<>();
audioBannerList=new ArrayList<>();
getBannerData();
getAllMoviesData(1);
categoryTab.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
switch (tab.getPosition()){
case 1:
setBannerMoviesPagerAdapter(tvBannerList);
getAllMoviesData(2);
return;
case 2:
setBannerMoviesPagerAdapter(songBannerList);
getAllMoviesData(3);
return;
case 3:
setBannerMoviesPagerAdapter(audioBannerList);
getAllMoviesData(4);
return;
default:
setBannerMoviesPagerAdapter(homeBannerList);
getAllMoviesData(1);
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
//titles
allCategoryList=new ArrayList<>();
}
private void setBannerMoviesPagerAdapter(List<BannerMovies> bannerMoviesList) {
bannerMoviesViewpager = findViewById(R.id.banner_viewPager);
bannerMoviesPagerAdapter = new BannerMoviesPagerAdapter(this, bannerMoviesList);
bannerMoviesViewpager.setAdapter(bannerMoviesPagerAdapter);
IndicatorTab.setupWithViewPager(bannerMoviesViewpager);
}
public void setMainRecycler(List<AllCategory>allCategoryList){
mainRecycler=findViewById(R.id.main_recycler);
RecyclerView.LayoutManager layoutManager= new LinearLayoutManager(this,RecyclerView.VERTICAL,false);
mainRecycler.setLayoutManager(layoutManager);
mainRecyclerAdapter =new MainRecyclerAdapter(this,allCategoryList);
mainRecycler.setAdapter(mainRecyclerAdapter);
}
private void getBannerData()
{
CompositeDisposable compositeDisposable = new CompositeDisposable();
compositeDisposable.add(RetrofitClient.getRetroFitClient().getAllBanners()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<List<BannerMovies>>() {
#Override
public void onNext(List<BannerMovies> bannerMovies) {
for ( int i=0; i < bannerMovies.size(); i++ )
{
if(bannerMovies.get(i).getBannerCategoryId().toString().equals("1"))
{
homeBannerList.add(bannerMovies.get(i));
} else if (bannerMovies.get(i).getBannerCategoryId().toString().equals("2"))
{
tvBannerList.add(bannerMovies.get(i));
} else if (bannerMovies.get(i).getBannerCategoryId().toString().equals("3"))
{
songBannerList.add(bannerMovies.get(i));
} else if (bannerMovies.get(i).getBannerCategoryId().toString().equals("4"))
{
audioBannerList.add(bannerMovies.get(i));
}
else
{
homeBannerList.add(bannerMovies.get(i));
}
}
}
#Override
public void onError(Throwable e) {
Log.d("bannerData",""+e);
}
#Override
public void onComplete() {
setBannerMoviesPagerAdapter(homeBannerList);
}
})
);
}
private void getAllMoviesData(Integer categoryId)
{
CompositeDisposable compositeDisposable = new CompositeDisposable();
compositeDisposable.add(RetrofitClient.getRetroFitClient().getAllCategoryMovies(categoryId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<List<AllCategory>>() {
#Override
public void onNext(List<AllCategory> allCategoryList) {
for ( int i=0; i < allCategoryList.size(); i++ )
{
}
setMainRecycler(allCategoryList);
}
#Override
public void onError(Throwable e) {
Log.d("bannerData",""+e);
}
#Override
public void onComplete() {
}
})
);
}
}
Itemrecycleradpater
package com.example.movieapp.adapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.example.movieapp.MovieDetails;
import com.example.movieapp.R;
import com.example.movieapp.model.CategoryItemList;
import java.util.ArrayList;
import java.util.List;
public class ItemRecyclerAdapter extends RecyclerView.Adapter<ItemRecyclerAdapter.ItemViewHolder> implements Filterable {
Context context;
private List<CategoryItemList> categoryItemList;
private List<CategoryItemList>allCategoryListFull;
public ItemRecyclerAdapter(Context context, List<CategoryItemList> categoryItemList) {
this.context = context;
this.categoryItemList = categoryItemList;
allCategoryListFull = new ArrayList<>(categoryItemList);
}
#NonNull
#Override
public ItemViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ItemViewHolder(LayoutInflater.from(context).inflate(R.layout.cardview_movies,parent,false));
}
#Override
public void onBindViewHolder(#NonNull ItemViewHolder holder, final int position) {
Glide.with(context).load(categoryItemList.get(position).getSmallThumb()).placeholder(R.drawable.image12).into(holder.image_item);
holder.image_item.setOnClickListener(view -> {
Intent i = new Intent(context, MovieDetails.class);
i.putExtra("movieId",categoryItemList.get(position).getId());
i.putExtra("movieName",categoryItemList.get(position).getMovieName());
i.putExtra("smallThumb",categoryItemList.get(position).getSmallThumb());
i.putExtra("movieImageUrl",categoryItemList.get(position).getImageUrl());
i.putExtra("movieFile",categoryItemList.get(position).getFileUrl());
i.putExtra("MovieDesc",categoryItemList.get(position).getMovieDesc());
i.putExtra("Language",categoryItemList.get(position).getLanguage());
i.putExtra("Singers",categoryItemList.get(position).getSingers());
i.putExtra("TrailerUrl",categoryItemList.get(position).getTrailerUrl());
context.startActivity(i);
});
}
#Override
public int getItemCount() {
return categoryItemList.size();
}
#Override
public Filter getFilter() {
return filterNew;
}
private Filter filterNew = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
List<CategoryItemList> filteredList = new ArrayList<>();
if(constraint == null || constraint.length() ==0){
filteredList.addAll(allCategoryListFull);
}
else{
String filterPattern = constraint.toString().toLowerCase().trim();
for(CategoryItemList item: allCategoryListFull)
{
if(item.getMovieName().toLowerCase().contains(filterPattern))
{
filteredList.add(item);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredList;
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
categoryItemList.clear();
categoryItemList.addAll((List)results.values);
notifyDataSetChanged();
}
};
public static final class ItemViewHolder extends RecyclerView.ViewHolder
{
ImageView image_item;
public ItemViewHolder(#NonNull View itemView) {
super(itemView);
image_item=itemView.findViewById(R.id.image_item);
}
}
}
package com.example.movieapp.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.movieapp.R;
import com.example.movieapp.model.AllCategory;
import com.example.movieapp.model.CategoryItemList;
import java.util.List;
public class MainRecyclerAdapter extends RecyclerView.Adapter<MainRecyclerAdapter.MainViewHolder> {
Context context;
private List<AllCategory> allCategoryList;
public MainRecyclerAdapter(Context context, List<AllCategory> allCategoryList) {
this.context = context;
this.allCategoryList = allCategoryList;
}
#NonNull
#Override
public MainViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new MainViewHolder(LayoutInflater.from(context).inflate(R.layout.main_recycler_row_item, parent, false));
}
#Override
public void onBindViewHolder(#NonNull MainViewHolder holder, int position) {
holder.categoryName.setText(allCategoryList.get(position).getCategoryTitle());
setItemRecycler(holder.itemRecycler,allCategoryList.get(position).getCategoryItemList());
}
#Override
public int getItemCount() {
return allCategoryList.size();
}
public static final class MainViewHolder extends RecyclerView.ViewHolder {
TextView categoryName;
RecyclerView itemRecycler;
public MainViewHolder(#NonNull View itemView) {
super(itemView);
categoryName = itemView.findViewById(R.id.item_category);
itemRecycler = itemView.findViewById(R.id.item_recycler);
}
}
private void setItemRecycler(RecyclerView recyclerview, List<CategoryItemList> categoryItemList) {
ItemRecyclerAdapter itemRecyclerAdapter = new ItemRecyclerAdapter(context,categoryItemList);
recyclerview.setLayoutManager(new LinearLayoutManager(context,RecyclerView.HORIZONTAL,false));
//recyclerview.setLayoutManager(new GridLayoutManager(context,3));
recyclerview.setAdapter(itemRecyclerAdapter);
}
}
package com.example.movieapp.adapter;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.movieapp.R;
import com.example.movieapp.model.AllCategory;
import com.example.movieapp.model.CategoryItemList;
import java.util.List;
public class MainRecyclerAdapter extends RecyclerView.Adapter<MainRecyclerAdapter.MainViewHolder> {
Context context;
private List<AllCategory> allCategoryList;
public MainRecyclerAdapter(Context context, List<AllCategory> allCategoryList) {
this.context = context;
this.allCategoryList = allCategoryList;
}
#NonNull
#Override
public MainViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new MainViewHolder(LayoutInflater.from(context).inflate(R.layout.main_recycler_row_item, parent, false));
}
#Override
public void onBindViewHolder(#NonNull MainViewHolder holder, int position) {
// Here is your call to an individual item or object like category name
holder.categoryName.setText(allCategoryList.get(position).getCategoryTitle());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Stuff to open new activity
List<CategoryItemList> categoryItemList = allCategoryList.get(position).getCategoryItemList()
Intent i = new Intent(context, MopvieList.class); //New screen
i.putExtra("itemList", categoryItemList); //passing serializable object
//NOTE: CategoryItemList class must implement Serializable
}
});
setItemRecycler(holder.itemRecycler, allCategoryList.get(position).getCategoryItemList());
}
#Override
public int getItemCount() {
return allCategoryList.size();
}
public static final class MainViewHolder extends RecyclerView.ViewHolder {
TextView categoryName;
RecyclerView itemRecycler;
public MainViewHolder(#NonNull View itemView) {
super(itemView);
categoryName = itemView.findViewById(R.id.item_category);
itemRecycler = itemView.findViewById(R.id.item_recycler);
}
}
private void setItemRecycler(RecyclerView recyclerview, List<CategoryItemList> categoryItemList) {
ItemRecyclerAdapter itemRecyclerAdapter = new ItemRecyclerAdapter(context, categoryItemList);
recyclerview.setLayoutManager(new LinearLayoutManager(context, RecyclerView.HORIZONTAL, false));
//recyclerview.setLayoutManager(new GridLayoutManager(context,3));
recyclerview.setAdapter(itemRecyclerAdapter);
}
}
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
List<CategoryItemList> categoryItemList = allCategoryList.get(position).getCategoryItemList();
Intent i = new Intent(context, MovieCat.class); //my activity for showing all category
i.putExtra("itemList", (Serializable) categoryItemList);
context.startActivity(i);
}
public class AllCategory implements Serializable {}// as instructed made it serializable.
public class MovieCat extends AppCompatActivity {
TextView MovieCat;
String movieNameCat;
ArrayList<CategoryItemList> catItems;
RecyclerView recyclerView;
MainRecyclerAdapter mainRecyclerAdapter;
Button button;
String cat; //for item_list intent
String catId;// for intent
int categoryId;// for get all movies data
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_cat);
MovieCat = findViewById(R.id.textView2);
movieNameCat = getIntent().getStringExtra("categoryTitle");
cat = getIntent().getStringExtra("itemList");
button=findViewById(R.id.view_all_btn);
MovieCat.setText(movieNameCat);
catId = getIntent().getStringExtra("categoryId"); getAllMoviesData(categoryId);
getAllMoviesData(categoryId);
}
private void getAllMoviesData(int categoryId) {
{
CompositeDisposable compositeDisposable = new CompositeDisposable();
compositeDisposable.add(RetrofitClient.getRetroFitClient().getAllCategoryMovies(categoryId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<List<AllCategory>>() {
#Override
public void onNext(List<AllCategory> itemList) {
for (int i = 0; i < itemList.size(); i++) {
setMainRecycler(itemList);
}
}
#Override
public void onError(Throwable e) {
Log.d("bannerData", "" + e);
}
#Override
public void onComplete() {
}
})
);
}
}
public void setMainRecycler(List<AllCategory> catItems) {
recyclerView = findViewById(R.id.item_cycle);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
mainRecyclerAdapter = new MainRecyclerAdapter(this, catItems);
recyclerView.setAdapter(mainRecyclerAdapter);
}
Updated my code
#KGeeks
I have defined a DateTime field as StringProperty in my model to display date. I have few rows where the date column is empty in database and have defined a cellfactory to display the date in a desired format & blank for empty rows. My problem starts when i try to update one of those empty columns. The new date doesnt appear. It works for the rows where there is already a date value present.
Part of cellfactory:
txtfld.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
if(txtfld.getText().length() == 0) {
commitEdit(null);
} else {
commitEdit((new DateTime(txtfld.getText(),"dd/MM/yyyy")).toString());
}
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
And the part where I am updating the model:
col_Purchase_DT.setOnEditCommit(new EventHandler<CellEditEvent<Purchase, String>>() {
#Override
public void handle(CellEditEvent<Purchase, String> tbl) {
(tbl.getTableView().getItems().get(tbl.getTablePosition().getRow())).setDOB(tbl.getNewValue());
}
});
I have figured it out that after updating the empty cell with a date col_Purchase_DT.setOnEditCommit() is not called. But is works for non-empty cell. I am using JodaTime for datetime.
I cannot update the second row. But it works perfectly for first & third row.
Any pointers will be helpful.
You seem to be doing way too much coding for this. There's a TextFieldTableCell class that you can use for creating editable cells, and it handles all the wiring for you. Here's an example, based on the usual example from the tutorial. I used the Java 8 java.time.LocalDate for the date column, but the same idea can be applied for JodaTime (I'm just not familiar with the API).
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class TableWithEditableDateColumn extends Application {
#Override
public void start(Stage primaryStage) {
final TableView<Person> table = new TableView<>();
final TableColumn<Person, String> firstNameCol = createTableColumn("firstName", "First Name", String.class);
final TableColumn<Person, String> lastNameCol = createTableColumn("lastName", "Last Name", String.class);
final TableColumn<Person, LocalDate> birthdayCol = createTableColumn("birthday", "Birthday", LocalDate.class);
final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd/MM/yyyy");
firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
birthdayCol.setCellFactory(TextFieldTableCell.forTableColumn(new StringConverter<LocalDate>() {
#Override
public String toString(LocalDate t) {
if (t==null) {
return "" ;
} else {
return dateFormat.format(t);
}
}
#Override
public LocalDate fromString(String string) {
try {
return LocalDate.parse(string, dateFormat);
} catch (DateTimeParseException exc) {
return null ;
}
}
}));
final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", LocalDate.parse("14/03/1975", dateFormat)),
new Person("Isabella", "Johnson", LocalDate.parse("27/09/1982", dateFormat)),
new Person("Ethan", "Williams", null),
new Person("Emma", "Jones", LocalDate.parse("12/07/1979", dateFormat)),
new Person("Michael", "Brown", LocalDate.parse("19/10/1984", dateFormat))
);
table.getColumns().addAll(firstNameCol, lastNameCol, birthdayCol);
table.setItems(data);
table.setEditable(true);
final BorderPane root = new BorderPane();
root.setCenter(table);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private <T> TableColumn<Person, T> createTableColumn(String property, String title, Class<T> type) {
TableColumn<Person, T> col = new TableColumn<>(title);
col.setCellValueFactory(new PropertyValueFactory<>(property));
col.setEditable(true);
col.setPrefWidth(100);
return col ;
}
public static class Person {
private final StringProperty firstName ;
private final StringProperty lastName ;
private final ObjectProperty<LocalDate> birthday ;
public Person(String firstName, String lastName, LocalDate birthday) {
this.firstName = new SimpleStringProperty(this, "firstName", firstName);
this.lastName = new SimpleStringProperty(this, "lastName", lastName);
this.birthday = new SimpleObjectProperty<>(this, "birthday", birthday);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String value) {
firstName.set(value);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String value) {
lastName.set(value);
}
public StringProperty lastNameProperty() {
return lastName;
}
public LocalDate getBirthday() {
return birthday.get();
}
public void setBirthday(LocalDate value) {
birthday.set(value);
}
public ObjectProperty birthdayProperty() {
return birthday;
}
}
public static void main(String[] args) {
launch(args);
}
}
onEditCommit is not fired if an update happens on a null value.
Here is the answer: http://www.wobblycogs.co.uk/index.php/computing/javafx/145-editing-null-data-values-in-a-cell-with-javafx-2
I have a simple java fx application which has a table view. The table view shows some data that is sorted in a certain order. Is there a way to show the arrow(by default) indicating that the data has been sorted in that order?
My code is below:
import java.util.Collections;
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application
{
private TableView<Person> tableView = new TableView<Person>();
private final ObservableList<Person> data = FXCollections.observableArrayList(new Person(
"Allan", "Smith", "allan.smith#example.com",
22), new Person("Zombie", "Jack",
"zombie.jack#test.com", 23), new Person(
"Michael", "Rock", "michael.rock#yahoo.com",
24), new Person("Best", "Jones",
"best.jones#example.com", 11), new Person(
"Michael", "Brown",
"michael.brown#example.com", 14));
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(650);
stage.setHeight(500);
final Label label = new Label("Roster");
label.setFont(new Font("Arial", 10));
tableView.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
TableColumn ageCol = new TableColumn("Age");
ageCol.setMinWidth(200);
ageCol.setCellValueFactory(new PropertyValueFactory<Person, Integer>("age"));
Collections.sort(data);
tableView.setItems(data);
tableView.getColumns().addAll(firstNameCol, lastNameCol, emailCol, ageCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, tableView);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person implements Comparable<Person>
{
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private final SimpleIntegerProperty age;
private Person(String fName, String lName, String email, int age)
{
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.age = new SimpleIntegerProperty(age);
}
public SimpleIntegerProperty getAge()
{
return age;
}
public void setAge(int passedAge)
{
age.set(passedAge);
}
public String getFirstName()
{
return firstName.get();
}
public void setFirstName(String fName)
{
firstName.set(fName);
}
public String getLastName()
{
return lastName.get();
}
public void setLastName(String fName)
{
lastName.set(fName);
}
public String getEmail()
{
return email.get();
}
public void setEmail(String fName)
{
email.set(fName);
}
public int compareTo(Person o)
{
return firstName.get().compareTo(o.getFirstName());
}
}
}
Try
...
firstNameCol.setSortType(TableColumn.SortType.ASCENDING);
...
tableView.setItems(data);
tableView.getColumns().addAll(firstNameCol, lastNameCol, emailCol, ageCol);
tableView.getSortOrder().add(firstNameCol);
...
Also see the discussions on: Javafx: Re-sorting a column in a TableView.
I want to update the code of this example:
http://www.java2s.com/Code/Java/JavaFX/StackPanebasedwizard.htm
package javafxwizard;
import javafx.application.Application;
import javafx.stage.Stage;
import java.util.Stack;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.LabelBuilder;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextAreaBuilder;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.StackPaneBuilder;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
public class JavaFXWizard extends Application {
public static void main(String[] args) throws Exception {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(new SurveyWizard(stage), 400, 250));
stage.show();
}
}
class Wizard extends StackPane {
private static final int UNDEFINED = -1;
private ObservableList<WizardPage> pages = FXCollections
.observableArrayList();
private Stack<Integer> history = new Stack<>();
private int curPageIdx = UNDEFINED;
Wizard(WizardPage... nodes) {
pages.addAll(nodes);
navTo(0);
setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
}
void nextPage() {
if (hasNextPage()) {
navTo(curPageIdx + 1);
}
}
void priorPage() {
if (hasPriorPage()) {
navTo(history.pop(), false);
}
}
boolean hasNextPage() {
return (curPageIdx < pages.size() - 1);
}
boolean hasPriorPage() {
return !history.isEmpty();
}
void navTo(int nextPageIdx, boolean pushHistory) {
if (nextPageIdx < 0 || nextPageIdx >= pages.size()) {
return;
}
if (curPageIdx != UNDEFINED) {
if (pushHistory) {
history.push(curPageIdx);
}
}
WizardPage nextPage = pages.get(nextPageIdx);
curPageIdx = nextPageIdx;
getChildren().clear();
getChildren().add(nextPage);
nextPage.manageButtons();
}
void navTo(int nextPageIdx) {
navTo(nextPageIdx, true);
}
void navTo(String id) {
Node page = lookup("#" + id);
if (page != null) {
int nextPageIdx = pages.indexOf(page);
if (nextPageIdx != UNDEFINED) {
navTo(nextPageIdx);
}
}
}
public void finish() {
}
public void cancel() {
}
}
/**
* basic wizard page class
*/
abstract class WizardPage extends VBox {
Button priorButton = new Button("_Previous");
Button nextButton = new Button("N_ext");
Button cancelButton = new Button("Cancel");
Button finishButton = new Button("_Finish");
WizardPage(String title) {
getChildren().add(
LabelBuilder.create().text(title)
.style("-fx-font-weight: bold; -fx-padding: 0 0 5 0;").build());
setId(title);
setSpacing(5);
setStyle("-fx-padding:10; -fx-background-color: honeydew; -fx-border-color: derive(honeydew, -30%); -fx-border-width: 3;");
Region spring = new Region();
VBox.setVgrow(spring, Priority.ALWAYS);
getChildren().addAll(getContent(), spring, getButtons());
priorButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
priorPage();
}
});
nextButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
nextPage();
}
});
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
getWizard().cancel();
}
});
finishButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
getWizard().finish();
}
});
}
HBox getButtons() {
Region spring = new Region();
HBox.setHgrow(spring, Priority.ALWAYS);
HBox buttonBar = new HBox(5);
cancelButton.setCancelButton(true);
finishButton.setDefaultButton(true);
buttonBar.getChildren().addAll(spring, priorButton, nextButton,
cancelButton, finishButton);
return buttonBar;
}
abstract Parent getContent();
boolean hasNextPage() {
return getWizard().hasNextPage();
}
boolean hasPriorPage() {
return getWizard().hasPriorPage();
}
void nextPage() {
getWizard().nextPage();
}
void priorPage() {
getWizard().priorPage();
}
void navTo(String id) {
getWizard().navTo(id);
}
Wizard getWizard() {
return (Wizard) getParent();
}
public void manageButtons() {
if (!hasPriorPage()) {
priorButton.setDisable(true);
}
if (!hasNextPage()) {
nextButton.setDisable(true);
}
}
}
/**
* This class shows a satisfaction survey
*/
class SurveyWizard extends Wizard {
Stage owner;
public SurveyWizard(Stage owner) {
super(new ComplaintsPage(), new MoreInformationPage(), new ThanksPage());
this.owner = owner;
}
#Override
public void finish() {
System.out.println("Had complaint? "
+ SurveyData.instance.hasComplaints.get());
if (SurveyData.instance.hasComplaints.get()) {
System.out.println("Complaints: "
+ (SurveyData.instance.complaints.get().isEmpty() ? "No Details"
: "\n" + SurveyData.instance.complaints.get()));
}
owner.close();
}
#Override
public void cancel() {
System.out.println("Cancelled");
owner.close();
}
}
/**
* Simple placeholder class for the customer entered survey response.
*/
class SurveyData {
BooleanProperty hasComplaints = new SimpleBooleanProperty();
StringProperty complaints = new SimpleStringProperty();
static SurveyData instance = new SurveyData();
}
/**
* This class determines if the user has complaints. If not, it jumps to the
* last page of the wizard.
*/
class ComplaintsPage extends WizardPage {
private RadioButton yes;
private RadioButton no;
private ToggleGroup options = new ToggleGroup();
public ComplaintsPage() {
super("Complaints");
nextButton.setDisable(true);
finishButton.setDisable(true);
yes.setToggleGroup(options);
no.setToggleGroup(options);
options.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
#Override
public void changed(ObservableValue<? extends Toggle> observableValue,
Toggle oldToggle, Toggle newToggle) {
nextButton.setDisable(false);
finishButton.setDisable(false);
}
});
}
#Override
Parent getContent() {
yes = new RadioButton("Yes");
no = new RadioButton("No");
SurveyData.instance.hasComplaints.bind(yes.selectedProperty());
return VBoxBuilder.create().spacing(5)
.children(new Label("Do you have complaints?"), yes, no).build();
}
#Override
void nextPage() {
// If they have complaints, go to the normal next page
if (options.getSelectedToggle().equals(yes)) {
super.nextPage();
} else {
// No complaints? Short-circuit the rest of the pages
navTo("Thanks");
}
}
}
/**
* This page gathers more information about the complaint
*/
class MoreInformationPage extends WizardPage {
public MoreInformationPage() {
super("More Info");
}
#Override
Parent getContent() {
TextArea textArea = TextAreaBuilder.create().wrapText(true).build();
nextButton.setDisable(true);
textArea.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observableValue,
String oldValue, String newValue) {
nextButton.setDisable(newValue.isEmpty());
}
});
SurveyData.instance.complaints.bind(textArea.textProperty());
return VBoxBuilder.create().spacing(5)
.children(new Label("Please enter your complaints."), textArea).build();
}
}
/**
* This page thanks the user for taking the survey
*/
class ThanksPage extends WizardPage {
public ThanksPage() {
super("Thanks");
}
#Override
Parent getContent() {
StackPane stack = StackPaneBuilder.create().children(new Label("Thanks!"))
.build();
VBox.setVgrow(stack, Priority.ALWAYS);
return stack;
}
}
Can you help me to update the code? In Java 8 LabelBuilder TextAreaBuilder StackPaneBuilder VBoxBuilder are obsolete code?
Can you help me to simplify the code and update it for Java 8?
Here is the same code, but without the builders... Buts its the same Logic:
import javafx.application.Application;
import javafx.stage.Stage;
import java.util.Stack;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
public class JavaFXWizard extends Application {
public static void main(String[] args) throws Exception {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(new SurveyWizard(stage), 400, 250));
stage.show();
}
}
class Wizard extends StackPane {
private static final int UNDEFINED = -1;
private ObservableList<WizardPage> pages = FXCollections
.observableArrayList();
private Stack<Integer> history = new Stack<>();
private int curPageIdx = UNDEFINED;
Wizard(WizardPage... nodes) {
pages.addAll(nodes);
navTo(0);
setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
}
void nextPage() {
if (hasNextPage()) {
navTo(curPageIdx + 1);
}
}
void priorPage() {
if (hasPriorPage()) {
navTo(history.pop(), false);
}
}
boolean hasNextPage() {
return (curPageIdx < pages.size() - 1);
}
boolean hasPriorPage() {
return !history.isEmpty();
}
void navTo(int nextPageIdx, boolean pushHistory) {
if (nextPageIdx < 0 || nextPageIdx >= pages.size()) {
return;
}
if (curPageIdx != UNDEFINED) {
if (pushHistory) {
history.push(curPageIdx);
}
}
WizardPage nextPage = pages.get(nextPageIdx);
curPageIdx = nextPageIdx;
getChildren().clear();
getChildren().add(nextPage);
nextPage.manageButtons();
}
void navTo(int nextPageIdx) {
navTo(nextPageIdx, true);
}
void navTo(String id) {
Node page = lookup("#" + id);
if (page != null) {
int nextPageIdx = pages.indexOf(page);
if (nextPageIdx != UNDEFINED) {
navTo(nextPageIdx);
}
}
}
public void finish() {
}
public void cancel() {
}
}
/**
* basic wizard page class
*/
abstract class WizardPage extends VBox {
Button priorButton = new Button("_Previous");
Button nextButton = new Button("N_ext");
Button cancelButton = new Button("Cancel");
Button finishButton = new Button("_Finish");
WizardPage(String title) {
Label label = new Label(title);
label.setStyle("-fx-font-weight: bold; -fx-padding: 0 0 5 0;");
getChildren().add(label);
setId(title);
setSpacing(5);
setStyle("-fx-padding:10; -fx-background-color: honeydew; -fx-border-color: derive(honeydew, -30%); -fx-border-width: 3;");
Region spring = new Region();
VBox.setVgrow(spring, Priority.ALWAYS);
getChildren().addAll(getContent(), spring, getButtons());
priorButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
priorPage();
}
});
nextButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
nextPage();
}
});
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
getWizard().cancel();
}
});
finishButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
getWizard().finish();
}
});
}
HBox getButtons() {
Region spring = new Region();
HBox.setHgrow(spring, Priority.ALWAYS);
HBox buttonBar = new HBox(5);
cancelButton.setCancelButton(true);
finishButton.setDefaultButton(true);
buttonBar.getChildren().addAll(spring, priorButton, nextButton,
cancelButton, finishButton);
return buttonBar;
}
abstract Parent getContent();
boolean hasNextPage() {
return getWizard().hasNextPage();
}
boolean hasPriorPage() {
return getWizard().hasPriorPage();
}
void nextPage() {
getWizard().nextPage();
}
void priorPage() {
getWizard().priorPage();
}
void navTo(String id) {
getWizard().navTo(id);
}
Wizard getWizard() {
return (Wizard) getParent();
}
public void manageButtons() {
if (!hasPriorPage()) {
priorButton.setDisable(true);
}
if (!hasNextPage()) {
nextButton.setDisable(true);
}
}
}
/**
* This class shows a satisfaction survey
*/
class SurveyWizard extends Wizard {
Stage owner;
public SurveyWizard(Stage owner) {
super(new ComplaintsPage(), new MoreInformationPage(), new ThanksPage());
this.owner = owner;
}
#Override
public void finish() {
System.out.println("Had complaint? "
+ SurveyData.instance.hasComplaints.get());
if (SurveyData.instance.hasComplaints.get()) {
System.out
.println("Complaints: "
+ (SurveyData.instance.complaints.get().isEmpty() ? "No Details"
: "\n"
+ SurveyData.instance.complaints
.get()));
}
owner.close();
}
#Override
public void cancel() {
System.out.println("Cancelled");
owner.close();
}
}
/**
* Simple placeholder class for the customer entered survey response.
*/
class SurveyData {
BooleanProperty hasComplaints = new SimpleBooleanProperty();
StringProperty complaints = new SimpleStringProperty();
static SurveyData instance = new SurveyData();
}
/**
* This class determines if the user has complaints. If not, it jumps to the
* last page of the wizard.
*/
class ComplaintsPage extends WizardPage {
private RadioButton yes;
private RadioButton no;
private ToggleGroup options = new ToggleGroup();
public ComplaintsPage() {
super("Complaints");
nextButton.setDisable(true);
finishButton.setDisable(true);
yes.setToggleGroup(options);
no.setToggleGroup(options);
options.selectedToggleProperty().addListener(
new ChangeListener<Toggle>() {
#Override
public void changed(
ObservableValue<? extends Toggle> observableValue,
Toggle oldToggle, Toggle newToggle) {
nextButton.setDisable(false);
finishButton.setDisable(false);
}
});
}
#Override
Parent getContent() {
yes = new RadioButton("Yes");
no = new RadioButton("No");
SurveyData.instance.hasComplaints.bind(yes.selectedProperty());
VBox vBox = new VBox();
vBox.setSpacing(5);
vBox.getChildren()
.addAll(new Label("Do you have complaints?"), yes, no);
return vBox;
}
#Override
void nextPage() {
// If they have complaints, go to the normal next page
if (options.getSelectedToggle().equals(yes)) {
super.nextPage();
} else {
// No complaints? Short-circuit the rest of the pages
navTo("Thanks");
}
}
}
/**
* This page gathers more information about the complaint
*/
class MoreInformationPage extends WizardPage {
public MoreInformationPage() {
super("More Info");
}
#Override
Parent getContent() {
TextArea textArea = new TextArea();
textArea.setWrapText(true);
nextButton.setDisable(true);
textArea.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(
ObservableValue<? extends String> observableValue,
String oldValue, String newValue) {
nextButton.setDisable(newValue.isEmpty());
}
});
SurveyData.instance.complaints.bind(textArea.textProperty());
VBox vBox = new VBox();
vBox.setSpacing(5);
vBox.getChildren().addAll(new Label("Please enter your complaints."),
textArea);
return vBox;
}
}
/**
* This page thanks the user for taking the survey
*/
class ThanksPage extends WizardPage {
public ThanksPage() {
super("Thanks");
}
#Override
Parent getContent() {
StackPane stack = new StackPane();
stack.getChildren().add(new Label("Thanks!"));
VBox.setVgrow(stack, Priority.ALWAYS);
return stack;
}
}
I'm having serious problems getting a JavaFX 2 TableView to deal with null values in my data. So much trouble in fact I've put together a simple demo of the problem which is shown below.
Essentially the problem is that some of my data may be null and coercing the null to a value, such as an empty string, is not valid, it must be null. In the real code I'm working on I have a null date value, to keep the example simple I've shown a null string below.
Rows 1 and 2 of the table have null values. The two columns are different, the first shows the behaviour of the TextFieldTableCell the second my implementation of an editable cell. Both show the same incorrect behaviour.
The current behaviour is this:
Click the cell to enter editing mode
Enter a value in the cell
Press enter to commit the edit
Nothing happens
At step 4 I would expect the onEditCommit handler for the column to get called but it isn't. Having a look at the source for javax.scene.control.TableCell the commit isn't happening because of the first line of commitEdit is:
if (! isEditing()) return;
It seems that because the cell is null the editing property never gets set to true although I admit I've not yet traced through all the code to see why it never get's switched to true.
Thanks as always for any help.
Example
Main Application
package example;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.stage.Stage;
import javafx.util.Callback;
public class NullCellEditingExample extends Application {
private TableView table = new TableView();
private final ObservableList<Person> data =
FXCollections.observableArrayList( new Person(null, "Smith"), new Person("Isabella", null),
new Person("Ethan", "Williams"), new Person("Emma", "Jones"), new Person("Michael", "Brown"));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
TableColumn firstNameCol = createSimpleFirstNameColumn();
TableColumn lastNameCol = createLastNameColumn();
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol);
table.setEditable(true);
((Group) scene.getRoot()).getChildren().addAll(table);
stage.setScene(scene);
stage.show();
}
private TableColumn createSimpleFirstNameColumn() {
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
firstNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue());
}
});
return firstNameCol;
}
private TableColumn createLastNameColumn() {
Callback<TableColumn, TableCell> editableFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setCellFactory(editableFactory);
lastNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<Person, String> t) {
t.getRowValue().setLastName(t.getNewValue());
}
});
return lastNameCol;
}
}
Editing Cell
package example;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.control.TableCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
#Override
public void startEdit() {
super.startEdit();
if( textField == null ) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#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());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) {
if (!arg2) { commitEdit(textField.getText()); }
}
});
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
String value = textField.getText();
if (value != null) { commitEdit(value); } else { commitEdit(null); }
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
Person
package example;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public SimpleStringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public SimpleStringProperty lastNameProperty() {
return lastName;
}
}
Since posting I found another question that is basically identical. They took the approach of always just avoiding null values which is fine for strings (e.g. use an empty string) but not acceptable for dates or other data types where the is no obvious "empty" value.
The solution is to pass a value of false into the super.updateItem call in the EditingCell.updateItem method. I've put together a full write up of this if anyone is interested in the complete analysis.