打字练习软件——JavaFx

版权声明:就是码字也不容易啊 https://blog.csdn.net/qq_40946921/article/details/85608117

运行效果: 

布局有些繁琐,用到多线程(计时)。

菜单窗口(选择文章)以及成绩单窗口不写,留给别人了

不得不说,系统字体有些难看,但是如果用一些个性化字体,别人的电脑如果没有安装这个字体,就会有些问题

代码如下,素材在网盘(自取)

网盘:https://pan.baidu.com/s/1tNp1k0sohDZpqJhOja19Rg

素材需放在与.class同一目录下

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.*;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
class MyBox extends HBox{
    public Label key,value;
    public MyBox(ImageView img,String key,String value,String unit){
        this.key=new Label(key);this.value=new Label(value);
        img.setFitHeight(30);img.setFitWidth(30);
        setAlignment(Pos.CENTER_LEFT);
        setSpacing(15);
        getChildren().addAll(img,this.key,new Label(":"),this.value,new Label(unit));
    }
}
public class Typist extends Application {
    Vector<TextFlow> textFlows;   //存储文本流   //这些Vector是为了方便创建以后再找到那些组件
    Vector<TextField> textFields; //存储文本域
    Vector<Vector<Text>> vectors; //二维文本
    Vector<Rectangle> rectangles; //矩形背景
    GridPane pane;
    ScrollPane inputPane;
    VBox menu;
    MyBox rate, right, error, back, count;
    Scene scene;
    int second;                       //计时器
    Label label,time,textName;
    ImageView btPause,btStop;
    Thread thread;                    //这个进程用来跑时间
    int now , size , keyCount , backCount , errorCount , Count;                  //now用来确定当前所在TextFiled
    ImageView clock=new ImageView("clock.png");
    private boolean pause = false;
    public void init(){
        thread=new Thread(()-> {
            try {
                while (true){
                    synchronized ((String)" ") {
                        if (!pause) {
                            Platform.runLater(() -> {
                                rate.value.setText(String.valueOf((int)(((double)keyCount/second)*60)));
                                time.setText(String.format("%02d:%02d", second % 3600 / 60, second % 60));
                            });
                            Thread.sleep(1000);
                            second++;
                        }
                    }
                }
            } catch (Exception ex) { }
        });
        pane=new GridPane();
        scene=new Scene(pane);
        menu=new VBox();
        label=new Label("时间:");
        time=new Label("00:00");
        textName=new Label();
        btPause=new ImageView("pause.png");
        btStop=new ImageView("stop.png");
        menu.setPrefSize(200,400);
        menu.setAlignment(Pos.TOP_CENTER);
        btPause.setOnMouseClicked(e->changePause());
        btPause.setFitWidth(75);btPause.setFitHeight(35);
        btStop.setFitWidth(75);btStop.setFitHeight(35);
        menu.setSpacing(5);
        menu.setPadding(new Insets(15,10,10,10));
        time.setFont(Font.font("System",FontWeight.BOLD,30));
        time.setTextFill(Color.rgb(239,169,37));
        clock.setFitWidth(120);clock.setFitHeight(120);
        Label label=new Label("开始输入时计时!");
        label.setTextFill(Color.rgb(192,36,33));
        HBox hbox=new HBox(label,new Label("文章名:"),textName);
        hbox.setPrefSize(550,20);
        hbox.setSpacing(60);
        hbox.setPadding(new Insets(10,10,0,10));
        pane.add(hbox,0,0);
        Rectangle rectangle=new Rectangle(200,420);
        rectangle.setArcWidth(20);rectangle.setArcHeight(20);
        rectangle.setFill(Color.rgb(203,230,222));
        pane.add(rectangle,1,1);
        pane.setBackground(new Background(new BackgroundFill(Color.rgb(217,228,242),null,null)));
    }
    private void loadText(File file)throws FileNotFoundException ,IOException{    //从file中读取文本并更新组件的数据。
        textFlows=new Vector<>();
        textFields=new Vector<>();
        vectors=new Vector<>();
        rectangles=new Vector<>();
        inputPane=new ScrollPane();
        now=0;size=0;errorCount=0;keyCount=0;backCount=0;
        rate=new MyBox(new ImageView("rate.png"),"速   度","-","KPM");
        right=new MyBox(new ImageView("right.png"),"正确率","100","%");
        error=new MyBox(new ImageView("error.png"),"错   误","0","字");
        back= new MyBox(new ImageView("back.png"),"退   格","0","次");
        count=new MyBox(new ImageView("count.png"),"总字数","0","字");
        HBox bt=new HBox(btPause,btStop);
        bt.setPrefWidth(200);
        bt.setAlignment(Pos.CENTER);
        bt.setSpacing(10);
        bt.setPadding(new Insets(5,5,5,5));
        pane.add(menu,1,1);
        menu.getChildren().addAll(clock,new Label("时间"),time, rate, right, error,back,count, bt);
        int count=1;
        second=0;
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "gbk"));
        textName.setText(file.getName().substring(0,file.getName().indexOf('.')));
        String str;
        GridPane grid=new GridPane();
        while ((str=reader.readLine())!=null) {         //读取文本
            TextFlow textFlow = new TextFlow();
            TextField textField = new TextField();
            Vector<Text> texts=new Vector<>();
            textFlow.getChildren().add(new Text("    "));
            for (int i = 0; i < str.length(); i++) {    //将每行的每个文字设为一个组件
                Text t = new Text(str.substring(i, i + 1));
                size++;
                t.setFont(Font.font("System", FontWeight.BOLD,20));
                t.setTextAlignment(TextAlignment.CENTER);
                textFlow.getChildren().add(t);
                texts.add(t);
            }
            textField.setFont(Font.font("System",20));
            textFlow.setMinHeight(30);
            textField.setEditable(false);

            pane.setVgap(10);pane.setHgap(5);
            pane.setAlignment(Pos.TOP_CENTER);
            Rectangle rectangle=new Rectangle(550,80);
            rectangle.setArcHeight(20);
            rectangle.setArcWidth(10);
            rectangle.setFill(Color.rgb(212,233,254));

            grid.add(rectangle,0,count);
            VBox box=new VBox(textFlow,textField);
            box.setPadding(new Insets(10,1,10,1));
            textField.textProperty().addListener(ov->{  //监听文本域变动
                if(!thread.isAlive())
                    thread.start();   //开始计时
                String s=textField.getText();
                for(int i=0;i<texts.size();i++){
                    if(i<s.length()){                   //修改文字高亮
                        if(s.charAt(i)==texts.get(i).getText().charAt(0)) {
                            texts.get(i).setFill(Color.rgb(78,203,107));
                            texts.get(i).setUnderline(false);
                        }
                        else {
                            texts.get(i).setFill(Color.rgb(219,88,96));
                            texts.get(i).setUnderline(true);
                        }
                    }
                    else {
                        texts.get(i).setFill(Color.BLACK);
                        texts.get(i).setUnderline(false);
                    }
                }
                update();
            });
            textField.setOnKeyPressed(e->{     //绑定面板的回车事件,当回车时切换焦点
                if(e.getCode()== KeyCode.BACK_SPACE){
                    backCount++;
                    back.value.setText(String.valueOf(backCount));
                }
                else if(e.getCode()== KeyCode.ENTER &&textField.getText().length() >= texts.size()){
                    textFields.get(now).setEditable(false);
                    rectangles.get(now).setFill(Color.rgb(212,233,254));
                    textFields.get(++now).setEditable(true);
                    textFields.get(now).requestFocus();
                    rectangles.get(now).setFill(Color.rgb(240,209,210));
                }
                else{
                    keyCount++;
                }
            });
            vectors.add(texts);
            textFlows.add(textFlow);
            textFields.add(textField);
            rectangles.add(rectangle);
            grid.add(box,0,count++);
        }
        textFields.lastElement().setOnKeyPressed(e->{
            if(e.getCode()== KeyCode.ENTER &&textFields.lastElement().getText().length() >= vectors.lastElement().size()){
                textFields.get(now).setEditable(false);
                rectangles.get(now).setFill(Color.rgb(212,233,254));
                changePause();
            }
        });
        inputPane.setContent(grid);
        inputPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
        inputPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        inputPane.setMinSize(580,420);
        inputPane.setPadding(new Insets(5,5,5,5));
        pane.add(inputPane,0,1);
        reader.close();

        textFields.get(now).setEditable(true);   //初始化第一个文本框
        rectangles.get(now).setFill(Color.rgb(240,209,210));
        textFields.get(now).requestFocus();
    }
    private boolean changePause(){   //改变暂停状态
        if(thread.isAlive()){
            if(pause){
                pause=false;
                btPause.setImage(new Image("pause.png"));
                textFields.get(now).setEditable(true);
            }
            else {
                pause = true;
                btPause.setImage(new Image("run.png"));
                textFields.get(now).setEditable(false);
            }
        }
        return pause;
    }

    private void  update(){   //更新数据
        errorCount=0;Count=0;
        for(int i=0;i<=now;i++){
            for(int j=0;j<textFields.get(i).getText().length()&&j<vectors.get(i).size();j++){
                if(textFields.get(i).getText().charAt(j)!=vectors.get(i).get(j).getText().charAt(0)) {
                    errorCount++;
                }
                Count++;
            }
        }
        error.value.setText(String.valueOf(errorCount));
        right.value.setText(String.valueOf((int)((1-(double)errorCount/Count)*100)));
        count.value.setText(String.valueOf(Count));
    }

    private void mainMenu(){        //显示选择(文章)菜单

    }

    private void over(){    //显示成绩单

    }

    public void start(Stage stage)throws FileNotFoundException ,IOException{
        stage.setScene(scene);
        stage.setWidth(800);
        stage.setHeight(480);
        stage.setResizable(false);          //尺寸不可变
        stage.show();
        loadText(new File("test.txt"));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40946921/article/details/85608117