开始:就是网络编程老师说的网上下载的点名器不好用,让同学谁写个最好支持excel的数据导入和导出;随后同学们好几个都拿出了自己的作品,但是都是或多或少的不完善甚至有些个做的在我看来也就是能简单的点个名不支持数据的导入导出,随后我问了一下我哥他觉得这东西并不难让我试试然后就有了下边的代码;
项目思路:
操作所用技术 | 支持功能大致描述 |
java、支持对java图形化拖拽的myeclipse、poi | excel导入和导出并且都支持数据复用,重复和不重复点名以及老师评价 |
poi可以说算是封装了对于office文件接口的访问方法等的,类似于dbutils但是没有它方便,poi提供的操作Excel的方法比较基础,类似于那种想要获取到一个列的所有值你还是需要通过一行行访问才能够拿到所有,没有现成的方法;
大概的成品设计成这样:
基本界面设计出来之后就只用数据读取了主要,读取后用List传回图形化然后开始可以点名等的;然后定义一下导出数据的格式是什么之后开始定义实体类有(因为空间有限我就直接写省略这部分怎么先的看代码就懂了);项目简单,大概的包和类:
导出数据的表格大概是这样:(可以复用,下次结果会识别位置添加)
对于excel的操作:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import entity.Student;
import entity.Tchoose;
public class Dao {
//Workbook是一个xlsx和xls对应的工作簿对象都实现的接口用它来就是为了满足两者都支持;
public Workbook workbook;
//通过学生名字找所在的行数(因为需要满足复写的问题,所以如果原本数据存在只需要修改对应的即可不用重新追加)
public int findByName(String name,List<String> list) {
if(list==null) return -1;
int id = -1;
id = list.indexOf(name);
return id;
}
//通过你选择的表现找位于第几行
public int findIdByChoose(Tchoose choose) {
switch(choose) {
case 优秀: return 2;
case 一般: return 3;
case 缺勤: return 4;
}
return 0;
}
//这里设置了初始化必须要传入Workbook对象结合着上一层容易理解
public Dao(Workbook workbook) {
this.workbook = workbook;
}
//导入数据的表格中不存在这个学生,就直接在后边追加了;
public void put(Student student) {
Sheet sheet = workbook.getSheetAt(0);
int lastRowNum = sheet.getLastRowNum();
Row dataRow = sheet.createRow(lastRowNum + 1);
dataRow.createCell(0).setCellValue(student.getName());
dataRow.createCell(1).setCellValue(student.getNum());
dataRow.createCell(2).setCellValue(student.getGood());
dataRow.createCell(3).setCellValue(student.getCommon());
dataRow.createCell(4).setCellValue(student.getNotCome());
//设置一下“最后提问时间”成自定义的字符串格式不然excel不会正常显示
CellStyle hssfCellStyleDate = workbook.createCellStyle();
DataFormat df = workbook.createDataFormat();
hssfCellStyleDate.setDataFormat(df.getFormat("yyyy-MM-dd HH:mm:ss"));
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Cell cell = dataRow.createCell(5);
cell.setCellValue(format.format(student.getLastTime()));
cell.setCellStyle(hssfCellStyleDate);
//如果你不设置这一单元格对应的长度,会因为显示不下而出现(xxxxx))不能正确识别
sheet.setColumnWidth(5, (int) (12 * 1.2d * 256 > 12 * 256 ? 12 * 1.2d * 256 : 12 * 256));
}
//改变表格中的数据(通过详细的信息)
public void change(String name,Tchoose choose,List<String> result) {
int flag = this.findByName(name,result);
int id = findIdByChoose(choose);
//flag==-1的时候就是原本点名结果表格中没有此学生
if(flag==-1) {
Student student = new Student(name);
if(id==3) {
student.setGood("0");
student.setCommon("1");
}
if(id==4) {
student.setGood("0");
student.setNotCome("1");
}
put(student);
}else {
//这就是如果原本的表格中有我们要做的了;
Row row = workbook.getSheetAt(0).getRow(flag+1);
Cell cell = row.getCell(id);
cell.setCellValue(Integer.parseInt(cell.getStringCellValue())+1+"");
cell = row.getCell(1);
cell.setCellValue(Integer.parseInt(cell.getStringCellValue())+1+"");
cell = row.getCell(5);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
cell.setCellValue(format.format(new Date()));
}
}
以上代码是我根据这次需要完成目标的特点写到一起的最“底层的方法吧”;然后就是上边直接调用他的类了:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import dao.Dao;
import entity.Student;
public class Server {
private File file;
private Dao dao ;
private InputStream input;
//导入数据了; result是需要导入到的excel的原本数据,students是这次点名中点到的所有人以及评价
public void adds(List<String> result,List<Student> students) {
for(Student stu:students) {
dao.change(stu.getName(),stu.getChoose(),result);
}
//最终是需要workbook写入到目表文件的,finish方法在下边定义
this.finish();
}
public Server(File file) {
//这个类的初始化需要给file对象以便能够创建workboo并传入dao层,并且通过文件名来判断需要创建的实现类对象应该是什么类型的;
if(file.getName().endsWith("xlsx")) {
try {
input = new FileInputStream(file);
dao = new Dao(new XSSFWorkbook(input));
} catch (IOException e) {
e.printStackTrace();//里边的异常处理一般我都抛出new RuntimeException(“massage”),以便前边捕捉到来提示用户相关信息
}
}
else {
try {
input = new FileInputStream(file);
dao = new Dao(new HSSFWorkbook(input));
} catch (IOException e) {
e.printStackTrace();
}
}
this.file = file;
}
public void finish() {
try {
//将workbook写入
OutputStream os = new FileOutputStream(file);
dao.workbook.write(os);
input.close();
dao.workbook.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
以上两个类都是已经可以支持了xls和xlsx两种类型的基本我们所需的操作了,并且这两个类对于异常的处理还可以基本如果有异常都是io异常(一般我整个完成下来是所有异常都是try-catch的,catch之后就抛出new RuntimeException(“message”)来处理方便前边捕捉并且显示出来为什么错误提示用户,比如说读取或导入数据的时候目标文件已经被打开等;)但是为了程序的扩展和方便,还有一个比较主要的类就是使用次数比较多的方法我放入进去方便调用的Utils类,里边实现的功能主要有:
数据 | 配置 |
对于用户选择操作文件后的读取成list的操作,并返回 | 将上次用户选择打的文件记录到配置文件中并且下次默认使用,以及方便扩展功能 |
代码如下:
package Server;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class Utils {
//读取导入表格中的数据并返回list《名字》
public List<String> readStartName(File file){
List<String> list = new ArrayList();
String aim = "姓名";
int id = -1;
InputStream inputStream = null;
Workbook workbook = null;
try {
inputStream = new FileInputStream(file);
if(file.getName().endsWith("xlsx"))
workbook = new XSSFWorkbook(inputStream);
else workbook = new HSSFWorkbook(inputStream);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Sheet sheetAt = workbook.getSheetAt(0);
if(sheetAt.getLastRowNum()<=0) {
throw new RuntimeException("所给表格为空");//前边方便捕获提示用户
}else{
Row row = sheetAt.getRow(0);
for(Cell cell:row) {
cell.setCellType(CellType.STRING);
if(cell.getStringCellValue().indexOf(aim)!=-1) {
id = cell.getColumnIndex();
}
}
if(id==-1) {
throw new RuntimeException("所给数据中没有姓名列");
}
for (Row rows : sheetAt) {
if (rows.getRowNum() == 0) {
continue;
}
String name = rows.getCell(id).getStringCellValue();
list.add(name);
}
}
try {
File ofile = new File("D:/DMQData/"+file.getName());
if(!ofile.exists()) {
ofile.createNewFile();
FileInputStream input = new FileInputStream(file);
FileOutputStream os = new FileOutputStream(ofile);
byte[] b = new byte[1024];
int len = -1;
while((len=input.read(b))!=-1) {
os.write(b);
os.flush();
}
input.close();
os.close();
}
setInit(ofile.getAbsolutePath());
workbook.write(new FileOutputStream(file));
workbook.close();
inputStream.close();
}catch(Exception e){
throw new RuntimeException("文件已经被打开");
}
return list;
}
//读取导出数据的文件中学生数据并返回list<姓名>,如果格式不满足我们需要的也是在这次操作就改了
public List<String> readResultName(File file) throws IOException{
List<String> list = new ArrayList();
String aim = "姓名";
int id = -1;
InputStream inputStream = null;
inputStream = new FileInputStream(file);
Workbook workbook = null;
if(file.getName().endsWith("xlsx"))
workbook = new XSSFWorkbook(inputStream);
else workbook = new HSSFWorkbook(inputStream);
Sheet sheetAt = null;
sheetAt = workbook.getSheetAt(0);
int last = sheetAt.getLastRowNum();
if(last<=0) {
//创建标题行(下边定义的方法)
createHead(sheetAt);
//将workbook写入(下边定义的方法)
workBookWrite(workbook,file);
inputStream.close();
return list;
}else{
Row row = sheetAt.getRow(0);
if(row!=null) {
for(Cell cell:row) {
cell.setCellType(CellType.STRING);
if(cell.getStringCellValue().contains(aim)) {
id = cell.getColumnIndex();
}
}
}
if(id==-1) {
//到了这一步我们已经完成了的操作结果--没有姓名列,更别说标题行了,那我们级把他的数据全部删除了覆盖成我们的数据;
sheetAt.shiftRows(1,sheetAt.getLastRowNum()+1,-1);
//经过我的测试发现只有删除后立马写出才能清楚所以下边代码就是写出并且重新创建;
workBookWrite(workbook,file);
inputStream.close();
inputStream = new FileInputStream(file);
if(file.getName().endsWith("xlsx"))
workbook = new XSSFWorkbook(inputStream);
else workbook = new HSSFWorkbook(inputStream);
sheetAt = workbook.getSheetAt(0);
createHead(sheetAt);
workBookWrite(workbook,file);
return list;
}else {
for (Row rows : sheetAt) {
if (rows.getRowNum() == 0) {
continue;
}
String name = rows.getCell(id).getStringCellValue();
list.add(name);
}
}
}
try {
workbook.write(new FileOutputStream(file));
workbook.close();
inputStream.close();
}catch(Exception e) {
throw new RuntimeException("程序已经打开");
}
return list;
}
// 创建标题行
public void createHead(Sheet sheetAt) {
int cloumn = sheetAt.getLastRowNum();
Row titlerRow =sheetAt.getRow(0);
if(cloumn<0) {
//3.创建标题行
titlerRow = sheetAt.createRow(0);
titlerRow.createCell(0).setCellValue("姓名");
titlerRow.createCell(1).setCellValue("提问次数");
titlerRow.createCell(2).setCellValue("表现良好");
titlerRow.createCell(3).setCellValue("表现一般");
titlerRow.createCell(4).setCellValue("缺勤");
titlerRow.createCell(5).setCellValue("最近一次提问时间");
}else {
titlerRow.getCell(0).setCellValue("姓名");
titlerRow.getCell(1).setCellValue("提问次数");
titlerRow.getCell(2).setCellValue("表现良好");
titlerRow.getCell(3).setCellValue("表现一般");
titlerRow.getCell(4).setCellValue("缺勤");
titlerRow.getCell(5).setCellValue("最近一次提问时间");
}
}
//将workbook对象写出
public void workBookWrite(Workbook workbook,File file) {
try {
FileOutputStream fo = new FileOutputStream(file);
workbook.write(fo);
workbook.close();
fo.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//这一下的方法都是对于配置文件来说的,因为是打包成exe我们需要一个固定的文件来记录我们想要记录的用户上次操作的相关属性,以便扩展和复用
public static String init() {
Properties pro = new Properties();
File file = new File("D:/DMQData");
if(!file.exists()) {
//如果一固定文件下没有我们的文件就创建有了就读取想要的然后返回用于返回给界面来初始化exe
file.mkdirs();
}
File afile = new File("D:/DMQData/点名器config.properties") ;
if(!afile.exists()) {
try {
afile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}else {
try {
FileInputStream input = new FileInputStream(afile);
//为了读取不乱码
pro.load(new InputStreamReader(input,"GBK"));
} catch (IOException e) {
e.printStackTrace();
}
String path = pro.getProperty("path");
return path;
}
}
//可以根据用户的操作来设置我们的配置文件内的值
public static void setInit(String path) {
Properties pro = new Properties();
File file = new File("D:/DMQData/点名器config.properties");
try {
pro.load(new FileInputStream(file));
pro.setProperty("path", path);
FileOutputStream fo = new FileOutputStream(file);
//为了写入不乱码
OutputStreamWriter osw = new OutputStreamWriter(fo, "GBK");
pro.store(osw,"");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上边这个类是该有的异常处理和常见的给的excel文件不满足要求时候我们怎么做都有但是这个地方要说明一下:其中读取要导入文件的数据的时候文件不满足要求我们可以重写,只能是对于xlsx文件,xls文件删除数据的话会报:java.lang.NoClassDefFoundError感兴趣同学可以去了解下我搞了好久弄不出来,所以相应的地方我们加了判断如果是不满足条件的xls文件(没法重写)就直接抛异常给前边了,让前边捕获提示用户换文件;
然后就是无关紧要的封装的实体类的了:
package entity;
import java.util.Date;
public class Student {
private String name;
private String num = "1";
private String good = "1";
private String common = "0";
private String notCome = "0";
private Date lastTime ;
private Tchoose choose = Tchoose.优秀;
public Tchoose getChoose() {
return choose;
}
public void setChoose(Tchoose choose) {
this.choose = choose;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String getNotCome() {
return notCome;
}
public void setNotCome(String notCome) {
this.notCome = notCome;
}
public String getCommon() {
return common;
}
public void setCommon(String common) {
this.common = common;
}
public String getGood() {
return good;
}
public void setGood(String good) {
this.good = good;
}
public Date getLastTime() {
return lastTime;
}
public void setLastTime(Date lastTime) {
this.lastTime = lastTime;
}
public Student(String name, Date lastTime) {
this.name = name;
this.lastTime = lastTime;
}
public Student() {
lastTime = new Date();
}
public Student(String name) {
lastTime = new Date();
this.name = name;
}
public Student(String name, Tchoose tchoose) {
lastTime = new Date();
this.name = name;
this.choose = tchoose;
}
}
public enum Tchoose {
优秀,一般,缺勤;
}