I have a list application what is uses RecyclerView.
Each line has got a button, a checkbox and 2 textview.
All object is defined the RecyclerView clas, but I would like to check the checkbox status in the Main class and evaluate the status and remove the item from the list and from the SharedPreferences where the checkbox is checked
Main class is here:
package com.example.homehandling;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class ToDoList extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener, View.OnClickListener {
public MyRecyclerViewAdapter adapter;
public Button btn;
public SharedPreferences sharedPreferences;
public SharedPreferences.Editor myEdit;
public LinkedList<String> items, remain_days, prio;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shoppinglist);
sharedPreferences = getSharedPreferences("ToDoList", Context.MODE_PRIVATE);
myEdit = sharedPreferences.edit();
btn = findViewById(R.id.button);
items = new LinkedList<>();
remain_days = new LinkedList<>();
prio = new LinkedList<>();
RecyclerView recyclerView = findViewById(R.id.list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new MyRecyclerViewAdapter(this,items,remain_days,prio);
adapter.setClickListener(this);
recyclerView.setAdapter(adapter);
btn.setOnClickListener(this);
StartDisplay();
}
#Override
public void onItemClick(View view, int position) {
}
#Override
public void onClick(View v) {
Intent NewTLItem = new Intent(ToDoList.this, NewTLItem.class);
startActivity(NewTLItem);
}
#Override
public void onResume() {
super.onResume();
StartDisplay();
}
public void StartDisplay()
{
sharedPreferences = getSharedPreferences("ToDoList", Context.MODE_PRIVATE);
Map<String, ?> allEntries = sharedPreferences.getAll();
items.clear();
prio.clear();
remain_days.clear();
for (Map.Entry<String, ?> entry : allEntries.entrySet())
{
String key_word = entry.getKey();
String [] item_total = entry.getValue().toString().split(";");
if(key_word.contains("_")) key_word = key_word.replace("_"," ");
items.add(key_word);
remain_days.add(Long.toString(DateCalc(item_total[0])));
prio.add(item_total[1]);
}
adapter.notifyDataSetChanged();
}
public long DateCalc(String startdate)
{
Date system_date, due_date;
long diff_date = 0;
String currentDate = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(new Date());
SimpleDateFormat dates = new SimpleDateFormat("dd-MM-yyyy");
try {
system_date = dates.parse(currentDate);
due_date = dates.parse(startdate);
diff_date = (due_date.getTime()-system_date.getTime())/86400000;
} catch (ParseException e) {
e.printStackTrace();
}
return diff_date;
}
}
At now the item is removed when the whole line is touched. I would like to exchange to if only the Checkbox is "true".
At now the remove method is in the public void onItemClick(View view, int position) function.
I would like to put the content of the OnItemClick method to "if" part of CheckBox status.
Here is the RecyclerView class:
package com.example.homehandling;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder>
{
public List<String> mData, subdata, subdata2;
public LayoutInflater mInflater;
public ItemClickListener mClickListener;
public Context context;
public CheckBox done;
MyRecyclerViewAdapter(Context context, List<String> data, List<String> subdata, List<String> subdata2) {
this.mInflater = LayoutInflater.from(context);
this.mData = data;
this.subdata = subdata;
this.subdata2 = subdata2;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String meta_data = mData.get(position);
String meta_subdata = subdata.get(position);
String meta_subdata2 = subdata2.get(position);
holder.myTextView.setText(meta_data);
holder.subtext.setText(meta_subdata +" "+ meta_subdata2);
holder.add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String[] item_param = holder.myTextView.getText().toString().split(" ");
final Intent intent;
intent = new Intent(context, NewSLItem.class);
intent.putExtra("param",item_param[0]);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mData.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
public TextView myTextView, subtext;
public Button add;
public ViewHolder(View itemView)
{
super(itemView);
context = itemView.getContext();
myTextView = itemView.findViewById(R.id.tvAnimalName);
subtext = itemView.findViewById(R.id.subtext);
add = itemView.findViewById(R.id.add);
done = itemView.findViewById(R.id.done);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view)
{
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
String getItem(int id) {return mData.get(id);
}
void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
public interface ItemClickListener
{
void onItemClick(View view, int position);
}
}
if(adapter.done.isChecked())should work I think, but I got null opbject reference exception.
Maybe the problem is that the checkbox is in the RecyclerView layout and not in Main class layout, but I am not sure.
Is it possible to handle the Checkbox methods from other class, and if yes, then where should I improve the code?
I have an application that is getting price,title etc. data from website. I get data with using jsoup with html parsing. I use a different class for html parsing. I also added membership system from Firebase Authentication to my application. Each user have different product lists. Products are located with user id in database. I want app to get values from database table but it gets titles of tables. My problem is getting titles from database table.
You can see the problem at this photo.
You can see another class at this photo
How can i get values ?
package com.example.lkuygulama;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.util.ArrayList;
public class UrunEklemeActivity extends AppCompatActivity {
FirebaseDatabase firebaseDatabase;
DatabaseReference reference;
private String userId;
private FirebaseAuth.AuthStateListener mAuthlistener;
public ArrayList<String> goster = new ArrayList<String>();
ListView view;
ArrayAdapter<String> arrayAdapter;
public Button aramam,bildirim,liste;
private FirebaseAuth mAuth;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_urun_ekleme);
Urun urun = new Urun();
bildirim=(Button) findViewById(R.id.bildirimButon);
liste = (Button) findViewById(R.id.urunListeButon);
view=(ListView)findViewById(R.id.listele);
arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,goster);
view.setAdapter(arrayAdapter);
mAuthlistener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = mAuth.getCurrentUser();
userId = user.getUid();
}
};
tanimla();
listele();
bildirim.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
gonderBildirim();
}
});
}
private void tanimla() {
firebaseDatabase = FirebaseDatabase.getInstance();
reference = firebaseDatabase.getReference();
}
private void listele() {
reference.child("urunler").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
String valeu =dataSnapshot.getKey();
goster.add(valeu);
arrayAdapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void gonderBildirim() {
Intent intent = new Intent(this, BildirimActivity.class);
startActivity(intent);
}
private void gonderAra() {
Intent intent = new Intent(this,AramaActivity.class);
startActivity(intent);
}
private void gonderListe() {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
}
You should use getValue(String.class).
Like this :
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
String valeu =dataSnapshot.getValue(String.class);
goster.add(valeu);
arrayAdapter.notifyDataSetChanged();
}
I recommend addListenerForSingleValueEvent instead of addChildEventListener.
I want to create a simple table with multiple selection using checkboxes on the first column.
I've created a custom "TableColumn" to handle this feature in every table I need. I've implemented all the features except one: when I select or deselect a single row the corresponding checkbox on the first colum must be selected or unselected. I need help to develop this last function.
public class ColumnSelectRow extends TableColumn{
private CheckBox headerCheckBox = new CheckBox();
public ColumnSelectRow(String title) {
super(title);
setSortable(false);
setPrefWidth(100);
setMaxWidth(100);
setMinWidth(100);
setResizable(false);
setGraphic(headerCheckBox);
setCellValueFactory( new Callback<TableColumn.CellDataFeatures<Object, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Object, Boolean> p) {
return new SimpleBooleanProperty(p.getValue() != null);
}
});
setCellFactory(new Callback<javafx.scene.control.TableColumn<Object, Boolean>, TableCell<Object, Boolean>>() {
#Override
public TableCell<Object, Boolean> call(javafx.scene.control.TableColumn<Object, Boolean> p) {
return new SelectButtonCell();
}
});
headerCheckBox.setOnAction(t->{
if(disableEvents) return;
TableView table = ColumnSelectRow.this.getTableView();
if (table == null) return;
if(headerCheckBox.isSelected()) table.getSelectionModel().selectAll();
else table.getSelectionModel().clearSelection();
});
}
public CheckBox getHeaderCheckBox() {
return headerCheckBox;
}
private boolean disableEvents = false;
private class SelectButtonCell extends TableCell {
private final CheckBox checkBox = new CheckBox();
public SelectButtonCell(){
setAlignment(Pos.CENTER);
checkBox.setOnAction(t -> {
int row = getTableRow().getIndex();
TableView table = SelectButtonCell.this.getTableView();
if (table == null) return;
if(checkBox.isSelected()) table.getSelectionModel().select(row);
else table.getSelectionModel().clearSelection(row);
table.getFocusModel().focus(row);
boolean empty = table.getSelectionModel().isEmpty();
boolean full = table.getSelectionModel().getSelectedCells().size()==table.getItems().size();
disableEvents = true;
headerCheckBox.setIndeterminate(false);
if(empty) headerCheckBox.setSelected(false);
else if(full) headerCheckBox.setSelected(true);
else headerCheckBox.setIndeterminate(true);
disableEvents = false;
});
}
public CheckBox getCheckBox() {
return checkBox;
}
#Override
protected void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if(!empty){
setGraphic(checkBox);
}
int row = getTableRow().getIndex();
TableView table = SelectButtonCell.this.getTableView();
checkBox.setSelected(table.getSelectionModel().isSelected(row));
}
public boolean isCheckBoxSelected(){
return checkBox.isSelected();
}
}
thanks!!
Edit: a custom table with the first column selectable
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class MyTable extends Application {
private ColumnSelectRow columSelect;
#Override
public void start(Stage primaryStage) throws Exception {
columSelect = new ColumnSelectRow();
TableView T = new TableView<>();
T.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ObservableList<myObject> al = FXCollections.observableArrayList();
al.add(new myObject(1));
al.add(new myObject(2));
al.add(new myObject(3));
T.setItems(al);
T.getColumns().add(columSelect);
TableColumn<myObject, Number> number = new TableColumn("Number");
number.setCellValueFactory(cellData->cellData.getValue().propetry);
T.getColumns().add(number);
Scene scene = new Scene(T);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private class myObject{
public SimpleIntegerProperty propetry = new SimpleIntegerProperty();
public myObject(int i) {
propetry.set(i);
}
}
}
I have number of objects. Let's say i have 10 Books object and i want user to select any number of the book. When user press the submit button i want to retrieve all the books "object" that user selected.
As of now, while showing screen to user i use
CheckBox cb= new CheckBox(book.getName());
this shows bookname to user and user selects the book. But on runtime i will be needing bookid and other properties of book object as well.
Is there anyway through which i can save the book object in the checkbox?
Basic Examle. if you want more sepecifc you need to post your code, we can set object to node using setUserDate, then we can use that object when we need. here i am using object id for example, in yor case save that object i hope this will solve your problem ,?s post a comment
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class UserData extends Application {
public void start(Stage primaryStage) {
VBox root = new VBox();
Book book = new Book(22, "firstBok");
Book book1 = new Book(2, "secondBok");
CheckBox checkB = new CheckBox(book.getName());
checkB.setUserData(book);
CheckBox checkB1 = new CheckBox(book1.getName());
checkB1.setUserData(book1);
Button btn = new Button("Submit");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (checkB.isSelected()) {
int firstCheckBxId = ((Book) checkB.getUserData()).getId();
System.out.println("id:" + firstCheckBxId);
}
if (checkB1.isSelected()) {
int secondCheckBxId = ((Book) checkB1.getUserData()).getId();
System.out.println("id:" + secondCheckBxId);
}
}
});
root.getChildren().addAll(checkB, checkB1, btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
class Book {
int id;
private String name;
Book(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return this.id;
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
launch(args);
}
}
Consider using a control with built-in selection functionality, such as a ListView. Then you can just check the selection model.
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class BookSelection extends Application {
#Override
public void start(Stage primaryStage) {
ListView<Book> bookList = new ListView<>();
bookList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
bookList.setCellFactory(lv -> new ListCell<Book>() {
#Override
public void updateItem(Book book, boolean empty) {
super.updateItem(book, empty);
if (empty) {
setText(null);
} else {
setText(book.getTitle());
}
}
});
IntStream.rangeClosed(1, 10).mapToObj(i -> new Book("Book "+i)).forEach(bookList.getItems()::add);
Button submit = new Button("Submit selection");
submit.setOnAction(e ->
bookList.getSelectionModel().getSelectedItems().forEach(book -> System.out.println(book.getTitle())));
BorderPane root = new BorderPane(bookList, null, null, submit, null);
BorderPane.setAlignment(submit, Pos.CENTER);
BorderPane.setMargin(submit, new Insets(10));
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Book {
private final StringProperty title = new SimpleStringProperty() ;
public Book(String title) {
setTitle(title);
}
public final StringProperty titleProperty() {
return this.title;
}
public final java.lang.String getTitle() {
return this.titleProperty().get();
}
public final void setTitle(final java.lang.String title) {
this.titleProperty().set(title);
}
}
public static void main(String[] args) {
launch(args);
}
}
If for some reason you really want to implement this with check boxes, you can keep a Set<Book> representing the selected books, and update it when each check box is selected/unselected. Note this is similar to #user99370's answer, but is more robust as it avoids downcasting the (essentially unknown-type) userData to your data type.
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class BookSelection extends Application {
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
grid.setHgap(5);
grid.setVgap(5);
grid.setPadding(new Insets(10 ));
List<Book> books = IntStream.rangeClosed(1, 10).mapToObj(i -> new Book("Book "+i)).collect(Collectors.toList());
Set<Book> selectedBooks = new HashSet<>();
int row = 0 ;
for (Book book : books) {
CheckBox checkBox = new CheckBox();
checkBox.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
selectedBooks.add(book);
} else {
selectedBooks.remove(book);
}
});
grid.addRow(row, checkBox, new Label(book.getTitle()));
row++ ;
}
Button submit = new Button("Submit selection");
submit.setOnAction(e ->
selectedBooks.forEach(book -> System.out.println(book.getTitle())));
GridPane.setHalignment(submit, HPos.CENTER);
grid.add(submit, 0, row, 2, 1);
Scene scene = new Scene(grid, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Book {
private final StringProperty title = new SimpleStringProperty() ;
public Book(String title) {
setTitle(title);
}
public final StringProperty titleProperty() {
return this.title;
}
public final java.lang.String getTitle() {
return this.titleProperty().get();
}
public final void setTitle(final java.lang.String title) {
this.titleProperty().set(title);
}
}
public static void main(String[] args) {
launch(args);
}
}
This is just a simplified version of the accepted answer.
We can use setUserData(Object) to set data and getUserData() to retrieve the previously set data of any JavaFX node (including CheckBox).
Reference:
https://docs.oracle.com/javase/8/javafx/api/javafx/scene/Node.html#setUserData-java.lang.Object-
I have an app that makes significant use of checkboxes in JavaFX TreeView and TableView. There is custom code as much of it was done before the enhancements that came later in JavaFX 2.2.
I now find that checkboxes that used to work do not work (as if they are disabled) although some work intermittently.
I have checked through the Oracle compatibility documentation and I can find nothing relevant.
I have a small sample app that works perfectly in Java 7.4 but shows the same faulty behaviour as the main app in Java 8.
Could anyone you please suggest where I might start looking from your own experience - e.g. cell factory, callback etc - or indicate if anything fundamental changed with this kind of construct at Java 8? I have posted the sample app code below (four separate classes).
Thank you in advance.
package samplefx2_original_v7;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
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;
import javafx.util.Callback;
public class SampleFX2_Original_V7 extends Application {
private TableView table = new TableView();
private boolean everything = false;
//Sample data for the table.
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new ControlPerson(false, "Select Columns", false, false),
new Person(true, "Jacob Smith", true, false),
new Person(true, "Isabella Johnson", true, true),
new Person(true, "Ethan Williams", false, false),
new Person(true, "Emma Jones", false, true),
new Person(false, "Michael Brown", true, true));
/**
* #param args the command line arguments
*/
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(850);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
//A custom cell factory that creates checkboxes for a boolean property.
Callback<TableColumn, TableCell> colCheckFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn p) {
return new CheckBoxCell();
}
};
//The various columns
TableColumn nameCol = new TableColumn("Name");
nameCol.setMinWidth(100);
nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
TableColumn contactCol = createContactColumn(colCheckFactory);
TableColumn emailCol = createEmailColumn(colCheckFactory);
TableColumn phoneCol = createPhoneColumn(colCheckFactory);
//Add the columns and data to the table.
table.setItems(data);
table.getColumns().addAll(nameCol, contactCol, emailCol, phoneCol);
//Make the table editable
table.setEditable(true);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
private TableColumn createEmailColumn(Callback<TableColumn, TableCell> cellFactory) {
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(75);
emailCol.setCellValueFactory(new PropertyValueFactory("email"));
emailCol.setCellFactory(cellFactory);
emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setEmail(event.getNewValue());
}
} else {
//Need to handle the indivdual cells as well as the special control cells.
Person p = event.getRowValue();
p.setEmail( event.getNewValue() );
}
}
});
return emailCol;
}
private TableColumn createPhoneColumn(Callback<TableColumn, TableCell> cellFactory) {
TableColumn phoneCol = new TableColumn("Phone");
phoneCol.setMinWidth(75);
phoneCol.setCellValueFactory( new PropertyValueFactory("phone"));
phoneCol.setCellFactory(cellFactory);
phoneCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setPhone(event.getNewValue());
}
} else {
Person p = event.getRowValue();
p.setPhone( event.getNewValue() );
}
}
});
return phoneCol;
}
/**
* This is the main control column in your application (containing the green and red circles).
*
* #param cellFactory
* #return
*/
private TableColumn createContactColumn( Callback<TableColumn, TableCell> cellFactory ) {
TableColumn contactCol = new TableColumn("Contact");
contactCol.setMinWidth(75);
contactCol.setCellValueFactory( new PropertyValueFactory( "contact"));
contactCol.setCellFactory(cellFactory);
contactCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
//This handler is different to the other two as it controls the checking/unchecking of both
//the whole table and individual rows.
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setContact(event.getNewValue());
p.setEmail(event.getNewValue());
p.setPhone(event.getNewValue());
}
//This is just an example of how you would control a special "everything" flag.
//You could call any method or take any action here to deal with the special
//case where everything is selected.
everything = event.getNewValue();
} else {
//Set the state of any boolean properties to modify the whole row.
Person p = event.getRowValue();
p.setContact( event.getNewValue() );
p.setEmail(event.getNewValue());
p.setPhone(event.getNewValue());
}
}
});
return contactCol;
}
}
package samplefx2_original_v7;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleBooleanProperty contact;
private final SimpleStringProperty name;
private final SimpleBooleanProperty email;
private final SimpleBooleanProperty phone;
public Person(boolean contact, String name, boolean email, boolean phone) {
this.contact = new SimpleBooleanProperty( contact );
this.name = new SimpleStringProperty(name);
this.email = new SimpleBooleanProperty( email );
this.phone = new SimpleBooleanProperty( phone );
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public SimpleStringProperty nameProperty() {
return name;
}
public boolean isContact() {
return contact.get();
}
public void setContact( boolean contact ) {
this.contact.set( contact );
}
public SimpleBooleanProperty contactProperty() {
return contact;
}
public boolean isEmail() {
return email.get();
}
public void setEmail( boolean email ) {
this.email.set( email );
}
public SimpleBooleanProperty emailProperty() {
return email;
}
public boolean isPhone() {
return contact.get();
}
public void setPhone( boolean phone ) {
this.phone.set( phone );
}
public SimpleBooleanProperty phoneProperty() {
return phone;
}
}
package samplefx2_original_v7;
/**
* This subclass of Person is used only to identify the row(s) that is used to control
* the checking and unchecking of columns.
*/
public class ControlPerson extends Person {
public ControlPerson(boolean active, String name, boolean email, boolean phone) {
super(active, name, email, phone);
}
}
package samplefx2_original_v7;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
public class CheckBoxCell extends TableCell<Person, Boolean> {
private CheckBox checkBox;
public CheckBoxCell() {
if (checkBox == null) {
checkBox = new CheckBox();
checkBox.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
CheckBox cb = (CheckBox) event.getSource();
getTableView().edit(getTableRow().getIndex(), getTableColumn());
commitEdit(cb.isSelected());
}
});
}
setGraphic(checkBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
//Center align the checkboxes.
setAlignment(Pos.CENTER);
}
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
//If we don't have an item don't draw the checkbox.
setGraphic( null );
// checkBox.setDisable(true);
// checkBox.setSelected(false);
} else {
checkBox.setDisable(false);
checkBox.setSelected(item);
}
}
}
A couple of things are going on here. A simple thing to note is that you don't set the graphic when the cell changes from empty to non-empty. You need:
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
//If we don't have an item don't draw the checkbox.
setGraphic( null );
// checkBox.setDisable(true);
// checkBox.setSelected(false);
} else {
checkBox.setDisable(false);
checkBox.setSelected(item);
setGraphic(checkBox);
}
}
But probably more of an issue is that your table cell implementation really relies on undocumented behavior, in that it forces the table into editing mode (getTableView().edit(...)) and (as far as I can tell) seems to rely on some side effects of this in order to get the functionality you want. Because the behavior is undocumented, there's no guarantee it will remain the same in new releases, so I'm not too surprised it breaks under Java 8.
A better way to implement the cells is just to use listeners (or bindings) to exchange data between the check box's selectedProperty and the property in the Person class. Because the display is always the same (i.e. a check box) no matter whether the cell is being edited or not, there's no really need to work with the editing API from the cell class. This is the approach the standard CheckBoxTableCell introduced in JavaFX 2.2 works.
Assuming the functionality you want is that the "contact" column check boxes should select both the "email" and "phone" checkboxes, I would approach this slightly differently. There's really no logical need to have a contact property in the model class (Person), because that information is already incorporated in the other two boolean properties. (Another way to think of this is that the "control" checkboxes are just UI components and not really part of the data, so they have no place in the model.) Your "contact" column doesn't really have a property associated with it; just make that a TableColumn<Person, Person> and have its cells update the other properties appropriately.
Also, your ControlPerson is a bit of a hack. Again this row in the table is really just for user interface purposes and is not part of the data. So it really shouldn't appear as part of the table's items. I would put the "select all" check boxes in the table header, not in the data part of the table. This will change their appearance some (though you can always fix that with CSS if you need) but the benefit is that they remain visible when you scroll, which is probably desirable.
Here's a complete example which works this way, and runs under both Java 7 (JavaFX 2.2) and under Java 8. (I tested under 1.7.0_65, 1.8.0_25, and 1.8.0_40.)
import java.util.Arrays;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
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.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SampleFX2 extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<Person> data = FXCollections.observableArrayList(
new Callback<Person, Observable[]>() {
#Override
public Observable[] call(Person person) {
return new Observable[] {person.emailProperty(), person.phoneProperty()};
}
});
data.addAll(Arrays.asList(
new Person("Jacob Smith", true, false),
new Person("Isabella Johnson", true, true),
new Person("Ethan Williams", false, false),
new Person("Emma Jones", false, true),
new Person("Michael Brown", true, true)));
final TableView<Person> table = new TableView<>();
table.setItems(data);
TableColumn<Person, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
final CheckBox allContactCheckBox = new CheckBox() ;
final CheckBox allEmailCheckBox = new CheckBox();
final CheckBox allPhoneCheckBox = new CheckBox();
updateHeaderCheckBoxes(table, allContactCheckBox, allEmailCheckBox, allPhoneCheckBox);
data.addListener(new ListChangeListener<Person>() {
#Override
public void onChanged(Change<? extends Person> change) {
updateHeaderCheckBoxes(table, allContactCheckBox, allEmailCheckBox, allPhoneCheckBox);
}
});
allPhoneCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setPhone(isSelected);
}
}
});
allEmailCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setEmail(isSelected);
}
}
});
allContactCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setPhone(isSelected);
person.setEmail(isSelected);
}
}
});
TableColumn<Person, Person> contactCol = new TableColumn<>();
contactCol.setPrefWidth(75);
contactCol.setGraphic(createTableHeader("Contact", allContactCheckBox));
contactCol.setCellValueFactory(new Callback<CellDataFeatures<Person, Person>, ObservableValue<Person>>() {
#Override
public ObservableValue<Person> call(
CellDataFeatures<Person, Person> person) {
return new ReadOnlyObjectWrapper<>(person.getValue());
}
});
contactCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() {
#Override
public TableCell<Person, Person> call(
TableColumn<Person, Person> param) {
return new ContactCell();
}
});
TableColumn<Person, Boolean> emailCol = new TableColumn<>();
emailCol.setPrefWidth(75);
emailCol.setGraphic(createTableHeader("Email", allEmailCheckBox));
emailCol.setCellValueFactory(new PropertyValueFactory<Person, Boolean>("email"));
emailCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override
public TableCell<Person, Boolean> call(
TableColumn<Person, Boolean> column) {
return new CheckBoxCell<Person>(new Callback<Person, BooleanProperty>() {
#Override
public BooleanProperty call(Person person) {
return person.emailProperty();
}
});
}
});
TableColumn<Person, Boolean> phoneCol = new TableColumn<>();
phoneCol.setPrefWidth(75);
phoneCol.setGraphic(createTableHeader("Phone", allPhoneCheckBox));
phoneCol.setCellValueFactory(new PropertyValueFactory<Person, Boolean>("phone"));
phoneCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override
public TableCell<Person, Boolean> call(
TableColumn<Person, Boolean> column) {
return new CheckBoxCell<Person>(new Callback<Person, BooleanProperty>() {
#Override
public BooleanProperty call(Person person) {
return person.phoneProperty();
}
});
}
});
table.getColumns().add(nameCol);
table.getColumns().add(contactCol);
table.getColumns().add(emailCol);
table.getColumns().add(phoneCol);
Button showButton = new Button("Debug");
showButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
for (Person p : table.getItems()) {
System.out.println(p.getName() + " " + p.isEmail() +" "+ p.isPhone());
}
System.out.println();
}
});
HBox controls = new HBox(5);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(10));
controls.getChildren().add(showButton);
BorderPane root = new BorderPane();
root.setCenter(table);
root.setBottom(controls);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node createTableHeader(String text, CheckBox checkBox) {
VBox header = new VBox(2);
header.setPadding(new Insets(2));
header.getChildren().addAll(new Label(text), checkBox);
header.setAlignment(Pos.CENTER);
return header ;
}
private static void updateHeaderCheckBoxes(final TableView<Person> table,
final CheckBox allContactCheckBox, final CheckBox allEmailCheckBox,
final CheckBox allPhoneCheckBox) {
boolean allPhoneSelected = true ;
boolean noPhoneSelected = true ;
boolean allEmailSelected = true ;
boolean noEmailSelected = true ;
for (Person person : table.getItems()) {
if (person.isEmail()) {
noEmailSelected = false ;
} else {
allEmailSelected = false ;
}
if (person.isPhone()) {
noPhoneSelected = false ;
} else {
allPhoneSelected = false ;
}
}
setCheckBoxState(allPhoneSelected, noPhoneSelected, allPhoneCheckBox);
setCheckBoxState(allEmailSelected, noEmailSelected, allEmailCheckBox);
setCheckBoxState(allPhoneSelected && allEmailSelected, noPhoneSelected && noEmailSelected, allContactCheckBox);
}
private static void setCheckBoxState(boolean on, boolean off, CheckBox checkBox) {
if (on) {
checkBox.setIndeterminate(false);
checkBox.setSelected(true);
} else if (off) {
checkBox.setIndeterminate(false);
checkBox.setSelected(false);
} else {
checkBox.setIndeterminate(true);
}
}
public static class ContactCell extends TableCell<Person, Person> {
private final CheckBox checkBox ;
public ContactCell() {
checkBox = new CheckBox();
final ChangeListener<Boolean> propertyListener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean oldValue, Boolean newValue) {
updateCheckBox();
}
};
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean oldValue, Boolean newValue) {
Person person = getItem();
person.setEmail(newValue);
person.setPhone(newValue);
}
});
itemProperty().addListener(new ChangeListener<Person>() {
#Override
public void changed(
ObservableValue<? extends Person> observable,
Person oldValue, Person newValue) {
if (oldValue != null) {
oldValue.emailProperty().removeListener(propertyListener);
oldValue.phoneProperty().removeListener(propertyListener);
}
if (newValue != null) {
newValue.emailProperty().addListener(propertyListener);
newValue.phoneProperty().addListener(propertyListener);
updateCheckBox();
}
}
});
emptyProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasEmpty, Boolean isEmpty) {
if (isEmpty) {
setGraphic(null);
} else {
setGraphic(checkBox);
}
}
});
setAlignment(Pos.CENTER);
}
private void updateCheckBox() {
Person person = getItem();
if (person != null) {
setCheckBoxState(person.isEmail() && person.isPhone(), ! person.isEmail() && ! person.isPhone(), checkBox);
if (person.isEmail() && person.isPhone()) {
checkBox.setSelected(true);
} else if (! person.isEmail() && ! person.isPhone()) {
checkBox.setSelected(false);
} else {
checkBox.setIndeterminate(true);
}
checkBox.setIndeterminate(person.isEmail() != person.isPhone());
}
}
}
public static class CheckBoxCell<T> extends TableCell<T, Boolean> {
private final CheckBox checkBox ;
public CheckBoxCell(final Callback<T, BooleanProperty> propertyMapper) {
checkBox = new CheckBox();
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
#SuppressWarnings("unchecked") // for some reason getTableRow() returns a raw type...
TableRow<T> row = getTableRow();
if (row != null) {
T item = row.getItem();
if (item != null) {
BooleanProperty property = propertyMapper.call(item);
property.set(newValue);
}
}
}
});
itemProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (newValue != null) {
checkBox.setSelected(newValue);
}
}
});
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setAlignment(Pos.CENTER);
emptyProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (newValue) {
setGraphic(null);
} else {
setGraphic(checkBox);
}
}
});
}
}
public static class Person {
private final StringProperty name;
private final BooleanProperty email;
private final BooleanProperty phone;
public Person(String name, boolean email, boolean phone) {
this.name = new SimpleStringProperty(name);
this.email = new SimpleBooleanProperty( email );
this.phone = new SimpleBooleanProperty( phone );
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
public boolean isEmail() {
return email.get();
}
public void setEmail( boolean email ) {
this.email.set( email );
}
public BooleanProperty emailProperty() {
return email;
}
public boolean isPhone() {
return phone.get();
}
public void setPhone( boolean phone ) {
this.phone.set( phone );
}
public BooleanProperty phoneProperty() {
return phone;
}
}
public static void main(String[] args) {
launch(args);
}
}