这段时间在学校开了Java课程之后动手写了一个小项目——点餐系统,我会分四篇博客去记录整个项目。
一:客户端
二:服务端
三:数据库端
四:项目心得和移植到linux环境下遇到的问题
源代码已上传:http://download.csdn.net/detail/u010214003/8138421
源码中包含客户端和服务端,数据库文件没有包含,在看完这几篇博客之后,可以自己动手去添加一些数据编译整个工程。
客户端详解:
一.界面分析
1.初始界面
用jfame的setUndecorated(true函数隐藏窗口标题栏,
然后就可以自定义自己的窗口,可以通过添加按钮和标签的方式实现最小化和关闭
调用函数;AWTUtilities.setWindowOpaque(this, false) 设置窗体透明
创建一个JLabel对象,用你想要的背景图片去填充,然后利用jframe的三层结构,把JLabel对象添加到分成窗格的最底层作伪背景图片,然后把
内容窗格设置为透明,即可实现上图效果,时钟是用Time单独开始一个线程画上去的
窗体背景代码:这里只是截取代码分析,具体实现参见源码
<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="white-space:pre"> </span>//获取屏幕大小 <span style="white-space:pre"> </span>Dimension screenSize =Toolkit.getDefaultToolkit().getScreenSize(); <span style="white-space:pre"> </span> <span style="white-space:pre"> </span>//设置窗体的位置和大小 <span style="white-space:pre"> </span>setBounds((screenSize.width-640)/2,(screenSize.height-480)/2,640,480); //不要标题栏的修饰 setUndecorated(true); //设置窗体透明 AWTUtilities.setWindowOpaque(this, false); background = init.backImage;// 背景图片 bgLabel = new JLabel(background);// 把背景图片显示在一个标签里面 // 把标签的大小位置设置为图片刚好填充整个面板 bgLabel.setBounds(0, 0, background.getIconWidth(), background.getIconHeight()); // 把内容窗格转化为JPanel,否则不能用方法setOpaque()来使内容窗格透明 JPanel imagePanel = (JPanel)this.getContentPane(); imagePanel.setOpaque(false); // 内容窗格默认的布局管理器为BorderLayout imagePanel.setLayout(new FlowLayout()); getLayeredPane().setLayout(null); // 把imagePanel.add(new JButton("测试按钮")); //背景图片添加到分层窗格的最底层作为背景 getLayeredPane().add(bgLabel, new Integer(Integer.MIN_VALUE)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(background.getIconWidth(), background.getIconHeight()); setResizable(false);</span>
最小化和关闭用按钮的话就去实现ActionListenr接口的actionPerformed函数
这里使用的是标签,要实现MouseListener接口的五个接口函数(有些用不到也要实现),代码如下
<pre name="code" class="java"><span style="font-family:KaiTi_GB2312;font-size:18px;">//添加监听 minButton.addMouseListener(this); exitButton.addMouseListener(this); //实现接口函数 <span style="white-space:pre"> </span>public void mouseClicked(MouseEvent e) { if(e.getSource() == minButton) { setExtendedState(JFrame.ICONIFIED); } else if(e.getSource() == exitButton) { dispose(); } } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { }</span>
定时器线程代码:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package mytool; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.Line2D; import java.util.Date; import javax.swing.JLabel; import javax.swing.Timer; public class CreateClock extends JLabel implements ActionListener { public Timer timer; double pointsx[]=new double[60]; double pointsy[]=new double[60]; double pointmx[]=new double[60]; double pointmy[]=new double[60]; double pointhx[]=new double[60]; double pointhy[]=new double[60]; public int second,hour,minute; public Date date; public Line2D secondline,hourline,minuteline; public int a,b,c; public CreateClock() { ///初始化各个点的坐标 pointsx[0]=pointmx[0]=pointhx[0]=0; pointsy[0]=-70; pointmy[0]=-55; pointhy[0]=-40; double angle=6* Math.PI/180; for(int i=0;i<59;i++) { pointsx[i+1]=pointsx[i]*Math.cos(angle)-pointsy[i]*Math.sin(angle); pointsy[i+1]=pointsy[i]*Math.cos(angle)+pointsx[i]*Math.sin(angle); pointmx[i+1]=pointmx[i]*Math.cos(angle)-pointmy[i]*Math.sin(angle); pointmy[i+1]=pointmy[i]*Math.cos(angle)+pointmx[i]*Math.sin(angle); pointhx[i+1]=pointhx[i]*Math.cos(angle)-pointhy[i]*Math.sin(angle); pointhy[i+1]=pointhy[i]*Math.cos(angle)+pointhx[i]*Math.sin(angle); } for(int i=0;i<60;i++) { pointsx[i]=pointsx[i]+80; pointsy[i]=pointsy[i]+100; pointmx[i]=pointmx[i]+80; pointmy[i]=pointmy[i]+100; pointhx[i]=pointhx[i]+80; pointhy[i]=pointhy[i]+100; } /////初始化各条线 a=b=c=0; secondline=new Line2D.Double(0,0,0,0); minuteline=new Line2D.Double(0,0,0,0); hourline=new Line2D.Double(0,0,0,0); //////////////////////////////// //////设置定时器 timer=new Timer(1000,this); timer.start(); } public void paint(Graphics g) { ///画表盘 for(int i=0;i<60;i++) { int m=(int)pointsx[i]; int n=(int)pointsy[i]; if(i%5==0) { g.setColor(Color.blue); g.fillOval(m-4, n-4, 8, 8); } else { g.setColor(Color.green); g.fillOval(m-2,n-2,4,4); } } g.fillOval(75,95,10,10); ///画表针 Graphics2D grap=(Graphics2D)g; BasicStroke bb=new BasicStroke(2f,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER); grap.setStroke(bb); grap.setColor(Color.green); grap.draw(secondline); BasicStroke bs=new BasicStroke(3f,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER); grap.setStroke(bs); grap.setColor(Color.BLUE); grap.draw(minuteline); BasicStroke bss=new BasicStroke(6f,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER); grap.setStroke(bss); grap.setColor(Color.YELLOW); grap.draw(hourline); } public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub if(arg0.getSource()==timer) { date=new Date(); String str=date.toString(); second=Integer.parseInt(str.substring(17,19)); minute=Integer.parseInt(str.substring(14,16)); hour=Integer.parseInt(str.substring(11,13)); int h=hour%12; a=second; b=minute; c=h*5+minute/12; secondline.setLine(80,100,pointsx[a],pointsy[a]); minuteline.setLine(80,100,pointmx[b],pointmy[b]); hourline.setLine(80,100,pointhx[c],pointhy[c]); repaint(); } } } </span>
2.主界面分析
这里在进入主界面的时候会进行登陆,服务端根据登陆的账号判断你是普通学生还是商家,在登陆后会根据返回信息确定进入哪个身份的界面
,这里只是简单介绍一下学生界面。
主界面使用一个JPanel对象mainPanel,对此mainPanel使用盒式布局,然后每一个功能区继承重写一个JPanel,添加到mainPanel,
当鼠标点击功能标签时,在监听标签的接口函数中调用card.show()函数进行JPanel的切换。
这里还对每个功能标签准备了两张图片,在点击相应的标签时,改变其背景图片与其他三张不一样就可以实现图上的效果。
监听函数中相应的代码如下:
<span style="font-family:KaiTi_GB2312;font-size:18px;">//添加监听 <span style="white-space:pre"> </span>每日推荐.addMouseListener(this); 店铺一览.addMouseListener(this); 我的菜单.addMouseListener(this); 吐槽专区.addMouseListener(this); public void mouseClicked(MouseEvent e) { if(e.getSource() == 每日推荐) { <span style="white-space:pre"> </span> //改变标签的图片 每日推荐.setIcon(init.suggestImage_enter); 店铺一览.setIcon(init.shopImage); 我的菜单.setIcon(init.myOrderImage); 吐槽专区.setIcon(init.opinionImage); <span style="white-space:pre"> </span>//切换面板 card.show(mainPanel,"suggest"); } else if (e.getSource() == 店铺一览) { 每日推荐.setIcon(init.suggestImage); 店铺一览.setIcon(init.shopImage_enter); 我的菜单.setIcon(init.myOrderImage); 吐槽专区.setIcon(init.opinionImage); card.show(mainPanel,"shop"); } else if(e.getSource() == 我的菜单) { 每日推荐.setIcon(init.suggestImage); 店铺一览.setIcon(init.shopImage); 我的菜单.setIcon(init.myOrderImage_enter); 吐槽专区.setIcon(init.opinionImage); myOrderPanel.update(); card.show(mainPanel,"myOrder"); } else if(e.getSource() == 吐槽专区) { 每日推荐.setIcon(init.suggestImage); 店铺一览.setIcon(init.shopImage); 我的菜单.setIcon(init.myOrderImage); 吐槽专区.setIcon(init.opinionImage_enter); JOptionPane.showMessageDialog(mainWindow.getContentPane(), "时间紧迫,此功能尚未开通,敬请期待!!", "系统信息", JOptionPane.INFORMATION_MESSAGE); } <span style="white-space:pre"> </span> } <span style="white-space:pre"> </span>public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { }</span>
二.Socket连接服务端
在登陆界面要求用户输入账号,密码,ip地址和端口号,客户端城尝试与服务端建立连接,验证用户并返回用户信息,
根据返回信息判断进入哪一个界面
<span style="font-family:KaiTi_GB2312;font-size:18px;"><span style="white-space:pre"> </span>String ip = loginPanel.ipTextField.getText(), account = loginPanel.accountTextField.getText(), passwd = new String(loginPanel.passwdTextField.getPassword()); int port = Integer.parseInt(loginPanel.portTextField.getText()); if(clientSocket.isConnected()) {} else { try { InetAddress address = InetAddress.getByName(ip); InetSocketAddress socketAddress = new InetSocketAddress(address,port); clientSocket.connect(socketAddress); } catch (IOException e1) { JOptionPane.showMessageDialog(getContentPane(), "网络连接异常", "系统信息", JOptionPane.ERROR_MESSAGE); } try { in = new DataInputStream(clientSocket.getInputStream()); out = new DataOutputStream(clientSocket.getOutputStream()); out.writeUTF("LOGIN"); out.writeUTF(account); out.writeUTF(passwd); String reply = in.readUTF(); if(reply.equals("student")) { studentPanel = new StudentPanel(init,in,out,this); mainPanel.add("student",studentPanel); card.show(mainPanel,"student"); } else if(reply.equals("seller")) { sellerPanel = new SellerPanel(init,in,out); mainPanel.add("seller",sellerPanel); card.show(mainPanel,"seller"); } else if(reply.equals("defeat")) { JOptionPane.showMessageDialog(getContentPane(), "用户名或密码输入错误", "系统信息", JOptionPane.ERROR_MESSAGE); } } catch (IOException e1) { } }</span>
三.功能分析
功能简介:
该点餐系统在用户(学生)登陆后,会从服务器端获取当天的推荐菜单信息,店铺信息和店铺商品信息。用户可以从推荐界面和店铺里进行点餐,并在我的菜单里修改菜单,提交订单和付款。点餐系统在商家登陆后可以管理自己店铺的菜单,获取学生的订单并对订单进行处理,然后反馈信息给服务端,所有与数据库进行的信息交互都在服务端完成,功能比较简单。
1.推荐菜单
先从服务器端获取信息,然后显示用JTabel来显示,并在JTabel中添加JCheckBox来直接通过对JCheckBox的操作来选中和取消相应的菜单项。
代码:SuggetPanel.java
<span style="font-family:KaiTi_GB2312;font-size:18px;">package mypanel; import init.Init; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.LinkedList; import javax.swing.DefaultCellEditor; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import mytool.Food; import mytool.SuggestTabelModel; import mytool.SuggestTableCellRenderer; public class SuggestPanel extends JPanel implements ActionListener{ DataInputStream in = null;; DataOutputStream out = null; public int food_num; public LinkedList<Food> food; public LinkedList<Food> selectfood; final String[] headers = {"编号","菜名","店铺","类别","价格/元","推荐度",""}; Object[][] cellData = null; JTable table; SuggestTabelModel model; JScrollPane jscrollPane; private TableColumn column = null; private JButton enterButton; public SuggestPanel(Init init,DataInputStream in,DataOutputStream out) { int i=0; selectfood = new LinkedList<Food>(); model = new SuggestTabelModel(cellData,headers); table = new JTable(model); jscrollPane = new JScrollPane(table); jscrollPane.setPreferredSize(new Dimension(340, 270)); enterButton = new JButton("加入菜单"); enterButton.setPreferredSize(new Dimension(340,30)); add(jscrollPane); add(enterButton); enterButton.addActionListener(this); //居中显示 DefaultTableCellRenderer r=new DefaultTableCellRenderer(); r.setHorizontalAlignment(JLabel.CENTER); table.setDefaultRenderer(Object.class,r); column = table.getColumnModel().getColumn(0); column.setPreferredWidth(120); column =table.getColumnModel().getColumn(1); column.setPreferredWidth(140); column =table.getColumnModel().getColumn(2); column.setPreferredWidth(170); column =table.getColumnModel().getColumn(6); column.setPreferredWidth(30); try { model.setRowCount(0); food_num = in.readInt(); System.out.println(food_num); food = new LinkedList<>(); for(i=0;i<food_num;i++) { Food tempfood=new Food(); tempfood.num = in.readInt(); tempfood.name = in.readUTF(); tempfood.shop = in.readUTF(); tempfood.type = in.readUTF(); tempfood.price = in.readFloat(); tempfood.satisfication = in.readUTF(); food.add(tempfood); } } catch(IOException e) { } for(i=0;i<food.size();i++) { model.addRow(new Object[]{food.get(i).num,food.get(i).name,food.get(i).shop,food.get(i).type ,food.get(i).price,food.get(i).satisfication,new Boolean(false)}); } table.getColumnModel().getColumn(6).setCellEditor (new DefaultCellEditor(new JCheckBox())); table.getColumnModel().getColumn(6).setCellRenderer (new SuggestTableCellRenderer()); table.invalidate(); } public void actionPerformed(ActionEvent e) { if(e.getSource() == enterButton) { for(int i=0;i<food_num;i++) if(Boolean.parseBoolean(table.getValueAt(i, 6).toString()) == true) { System.out.println(table.getValueAt(i,1).toString()); selectfood.add(food.get(i)); table.setValueAt(false, i, 6); } } } } </span>
2.店家预览
系统把从服务端获取的店铺信息已JTable的形式显示出来,用户可以在对应店铺的选中条上双击进入店铺
代码:ShopPanel.java
<span style="font-family:KaiTi_GB2312;font-size:18px;">package mypanel; import java.awt.CardLayout; import java.awt.Dimension; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; import java.util.LinkedList; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import mytool.Food; import mytool.Shop; import mytool.SuggestTabelModel; public class ShopPanel extends JPanel{ LinkedList<Shop> shop; public LinkedList<Food> orderFood; public LinkedList<Food> shop1; public LinkedList<Food> shop2; public LinkedList<Food> shop3; int shop_num,shop1_num,shop2_num,shop3_num; public JTable table; SuggestTabelModel model; JScrollPane jscrollPane; final String[] headers = {"序号","编号","店铺名"}; Object[][] cellData = null; private TableColumn column = null; public ShopPanel(StudentPanel studentPanel) { shop = new LinkedList<Shop>(); orderFood = new LinkedList<Food>(); shop1 = new LinkedList<Food>(); shop2 = new LinkedList<Food>(); shop3 = new LinkedList<Food>(); model = new SuggestTabelModel(cellData,headers); table = new JTable(model); jscrollPane = new JScrollPane(table); jscrollPane.setPreferredSize(new Dimension(340, 310)); add(jscrollPane); //居中显示 DefaultTableCellRenderer r=new DefaultTableCellRenderer(); r.setHorizontalAlignment(JLabel.CENTER); table.setDefaultRenderer(Object.class,r); // column = table.getColumnModel().getColumn(0); // column.setPreferredWidth(120); // column =table.getColumnModel().getColumn(1); // column.setPreferredWidth(140); // column =table.getColumnModel().getColumn(2); // column.setPreferredWidth(170); // column =table.getColumnModel().getColumn(6); // column.setPreferredWidth(30); try { shop_num = studentPanel.in.readInt(); System.out.println(shop_num); for(int i=0;i<shop_num;i++) { Shop tempshop=new Shop(); tempshop.num = studentPanel.in.readInt(); tempshop.name = studentPanel.in.readUTF(); shop.add(tempshop); } shop1_num = studentPanel.in.readInt(); for(int i=0;i<shop1_num;i++) { Food tempfood=new Food(); tempfood.num = studentPanel.in.readInt(); tempfood.name = studentPanel.in.readUTF(); tempfood.shop = "龙鑫赣味小炒"; tempfood.type = studentPanel.in.readUTF(); tempfood.price = studentPanel.in.readFloat(); shop1.add(tempfood); } shop2_num = studentPanel.in.readInt(); for(int i=0;i<shop2_num;i++) { Food tempfood=new Food(); tempfood.num = studentPanel.in.readInt(); tempfood.name = studentPanel.in.readUTF(); tempfood.shop = "牛肉面馆"; tempfood.type = studentPanel.in.readUTF(); tempfood.price = studentPanel.in.readFloat(); shop2.add(tempfood); } shop3_num = studentPanel.in.readInt(); for(int i=0;i<shop3_num;i++) { Food tempfood=new Food(); tempfood.num = studentPanel.in.readInt(); tempfood.name = studentPanel.in.readUTF(); tempfood.shop = "dashitou"; tempfood.type = studentPanel.in.readUTF(); tempfood.price = studentPanel.in.readFloat(); shop3.add(tempfood); } } catch(IOException e) { } model.setRowCount(0); for(int i=0;i<shop.size();i++) { model.addRow(new Object[]{i+1,shop.get(i).num,shop.get(i).name}); } table.addMouseListener(studentPanel); // new MouseAdapter() // { // public void mouseClicked(MouseEvent e) // { // if(e.getClickCount() == 2) // { // int row =((JTable)e.getSource()).rowAtPoint(e.getPoint()); //获得行位置 // int col=((JTable)e.getSource()).columnAtPoint(e.getPoint()); //获得列位置 // System.out.println("行号:"+row+"列号:"+col+"数据:"); // if(row == 0) // { // shopFoodPanel.updatePanel(1); // studentPanel.card.show(studentPanel, "foodPanel"); // } // else if(row == 1) // { // // } // else if(row == 2) // { // // } // } // else return; // } // }); table.invalidate(); } public ShopPanel() { } } </span>
3.我的菜单
在用户选择了菜单后系统会添加到我的菜单里,用户可以在该功能界面进行预览和修改菜单,确认后可以提交并进入付款界面
对应的类为MyOrderPanel.java和PayPanel.java,代码在源码中查看,不再贴出来。
4.吐槽专区
该功能尚未实现,打算用UDP来传输信息,由于时间问题,以后再说吧。如果大家有好的方法,可以在底下回复我。