小型仓库管理系统的详细设计实现
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
小型仓库管理系统的详细设计及实现
1.系统设计概述
(1)问题域部分,设计构造一组为底层应用建立模型的类和对象,细化分析结果;由建立数据对象部分实现。
(2)人机交互部分,设计一组有关类接口视图的用户模型的类和对象,设计用户界面;由建立商品数据维护界面部分实现。
(3)任务管理部分,确定系统资源的分配,设计用于系统中类的行为控制的对象/类;由建立商品类别显示模型和商品类型模型部分以及建立商品数据维护界面部分共同实现。
(4)数据管理部分,确定持久对象的存储,将对象转换成数据库记录或表格;由连接数据库和建立商品数据访问对象部分实现。
2.连接数据库
要通过Java访问数据库,必须要使用JDBC(Java Data Base Connectivity,java数据库连接),访问不同的数据库,采用的JDBC驱动程序也不同。
连接Access采用Jdbc-odbc桥的方式比较方便,不需要引入额外的驱动程序包。
主流的数据库都提供了专门的JDBC驱动,如ORCALE、Sql Server、Sybase等等,对于微软的Access和Excel以及其他的一些小型的桌面数据库,可以通过JDBC-ODBC桥接的方式来访问。
通过编码,我们不需要在环境中配置ODBC数据源,就可以直接访问这些小型桌面数据库。
在应用系统中,数据库的连接参数一般都是写在配置文件当中,以防止数据库的属性的变化导致程序的修改。
对于本任务来说,Access是桌面数据库,我们需要知道的只是Access 数据库文件的路径,因此我们可以将Access数据库文件拷贝到发布路径下,从而可以通过编码获得数据库文件路径,而不需要使用配置文件。
package test.sample.dao;
import .URL;
import java.sql.Connection;
import java.sql.DriverManager;
public class DaoFactory {
static public Connection getConnection() throws Exception{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //加载数据库驱动
URL dbUrl =DaoFactory.class.getClassLoader().getResource("Mystock.mdb");
String dbPath=dbUrl.getPath(); //通过Java的类加载机制,获得数据库文件路径
if (dbPath.charAt(0)=='/'){ //去掉路径的第一个字符/
dbPath=dbPath.substring(1);
}
String url = "jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ="+dbPath;
Connection con = DriverManager.getConnection(url, "sa", "");
return con;
}
}
Class.forName方法的作用是通过类的完整路径名获得一个类的实例,在本例子中的作用是加载我们要使用的数据库驱动。
在使用DriverManager获得数据库连接前,必须通过此语句加载数据库驱动。
ClassLoader类负责管理Java编译后代码的加载,因此能够获取class文件的加载路径,也就是应用的发布路径。
我们把数据库文件放到发布路径下(即class文件所在路径),即可以通过ClassLoader类的getResource方法获得该文件的绝对路径。
由于获得的绝对路径的第一个字符是“/”,这是为了兼容不同的操作系统而导致的,这个符号在Access的数据库路径上是不合法的,需要去掉。
通过数据库url,提供数据库的驱动描述,以及数据库的路径,就可以使用DriverManager 类来获得数据库连接。
3.建立商品数据访问对象
从数据库中提取数据,以及向数据库中插入记录和删除数据,都必须使用SQL语句来完成。
因此数据访问对象的方法,实际上就是通过Java数据访问对象,在数据库数据和数据对象之间进行转换。
在数据库的访问中,我们需要用到几个数据访问对象:
1.java.sql.Connection:用来获得数据库连接。
2.java.sql.PreparedStatement和java.sql.Statement:都可以用来执行SQL语句。
其中PreparedStatement的功能较强,SQL 语句被预编译并且存储在PreparedStatement 对象中。
然后可以使用此对象高效地多次执行该语句。
PreparedStatement可以设置set方法设置参数。
以下是一个设置参数的示例中,con 表示一个活动连接:
PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 2000.00) ;
pstmt.setInt(2, 100) ;
pstmt.excuteUpdate();
SQL语句中需要赋值的部分可以用?代替,然后通过set方法来设置参数。
由于设计参数时不同的类型方法也不同,能够很好的处理不同的类型。
例如在设置日期型的参数时,可以直接通过setDate方法设置参数,而如果通过SQL语句的字符串拼写,则必须对日期进行格式化。
对于复杂的Insert、update和delete的SQL语句,以及需要多次执行而参数不同的SQL语句,使用PreparedStatement具有明显优势。
3.java.sql.ResultSet:从数据库库提取后的数据保存在ResultSet中,ResultSet提供的getter 方法能够方便的返回记录集中的数据。
程序代码设计如下:
package test.sample.dao;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import test.sample.bean.Product;
import test.sample.bean.ProductType;
public class ProductDao {
//提取商品数据,并转换为商品对象,存储到列表中
public static List getProductList() throws Exception{
Connection conn=DaoFactory.getConnection();
try{
Statement st = conn.createStatement();
String sql="select * from Product";
ResultSet rs = st.executeQuery(sql);
List list=new ArrayList();
while(rs.next()){
Product p=new Product();
p.setId(rs.getInt("ID"));
p.setName(rs.getString("NAME"));
p.setType(rs.getInt("TYPE"));
p.setCountInBox(rs.getString("COUNT_IN_BOX"));
p.setPrice(rs.getFloat("PRICE"));
p.setStock(rs.getInt("STOCK"));
list.add(p);
}
rs.close();
st.close();
return list;
}
finally{
conn.close();
}
}
//将一个商品对象插入到数据库
public static Product InsertProduct(Product p) throws Exception{
Connection conn=DaoFactory.getConnection();
try{
String sql="INSERT INTO Product(NAME,TYPE,COUNT_IN_BOX,PRICE,STOCK) VALUES(?,?,?,?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1,p.getName());
ps.setInt(2,p.getType());
ps.setString(3,p.getCountInBox());
ps.setFloat(4,p.getPrice());
ps.setInt(5,p.getStock());
ps.executeUpdate();
Statement st=conn.createStatement();
ResultSet rs=st.executeQuery("select max(ID) from Product");
rs.next();
int id = rs.getInt(1);
p.setId(id);
rs.close();
st.close();
ps.close();
return p;
}
finally{
conn.close();
}
}
//将一个商品对象插入到数据库
public static Product updateProduct(Product p) throws Exception{
Connection conn=DaoFactory.getConnection();
try{
String sql="UPDATE Product set
NAME=?,TYPE=?,COUNT_IN_BOX=?,PRICE=?,STOCK=? where ID=?";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1,p.getName());
ps.setInt(2,p.getType());
ps.setString(3,p.getCountInBox());
ps.setFloat(4,p.getPrice());
ps.setInt(5,p.getStock());
ps.setInt(6,p.getId());
ps.executeUpdate();
ps.close();
return p;
}
finally{
conn.close();
}
}
//删除一个商品记录
public static boolean deleteProduct(int id) throws Exception{
Connection conn=DaoFactory.getConnection();
try{
String sql="delete from Product where ID="+id;
Statement ps=conn.createStatement();
boolean b = ps.execute(sql);
ps.close();
return b;
}
finally{
conn.close();
}
}
//获取商品类型数据,并转换为商品类型对象,存储到列表
public static List getProductTypeList() throws Exception{
Connection conn=DaoFactory.getConnection();
try{
Statement st = conn.createStatement();
String sql="select * from ProductType";
ResultSet rs = st.executeQuery(sql);
List list=new ArrayList();
while(rs.next()){
ProductType pt=new ProductType();
pt.setId(rs.getInt("TYPE_ID"));
pt.setName(rs.getString("TYPE_NAME"));
pt.setRemark(rs.getString("REMARK"));
list.add(pt);
}
rs.close();
st.close();
return list;
}
finally{
conn.close();
}
}
}
通过上面的代码我们可以看到,从ProductDao调用方法的对象不需要关系数据库,而
只需要知道Product对象即可,无论我们如何修改“商品”数据的存储方法,都不会影响访问对象。
也就是说,我们通过ProductDao对象将对商品表数据的访问完全的封装了。
访问商品数据的对象甚至不知道商品数据是保存在什么样的数据库当中。
这就是面向对象设计的重要特性——封装。
ProductDao对象提供了“商品”数据的提取、插入、删除方法,提供了商品类别的提取方法。
事实上,这不是数据操作的全部,所有涉及商品的数据库操作,都可以放到ProductDao 对象中,如修改商品的名称,统计商品的库存等等。
4.建立数据对象
数据对象作为应用程序的数据载体,能够用于数据加工和数据传递,同时由于数据对象是无状态的,因此数据对象的使用能够应用实现更加地灵活。
数据对象(JavaBean)是一种特殊的对象,特殊的是在类的方法命名上遵守以下规则:1.如果类的成员变量的名字是xxx,那么为了更改或获取成员变量的值,即更改或获取属性,在类中可使用两个方法:getXxx(),用来获取属性;xxx;setXxx(),用来修改属性xxx.。
2.对于boolean类型的成员变量,即布尔逻辑类型的属性,允许使用"is"代替上面的"get"。
3.类中方法的访问属性都必须是public的。
4.类中如果有构造方法,那么这个构造方法也是public的并且是无参数的。
事实上,这种方式是面向对象设计的一个理念,即封装。
通过get和set 方法,将对象的外在表现和内部实现分离,从而为对象的修改和重用提供良好的基础。
例如:一个方法getName,它的作用是返回对象的名字,作为访问者无需知道这个名字是如何存储的,只需调用方法访问即可。
package test.sample.bean;
/**
* 商品对象
*/
public class Product {
private int id; //商品的唯一标识
private String name; //商品的名称
private int type; //商品的类型内码
private String countInBox; //每箱数量
private float price; //商品的价格
private int stock; //库存数量
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
= name;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getCountInBox() {
return countInBox;
}
public void setCountInBox(String countInBox) {
this.countInBox = countInBox;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
}
我们可以看到,上面的两个数据对象中,都是通过get和set方法对私有属性进行封装。
那么,不使用get 和set方法,直接将属性作为公开属性不行吗?单从程序访问的角度没有问题,我们可以直接访问公开属性,但同时也破坏了数据对象的封装性。
更为重要的是,数据对象这种形式已经广泛的为业界所承认,成为一种规则,绝大部分的开源软件通过get和set这种形式来访问数据,例如Hibernate,spring。
事实上,这种形式不仅仅在java中使用,在主流的面向对象语言中,都采用了这种形式。
5.建立商品列表显示模型和商品类型模型
为了能够将数据对象列表能够以表格(table)的形式进行显示,我们需要为表格显示控件JTable提供一个数据来源。
实现一个AbstractTableModel,能够为JTable控件提供数据,将商品对象列表以表格的形式进行显示。
在Java swing中以表格形式显示数据的标准控件是JTable,一般的情况下,数据以对象列表或者对象数组的形式存储,表格的每一行显示一个对象的信息,表格的每一列显示对象某个属性的信息。
为了能够方便的使用JTable,Java提供了专门支持它的数据模型TableModel。
TableModel 负责将数据整理为JTable显示所需要的形式,是介于基础数据和显示控件之间的代理。
在创建JTable时,可以采用带参数的创建方法,将TableModel作为参数传递给JTable。
JTable的数据显示是通过TableModel进行驱动的,因此修改TableModel中的数据,能够通过调用方法刷新JTable的数据显示。
为了更加方便的处理数据和数据的显示,我们需要从AbstractTableModel类继承,实现自己的TableModel类。
AbstractTableModel是一个抽象数据类型,我们有三个方法必须实现: public int getColumnCount() // 获得要显示的数据的列数
public int getRowCount() // 获得要显示的数据的行数
public Object getValueAt(int row, int col) // 获得一个单元格的数据
以上三个方法的实现可以根据原始数据和显示需求来实现,例如getColumnCount可以根据需要显示的属性的个数确定,getRowCount可以通过数据记录的个数确定。
为了支持对数据的修改操作,我们还可以在TableModel中添加一些数据修改的方法。
package test.sample.model;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import test.sample.bean.Product;
import test.sample.dao.ProductDao;
public class ProductModel extends AbstractTableModel {
private List pList=new ArrayList();
public void loadData() throws Exception{
pList = ProductDao.getProductList();
}
//列标题
String[] headers = { "商品内码", "商品名称", "商品类型", "单位数量", "价格", "库存"}; //获得列数
public int getColumnCount() {
return headers.length;
}
//获得行数
public int getRowCount() {
return pList.size();
}
//获得一个单元格的数据
public Object getValueAt(int row, int col) {
if ((row<0) ||(row>=pList.size())) return "";
Product p = (Product) pList.get(row);
switch (col) {
case 0: return new Integer(p.getId());
case 1: return p.getName();
case 2: return ProductTypeEnum.getTypeName(p.getType());
case 3: return p.getCountInBox();
case 4: return new Float(p.getPrice());
case 5: return new Integer(p.getStock());
}
return "";
}
public Product getProduct(int row){
return (Product) pList.get(row);
}
//获得列的名字
@Override
public String getColumnName(int col) {
return headers[col];
}
//添加一个商品对象
public void addProduct(Product p) throws Exception{
ProductDao.InsertProduct(p);
pList.add(p);
this.fireTableRowsInserted(pList.size()-1, pList.size()-1);
}
//更新一个商品对象
public void updateProduct(int index,Product p) throws Exception{
if ((index<0) ||(index>=pList.size())) return ;
ProductDao.updateProduct(p);
pList.set(index, p);
this.fireTableRowsUpdated(index, index);
}
//删除一个商品对象
public void deleteProduct(int index) throws Exception{
if ((index<0) ||(index>=pList.size())) return ;
Product p1=getProduct(index);
ProductDao.deleteProduct(p1.getId());
pList.remove(index);
this.fireTableRowsDeleted(index, index);
}
}
ProductModel扩展类AbstractTableModel类,以从数据库提出的对象列表作为数据源。
并根据数据列表信息实现了基类的三个虚方法:getRowCount()、getColumnCount()、getValueAt(int row, int column);
为了支持数据的增加、删除和修改功能,ProductModel还实现了三个方法:1.addProduct(Product p):添加一个商品信息。
2.updateProduct(int index,Product p):更新某行的商品信息。
3.deleteProduct(int index):删除某行的商品信息。
以上三个方法直接调用数据库操作对象的方法,进行数据的添加、删除和修改,数据库修改成功后,修改显示列表,并调用方法fireTableRowsInserted 、fireTableRowsUpdated、fireTableRowsDeleted ,激活JTable的显示更新。
另外、对于商品类型属性,在商品对象中保存的是类型的内码,而显示的时候,需要显示类型的名称,在这种情况下,我们需要做通过商品类别模型获得类型名称。
商品表的商品类别保存的是商品类别内码,或者说,商品类别内码是商品表的一个外键。
类别表保存了所有商品类别的信息,包括类别ID,类别名称和类别描述三个字段。
虽然商品表中使用类别ID记录商品的类别,但是在列表中显示的时候,需要显示给用户商品类别的类别名称。
在新增商品信息时,也需要将商品类别名称做成下拉列表,在列表中选择商品类别,同时在商品对象中要保存的却是类别ID。
以上的情况都要求一个对象来管理商品类别中类别ID和类别名称的映射关系。
作为商品的关联信息,商品类别的在商品表中存储时使用类别ID,而展示时总是展示类别名称,因此通过类别ID获得类别名称,以及通过类别名称获得类别ID的功能是必须的。
package test.sample.model;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import test.sample.bean.ProductType;
import test.sample.dao.ProductDao;
public class ProductTypeEnum {
static private Map map = null;
static private Map nameMap = null;
static private String[] names;
static private List dataList = null;
private static List getDataList() {
if (dataList == null) {
try {
dataList = ProductDao.getProductTypeList();
} catch (Exception e) {
e.printStackTrace();
}
}
return dataList;
}
static public String getTypeName(int type) {
if (map == null) {
map = new HashMap();
List list = getDataList();
for (int i = 0; i < list.size(); i++) {
ProductType pt = (ProductType) list.get(i);
map.put("" + pt.getId(), pt);
}
}
ProductType p = (ProductType) map.get("" + type);
if (p != null)
return p.getName();
else
return "";
}
static public int getNameId(String name) {
if (nameMap == null) {
nameMap = new HashMap();
List list = getDataList();
for (int i = 0; i < list.size(); i++) {
ProductType pt = (ProductType) list.get(i);
nameMap.put(pt.getName(), pt);
}
}
ProductType p = (ProductType) nameMap.get(name);
if (p != null)
return p.getId();
else
return -1;
}
public static String[] getNames() {
if (names == null) {
List list = getDataList();
names = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
ProductType pt = (ProductType) list.get(i);
names[i] = pt.getName();
}
}
return names;
}
}
6.构造商品数据维护界面
整个界面可以分成三大部分:显示列表的部分、显示录入控件的部分、弹出菜单。
列表的显示通过JTable可以完成,弹出菜单使用JpopupMenu控件来完成,而数据的录入部分则需要用到Jlabel、JcomboBox、JtextField、JformattedTextField、Jbutton等控件。
由于录入控件较多,我们采用固定位置的布局方式。
package test.sample.view;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import test.sample.bean.Product;
import test.sample.model.ProductModel;
import test.sample.model.ProductTypeEnum;
public class ProductView extends JFrame {
private JButton buttonInsert; // 插入按钮
private JButton buttonUpdate; // 修改按钮
private JTextField jxt1; // 商品名称录入控件
private JTextField jxt2; // 商品数量录入控件
private JComboBox jcb1; // 商品类别下拉列表
private JFormattedTextField jft1; // 价格录入
private JFormattedTextField jft2; // 库存录入
private JPopupMenu popup; // 弹出菜单
private JTable jt; // 商品列表显示控件
private JScrollPane jsp; // 商品列表容器
private JPanel jp1; // 数据明细控件容器
private ProductModel pm; // 商品模型实例
private Container rootPanel; // 主界面容器
// 让界面居与屏幕中间
private void centerFrame() {
setSize(600, 400); // 设置界面大小
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;
int frameH = getHeight();
int frameW = getWidth(); // 计算和设置界面居中的位置
setLocation((screenWidth - frameW) / 2, (screenHeight - frameH) / 2);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
// 初始化弹出菜单
private void initPopupMenu() {
popup = new JPopupMenu();
JMenuItem item = new JMenuItem("删除商品信息");
item.addActionListener(new ActionListener() { // 鼠标点击处理匿名内部类
public void actionPerformed(ActionEvent e) {
int index = jt.getSelectedRow(); // 确定在JTable上右键点击位置的行
if (index >= 0) {
Integer pId = (Integer) jt.getValueAt(index, 0);
String pName = (String) jt.getValueAt(index, 1);
int i = JOptionPane.showConfirmDialog(null,
"确定要删除商品——" + pName + "?", "标题",
JOptionPane.YES_NO_OPTION); // 弹出确认对话框
if (i == JOptionPane.YES_OPTION)
try { // 删除数据库记录
pm.deleteProduct(index); // 删除显示记录
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
});
popup.add(item);
jt.addMouseListener(new MouseAdapter() { // 鼠标右键监听匿名内部类
public void mouseReleased(MouseEvent event) {
final int row = jt.rowAtPoint(event.getPoint());
if (row != -1) {
jt.setRowSelectionInterval(row, row); // 高亮选择右键点击的行
}
if (event.isPopupTrigger())
popup.show(event.getComponent(), event.getX(),
event.getY());
}
});
}
// 初始化录入控件
public void iniInsertPanel() throws Exception {
JLabel jl1 = new JLabel();
jl1.setText("商品名称");
jl1.setBounds(10, 10, 60, 20);
jp1.add(jl1);
jxt1 = new JTextField(10);
jxt1.setBounds(75, 10, 200, 20);
jp1.add(jxt1);
JLabel jl2 = new JLabel();
jl2.setText("商品类型");
jl2.setBounds(285, 10, 60, 20);
jp1.add(jl2);
jcb1 = new JComboBox(ProductTypeEnum.getNames());
jcb1.setBounds(350, 10, 100, 20);
jp1.add(jcb1, BorderLayout.EAST);
JLabel jl3 = new JLabel();
jl3.setText("单位数量");
jp1.add(jl3);
jl3.setBounds(10, 40, 60, 20);
jxt2 = new JTextField(6);
jxt2.setBounds(75, 40, 80, 20);
jp1.add(jxt2);
JLabel jl4 = new JLabel();
jl4.setText("单价");
jl4.setBounds(160, 40, 60, 20);
jp1.add(jl4);
jft1 = new JFormattedTextField();
jft1.setColumns(5);
jft1.setBounds(195, 40, 80, 20);
jp1.add(jft1);
JLabel jl5 = new JLabel();
jl5.setText("库存");
jl5.setBounds(310, 40, 40, 20);
jp1.add(jl5);
jft2 = new JFormattedTextField();
jft2.setColumns(5);
jft2.setBounds(350, 40, 100, 20);
jp1.add(jft2);
buttonInsert = new JButton("添加");
buttonInsert.setBounds(460, 10, 60, 50);
// 添加按钮点击事件
buttonInsert.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
try {
doInsertProduct();
} catch (Exception e) {
e.printStackTrace();
}
}
});
jp1.add(buttonInsert);
buttonUpdate = new JButton("修改");
buttonUpdate.setBounds(530, 10, 60, 50);
// 修改按钮点击事件
buttonUpdate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
try {
doUpdateProduct();
} catch (Exception e) {
e.printStackTrace();
}
}
});
jp1.add(buttonUpdate);
}
// 从录入控件中组织一个商品信息
private Product getProductInfo() {
Product p = new Product();
if (jxt1.getText().equals("")){
JOptionPane.showMessageDialog(buttonInsert,"请输入商品信息!","提示",JOptionPane.WARNING_MESSAGE);
return null;
}
p.setName(jxt1.getText());
int typeId = ProductTypeEnum.getNameId((String) jcb1.getSelectedItem());
p.setType(typeId);
p.setCountInBox(jxt2.getText());
p.setPrice(Float.parseFloat(jft1.getText()));
p.setStock(Integer.parseInt(jft2.getText()));
return p;
}
// 新增商品数据方法
private void doInsertProduct() throws Exception {
if (getProductInfo()==null)
return;
pm.addProduct(getProductInfo());
int row=pm.getRowCount();
jt.setRowSelectionInterval(row-1,row-1);
Rectangle rect = jt.getCellRect(row-1, 0, true);
jt.scrollRectToVisible(rect);
}
// 更新商品数据
private void doUpdateProduct() throws Exception {
Product p1 = getProductInfo();
int selectedRowIndex = jt.getSelectedRow();
if (selectedRowIndex<0) {
JOptionPane.showMessageDialog(this,"请选择要修改的商品!","提示",JOptionPane.WARNING_MESSAGE);
return;
}
int id = ((Integer) pm.getValueAt(selectedRowIndex, 0)).intValue();
p1.setId(id);
pm.updateProduct(selectedRowIndex, p1);
}
public ProductView() {
super("商品信息");
centerFrame();
try {
pm = new ProductModel(); // 创建数据模型
pm.loadData(); // 提取数据
jt = new JTable(pm);
ListSelectionModel model = jt.getSelectionModel();
// 设置JTable的行选择方式为单选
jt.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// 监听JTable选择行事件,将选择行数据内容显示在窗口底部的控件中。
model.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
int selectedRowIndex = jt.getSelectedRow();
if (selectedRowIndex > 0) {
Product p = pm.getProduct(selectedRowIndex);
jxt1.setText(p.getName());
jcb1.setSelectedIndex(p.getType() - 1);
jxt2.setText(p.getCountInBox());
jft1.setValue(p.getPrice());
jft2.setValue(p.getStock());
}
}
});
jsp = new JScrollPane(jt);
initPopupMenu();
rootPanel = getContentPane();
rootPanel.setLayout(null);
jsp.setBounds(0, 0, 594, 300);
jp1 = new JPanel(null);
rootPanel.add(jp1);
jp1.setBounds(0, 300, 600, 80);
rootPanel.add(jsp);
iniInsertPanel();
// 监听窗口大小变化事件,调整明细Panel的大小
this.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent ce) {
jsp.setBounds(0, 0, rootPanel.getWidth() - 4, rootPanel
.getHeight() - 80);
jsp.updateUI();
jp1.setBounds(0, rootPanel.getHeight() - 80, rootPanel
.getWidth() - 4, 80);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ProductView ps = new ProductView();
ps.setVisible(true);
} }
在本实例中所有控件的事件监听和弹出菜单的事件响应都是使用匿名内部类来完成的,例如按钮的点击事件监听代码。
匿名内部类可以使代码更加简洁、紧凑,模块化程度更高。
固定坐标的布局:本实例采用固定坐标的布局,这种方式可以使控件布局较整齐,但是缺点是确定坐标比较麻烦(如果有图形化的界面构建工具会方便的多)。
作为参考,本实例监听了界面的大小变化的事件,并动态修改两个Panel的位置、高等和宽度,以适应界面的变化。
在设置界面大小的时候,要注意rootPanel并不等于界面的大小,因为界面中除了
rootPanel,还包括界面的标题栏和边框,一般情况下,标题栏的高度为20,边框的宽度为3。