这段时间在学校开了Java课程之后动手写了一个小项目——点餐系统,我会分四篇博客去记录整个项目。
一:客户端
二:服务端
三:数据库端
四:项目心得和移植到linux环境下遇到的问题
源代码已上传:http://download.youkuaiyun.com/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来传输信息,由于时间问题,以后再说吧。如果大家有好的方法,可以在底下回复我。