JavaFX-按列搜索tableview中的行

时间:2019-01-20 19:17:57

标签: search javafx tableview row

我有一个表格视图,并且可以按单列e.g.

进行搜索

当我有很多列并且每列必须有很多文本字段时,问题就开始了。有没有简单的方法可以使表格视图中的第一行是可编辑搜索框的行?

2 个答案:

答案 0 :(得分:0)

如果需要一行文本字段,每列一个,则可能需要在TableView上方放置一个HBox容器,并将TextField实例放入HBox。为了获得正确的大小调整,您可以将文本字段(首选!)的宽度绑定到相应的列宽,并将HBox(首选!)的宽度绑定到TableView的宽度:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
 import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;


/**
 *
 * @author kids
 */
public class game extends javax.swing.JFrame {
private PaintSurface canvas;
    int x_position = 0;
    int y_position = 580;
    int x_speed = 7;
    int enemy_posX = 0;
    int enemy_posY = 560;
int x_pos = 0;
int y_pos = 0;
int x_pos2 = -1200;
int x_position2 = -1200;
int enemy_posX2 = -1150;
int enemy_posY2 = 560;
int cloudOnex = 30;
int cloudOney = 70;
int cloud2x = -1150;
int cloud2y = 70;
int cloud3x = 700;
int cloud3y = 70;
int cloud4x = -600;
int cloud4y = 70;
int playerx =400;
int playery = 540;
    /**
     * Creates new form game
     */
    public game() {

          JPanel btnPanel = new JPanel(new FlowLayout());

        JButton btnUp = new JButton("Move Up ");
        btnPanel.add(btnUp);
        btnUp.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                playery += 10;

                canvas.repaint();
                requestFocus(); // change the focus to JFrame to receive KeyEvent
            }
        });


        Container cp = getContentPane();

        cp.setLayout(new BorderLayout());

        // "super" JFrame fires KeyEvent
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent evt) {

                switch (evt.getKeyCode()) {

                    case KeyEvent.VK_UP:
                        playery -= 22;
                        repaint();
                        break;




                }
            }
        });

 this.setTitle("Scrolling Game");
        this.setSize(1200, 650);
        // super.setBackground(Color.YELLOW);
        this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
        this.add(new PaintSurface(), BorderLayout.CENTER);
        this.setVisible(true);
        canvas = new PaintSurface();
        this.add(canvas, BorderLayout.CENTER);




        //settings for the form, handling things such as exiting and size
        Timer timer = new Timer(10, e -> {
            canvas.movement();
           canvas.check();
            canvas.repaint();
        });
        timer.start();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>                        



     class PaintSurface extends JComponent {

        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2 = (Graphics2D) g;

            Rectangle2D background = new Rectangle2D.Float(x_pos, y_pos, 1200, 650);
            g2.setColor(Color.CYAN);
            g2.fill(background);

            Rectangle2D ground = new Rectangle2D.Float(x_position, y_position, 1200, 30);
            g2.setColor(Color.GREEN);
            g2.fill(ground);

            Rectangle2D cloudOne = new Rectangle2D.Float(cloudOnex, cloudOney, 70, 70);
            g2.setColor(Color.WHITE);
            g2.fill(cloudOne);

             Rectangle2D cloudThree = new Rectangle2D.Float(cloud3x, cloud3y, 70, 70);
            g2.setColor(Color.WHITE);
            g2.fill(cloudThree);





            Rectangle2D background2 = new Rectangle2D.Float(x_pos2, y_pos, 1200, 650);
            g2.setColor(Color.CYAN);
            g2.fill(background2);

            Rectangle2D ground2 = new Rectangle2D.Float(x_position2, y_position, 1200, 30);
            g2.setColor(Color.GREEN);
            g2.fill(ground2);

           Rectangle2D cloudTwo = new Rectangle2D.Float(cloud2x, cloud2y, 70, 70);
            g2.setColor(Color.WHITE);
            g2.fill(cloudTwo);

             Rectangle2D cloudFour = new Rectangle2D.Float(cloud4x, cloud4y, 70, 70);
            g2.setColor(Color.WHITE);
            g2.fill(cloudFour);

            Rectangle2D player = new Rectangle2D.Float(playerx, playery,40,40);
            g2.setColor(Color.red);
            g2.fill(player);


            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        /**
         * @param args the command line arguments
         */
    }

      public void movement(){

          cloudOnex += 10;
          cloud2x += 10;
          cloud3x += 10;
          cloud4x += 10;
          x_position += 10;
          x_pos += 10;
          x_position2 += 10;
          x_pos2 += 10;
          enemy_posX += 10;
          enemy_posX2 += 10;



                 try { Thread.sleep(20); }   /* this will pause for 50 milliseconds */
                 catch (InterruptedException e) { System.err.println("sleep exception"); }


        }
 public void check(){
             if (x_pos == 1200 ) {
                x_pos = -1200;
                //x_position = -1200;
                repaint();
            }
               if (x_pos2 == 1200) {
                   x_pos2 = -1200;
                 // x_position2 = -1200;
                   repaint();

               }
               if (x_position == 1200) {
                   x_position = -1200;
                   repaint();
               }
               if (x_position2 == 1200) {
                   x_position2 = -1200;
                   repaint();

               }
               if (cloudOnex == 1200) {
                   cloudOnex = -1150;
                   repaint();

               }
               if (cloud2x == 1200){
                   cloud2x = -1150;
                   repaint();

               }
              // if (x_position2 > 1300) {
               //    x_position2 = -600;
               //    repaint();

              // }

        }



     }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(game.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(game.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(game.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(game.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new game().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    // End of variables declaration                   
}

一种非常简单但有效的过滤方法是将TableView数据包装在FilteredList中,如果其中一个搜索框内的文本发生更改,则更新过滤谓词:

hbox.prefWidthProperty().bind(tableview.widthProperty());
textfield1.prefWidthProperty().bind(col1.widthProperty());
textfield2.prefWidthProperty().bind(col2.widthProperty());

如果您的列不适合TabelView的视口,那么您可能希望将搜索框放在列标题中,即将TextFields作为图形属性设置为TableColumn:

ObservableList<Person> data = FXCollections.observableArrayList();
data.add(....); // add data ...

// wrap the data collection in a filterd list:
filteredList = new FilteredList<>(data);
filteredList.setPredicate(p -> true); // Initial: show all rows

// set the FilteredList as TableView data:
tableview.itemsProperty().set(filteredList);

// Create a changelistener for the search box:
// "dataObject" is an instance of your data class/entity etc.
 textfield1.textProperty().addListener((observable, oldValue, newValue) -> {
  updatePredicate();
});
textfield2.textProperty().addListener((observable, oldValue, newValue) -> {
  updatePredicate();
});

首先,图形节点将隐藏列标题。您可以尝试通过css样式(只是一个提示,我需要检查一下)来纠正此问题,或者您将新的Label和文本字段一起包装在HBox中,该HBox将成为列标题的图形节点。

更新过滤谓词的方法:

col1.setGraphic(textfield1);
col2.setGraphic(textfield2);

希望有帮助:-)

答案 1 :(得分:0)

enter image description here

这是一个草稿示例,其中的搜索通过“与”逻辑进行,即所有搜索文本均已考虑在内。

假设您有一个人作为数据模型:

package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;

import javafx.beans.property.SimpleStringProperty;

class Person {
    private final SimpleStringProperty firstName;
    private final SimpleStringProperty lastName;
    private final SimpleStringProperty email;

    Person(String fName, String lName, String email) {
        this.firstName = new SimpleStringProperty(fName);
        this.lastName = new SimpleStringProperty(lName);
        this.email = new SimpleStringProperty(email);
    }

    String getFirstName() {
        return firstName.get();
    }

    String getLastName() {
        return lastName.get();
    }

    String getEmail() {
        return email.get();
    }
}

该表的设计如下:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051.MyCellValueFactory?>

<AnchorPane prefHeight="400.0"
            prefWidth="600.0"
            xmlns="http://javafx.com/javafx/8.0.171"
            xmlns:fx="http://javafx.com/fxml/1"
            fx:controller="com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051.Controller">
    <TableView fx:id="tableView" prefWidth="600">
        <columnResizePolicy>
            <TableView fx:constant="UNCONSTRAINED_RESIZE_POLICY"/>
        </columnResizePolicy>

        <columns>
            <TableColumn text="First Name">
                <cellValueFactory>
                    <MyCellValueFactory property="firstName"/>
                </cellValueFactory>
            </TableColumn>

            <TableColumn text="Last Name">
                <cellValueFactory>
                    <MyCellValueFactory property="lastName"/>
                </cellValueFactory>
            </TableColumn>

            <TableColumn text="E-mail">
                <cellValueFactory>
                    <MyCellValueFactory property="email"/>
                </cellValueFactory>
            </TableColumn>
        </columns>
    </TableView>
</AnchorPane>

我们有自定义表格行类。

MyTableRowData:

package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;

public abstract class MyTableRowData<T> {
    public abstract T firstNameProperty();

    public abstract T lastNameProperty();

    public abstract T emailProperty();
}

SearchTableRowData:

package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;

public class SearchTableRowData extends MyTableRowData<ObjectProperty<String>> {
    private final ObjectProperty<String> firstName = new SimpleObjectProperty<>();
    private final ObjectProperty<String> lastName = new SimpleObjectProperty<>();
    private final ObjectProperty<String> email = new SimpleObjectProperty<>();

    @Override
    public ObjectProperty<String> firstNameProperty() {
        return firstName;
    }

    @Override
    public ObjectProperty<String> lastNameProperty() {
        return lastName;
    }

    @Override
    public ObjectProperty<String> emailProperty() {
        return email;
    }
}

PersonTableRowData:

package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;

class PersonTableRowData extends MyTableRowData<ObjectProperty<String>> {
    private final ObjectProperty<String> firstName;
    private final ObjectProperty<String> lastName;
    private final ObjectProperty<String> email;

    PersonTableRowData(final Person person) {
        this.firstName = new SimpleObjectProperty<>(person.getFirstName());
        this.lastName = new SimpleObjectProperty<>(person.getLastName());
        this.email = new SimpleObjectProperty<>(person.getEmail());
    }

    @Override
    public ObjectProperty<String> firstNameProperty() {
        return firstName;
    }

    @Override
    public ObjectProperty<String> lastNameProperty() {
        return lastName;
    }

    @Override
    public ObjectProperty<String> emailProperty() {
        return email;
    }
}

MyCellValueFactory 类是所有魔术的心脏:

package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;

import java.lang.reflect.Method;

import javafx.beans.NamedArg;
import javafx.beans.Observable;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextField;
import javafx.scene.text.Text;
import javafx.util.Callback;

public class MyCellValueFactory<P extends MyTableRowData<?>, S extends Node>
        implements Callback<TableColumn.CellDataFeatures<P, S>, ObservableValue<S>> {
    private final String property;

    public MyCellValueFactory(@NamedArg("property") final String property) {
        this.property = property;
    }

    @Override
    public ObservableValue<S> call(final TableColumn.CellDataFeatures<P, S> param) {
        final P tableRowData = param.getValue();

        if (tableRowData instanceof SearchTableRowData) {
            return new SimpleObjectProperty<>(buildSearchTextBox(tableRowData));
        } else if (tableRowData instanceof PersonTableRowData) {
            return new SimpleObjectProperty<>(buildText(tableRowData));
        }

        return new SimpleObjectProperty<>();
    }

    @SuppressWarnings("unchecked")
    private S buildText(final P tableRowData) {
        final Text text = new Text();
        text.textProperty().bind(extractProperty(tableRowData));

        return (S) text;
    }

    @SuppressWarnings("unchecked")
    private S buildSearchTextBox(final P tableRowData) {
        final TextField searchTextField = new TextField();
        searchTextField.promptTextProperty().set(property);
        searchTextField.textProperty().bindBidirectional(extractProperty(tableRowData));

        return (S) searchTextField;
    }

    @SuppressWarnings("unchecked")
    private <T extends Observable> T extractProperty(final P tableRowData) {
        try {
            final Class<?> c = Class.forName(tableRowData.getClass().getName());
            final Method method = c.getDeclaredMethod(property + "Property");

            return (T) method.invoke(tableRowData);
        } catch (final Exception exc) {
            throw new RuntimeException(exc);
        }
    }
}

控制器类:

package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;

import org.apache.commons.lang3.StringUtils;

public class Controller {
    @FXML
    private TableView<MyTableRowData<?>> tableView;

    private final ObservableValue<? extends ObservableList<MyTableRowData<?>>> observableRows =
            new SimpleListProperty<>(FXCollections.observableArrayList());

    private final SearchTableRowData searchTableRowData = new SearchTableRowData();

    private final List<Person> originalPersons = Arrays.asList(
            new Person("A", "B", "c@c.com"),
            new Person("AA", "BB", "cc@c.com"),
            new Person("AAA", "BBB", "ccc@c.com"),
            new Person("AAAA", "BBBB", "cccc@c.com"));

    @FXML
    void initialize() {
        tableView.itemsProperty().bind(observableRows);

        observableRows.getValue().add(searchTableRowData);

        searchTableRowData.firstNameProperty().addListener((o, oldValue, newValue) -> fillPersons());
        searchTableRowData.lastNameProperty().addListener((o, oldValue, newValue) -> fillPersons());
        searchTableRowData.emailProperty().addListener((o, oldValue, newValue) -> fillPersons());

        fillPersons();
    }

    private void fillPersons() {
        ((ObservableList) observableRows).remove(1, ((ObservableList) observableRows).size());

        observableRows
                .getValue()
                .addAll(originalPersons
                        .stream()
                        .filter(getFirstNamePredicate())
                        .filter(getLastNamePredicate())
                        .filter(getEmailPredicate())
                        .map(PersonTableRowData::new)
                        .collect(Collectors.toList()));
    }

    private Predicate<Person> getFirstNamePredicate() {
        final String value = searchTableRowData.firstNameProperty().get();
        return StringUtils.isNoneEmpty(value) ?
                person -> StringUtils.containsIgnoreCase(person.getFirstName(), value) :
                person -> true;
    }

    private Predicate<Person> getLastNamePredicate() {
        final String value = searchTableRowData.lastNameProperty().get();
        return StringUtils.isNoneEmpty(value) ?
                person -> StringUtils.containsIgnoreCase(person.getLastName(), value) :
                person -> true;
    }

    private Predicate<Person> getEmailPredicate() {
        final String value = searchTableRowData.emailProperty().get();
        return StringUtils.isNoneEmpty(value) ?
                person -> StringUtils.containsIgnoreCase(person.getEmail(), value) :
                person -> true;
    }
}

最后,您将通过此 Main 类一起运行所有

package com.dmaslenko.stackexchange.stackoverflow.javafx.q54280051;

import java.net.URL;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import com.dmaslenko.stackexchange.stackoverflow.q54033646.CsvReader;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    @SuppressWarnings("Duplicates")
    public void start(final Stage stage) throws Exception {
        final URL fxmlFileResource =
                CsvReader.class.getResource("/com/dmaslenko/stackexchange/stackoverflow/javafx/q54280051/main.fxml");
        final FXMLLoader loader = new FXMLLoader(fxmlFileResource);
        final Parent rootPane = loader.load();

        loader.getController();

        final Scene scene = new Scene(rootPane, 600, 400);
        stage.setScene(scene);
        stage.show();
    }
}

这是草稿但有效的示例,没有太多优化。

需要改进的已知领域:

  1. 搜索字段中的任何更改都会失去焦点和光标。
  2. 第一行是可选的,但不能选择。