就是个人学习的笔记,按照下面的Demo一个一个复制粘贴跑起来大概就会使用Swing了,建议先从上半部分开始看

上半部分

【Java基础】swing-图形界面学习(上)

八.菜单

GUI的菜单分为 菜单栏JMenuBar菜单JMenu菜单项JMenuItem

8.1.菜单栏和菜单

    @Testpublic void MenuAndJMenuBar() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);// 菜单栏JMenuBar mb = new JMenuBar();// 菜单JMenu mHero = new JMenu("英雄");JMenu mItem = new JMenu("道具");JMenu mWord = new JMenu("符文");JMenu mSummon = new JMenu("召唤师");JMenu mTalent = new JMenu("天赋树");// 把菜单加入到菜单栏mb.add(mHero);mb.add(mItem);mb.add(mWord);mb.add(mSummon);mb.add(mTalent);// 把菜单栏加入到frame,这里用的是set而非addframe.setJMenuBar(mb);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

8.2.菜单项

@Testpublic void MenuItem() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 400);frame.setLocation(200, 200);JMenuBar mb = new JMenuBar();JMenu mHero = new JMenu("英雄");JMenu mItem = new JMenu("道具");JMenu mWord = new JMenu("符文");JMenu mSummon = new JMenu("召唤师");JMenu mTalent = new JMenu("天赋树");// 菜单项mHero.add(new JMenuItem("近战-Warriar"));mHero.add(new JMenuItem("远程-Range"));mHero.add(new JMenuItem("物理-physical"));mHero.add(new JMenuItem("坦克-Tank"));mHero.add(new JMenuItem("法系-Mage"));mHero.add(new JMenuItem("辅助-Support"));mHero.add(new JMenuItem("打野-Jungle"));mHero.add(new JMenuItem("突进-Charge"));mHero.add(new JMenuItem("男性-Boy"));mHero.add(new JMenuItem("女性-Girl"));// 分隔符mHero.addSeparator();mHero.add(new JMenuItem("所有-All"));mb.add(mHero);mb.add(mItem);mb.add(mWord);mb.add(mSummon);mb.add(mTalent);frame.setJMenuBar(mb);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

8.3.练习-完成一个完整的记事本界面

    @Testpublic void NotePad() throws InterruptedException {//创建容器JFrame frame = new JFrame("记事本");//frame.setSize(400, 400);frame.setLocation(200, 200);//创建菜单栏JMenuBar menuBar = new JMenuBar();//创建菜单JMenu document = new JMenu("文件(A)");JMenu editor = new JMenu("编辑(E)");JMenu layout = new JMenu("格式(O)");JMenu viewing = new JMenu("查看(V)");JMenu help = new JMenu("帮助(H)");// 文件菜单添加菜单项document.add(new JMenuItem("新建(N)                         Ctrl+N"));document.add(new JMenuItem("打开(O)                         Ctrl+O"));document.add(new JMenuItem("保存(S)                         Ctrl+S"));document.add(new JMenuItem("另存为(N)"));document.addSeparator();//分割线document.add(new JMenuItem("页面设置(U)"));document.add(new JMenuItem("打印(P)                         Ctrl+P"));document.addSeparator();//分割线document.add(new JMenuItem("退出(X)"));// 编辑项菜单添加菜单项editor.add(new JMenuItem("撤销(U)                         Ctrl+Z"));editor.addSeparator();//分割线editor.add(new JMenuItem("剪切(T)                         Ctrl+X"));editor.add(new JMenuItem("复制(C)                         Ctrl+C"));editor.add(new JMenuItem("粘贴(P)                         Ctrl+V"));editor.add(new JMenuItem("删除(L)                             Del"));editor.addSeparator();//分割线editor.add(new JMenuItem("查找(frame)                         Ctrl+frame"));editor.add(new JMenuItem("查找下一个(N)                    F3"));editor.add(new JMenuItem("替换(R)                         Ctrl+H"));editor.add(new JMenuItem("转到(G)                         Ctrl+G"));editor.addSeparator();//分割线editor.add(new JMenuItem("全选(A)                         Ctrl+A"));editor.add(new JMenuItem("时间/日期(D)                       F5"));//格式菜单添加菜单项layout.add(new JMenuItem("自动换行(W)            "));layout.add(new JMenuItem("字体(frame)"));//查看菜单添加菜单项viewing.add(new JMenuItem("状态栏(S)"));//帮助菜单添加菜单项help.add(new JMenuItem("查看帮助(H)"));help.add(new JMenuItem("关于记事本(A)"));//菜单加入菜单栏menuBar.add(document);menuBar.add(editor);menuBar.add(layout);menuBar.add(viewing);menuBar.add(help);//多行文本框JTextArea textArea = new JTextArea();textArea.setLineWrap(true);//加入滚动条面版JScrollPane scrollPane = new JScrollPane(textArea);//滚动面板加入容器frame.setContentPane(scrollPane);//容器加入菜单栏frame.setJMenuBar(menuBar);//设置默认关闭操作frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置容器可显示frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

九.工具栏

工具栏用于存放常用的按钮

btn.setToolTipText() 可以设置按钮提示信息
toolBar.setFloatable(false);禁止工具栏拖动:true可以拖动 false不可以拖动

    @Testpublic void JToolBarTest() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);// 添加菜单addMenu(frame);// 工具栏JToolBar toolBar = new JToolBar();// 为工具栏增加按钮JButton b1 = new JButton(new ImageIcon("e:/data/1.jpg"));//设置按钮提示文字b1.setToolTipText("坑爹英雄");JButton b2 = new JButton(new ImageIcon("e:/data/2.jpg"));JButton b3 = new JButton(new ImageIcon("e:/data/3.jpg"));JButton b4 = new JButton(new ImageIcon("e:/data/4.jpg"));JButton b5 = new JButton(new ImageIcon("e:/data/5.jpg"));JButton b6 = new JButton(new ImageIcon("e:/data/6.jpg"));toolBar.add(b1);toolBar.add(b2);toolBar.add(b3);toolBar.add(b4);toolBar.add(b5);toolBar.add(b6);// 禁止工具栏拖动toolBar.setFloatable(false);// 把工具栏放在north的位置frame.setLayout(new BorderLayout());frame.add(toolBar, BorderLayout.NORTH);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

十.日期控件

10.1.DatePicker

<dependency><groupId>org.jdatepicker</groupId><artifactId>jdatepicker</artifactId><version>1.3.4</version>
</dependency>

本例使用datepicker.jar 包,有一个缺点,不能设置时间,只能在创建控件的时候传入指定日期。
需要设置日期,请使用JXDatePicker

import com.eltima.components.ui.DatePicker;
public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(400, 300);f.setLocation(200, 200);f.setLayout(null);final DatePicker datepick;datepick = getDatePicker();f.add(datepick);JButton b = new JButton("获取时间");b.setBounds(137, 183, 100, 30);f.add(b);b.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {JOptionPane.showMessageDialog(f, "获取控件中的日期:" + datepick.getValue());System.out.println(datepick.getValue());}});f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}private static DatePicker getDatePicker() {final DatePicker datepick;// 格式String DefaultFormat = "yyyy-MM-dd HH:mm:ss";// 当前时间Date date = new Date();// 字体Font font = new Font("Times New Roman", Font.BOLD, 14);Dimension dimension = new Dimension(177, 24);int[] hilightDays = { 1, 3, 5, 7 };int[] disabledDays = { 4, 6, 5, 9 };datepick = new DatePicker(date, DefaultFormat, font, dimension);datepick.setLocation(137, 83);datepick.setBounds(137, 83, 177, 24);// 设置一个月份中需要高亮显示的日子datepick.setHightlightdays(hilightDays, Color.red);// 设置一个月份中不需要的日子,呈灰色显示datepick.setDisableddays(disabledDays);// 设置国家datepick.setLocale(Locale.CHINA);// 设置时钟面板可见datepick.setTimePanleVisible(true);return datepick;}
}

10.2.JXDatePicker

本例使用 包swingx-core-1.6.5-1.jar,界面比较简约,可以设置日期

   <dependency><groupId>org.swinglabs.swingx</groupId><artifactId>swingx-core</artifactId><version>1.6.5-1</version></dependency>

10.3.时间控件练习:统计大于指定更新时间文件

借助时间控件,选中一个时间,然后根据这个时间,统计e:\project 修改时间大于这个时间的文件,一共有多少

import com.eltima.components.ui.DatePicker;public class TestGUI {public static void main(String[] args) {JFrame f = new JFrame("LoL");f.setSize(500, 300);f.setLocation(200, 200);f.setLayout(null);final DatePicker datepick;datepick = getDatePicker();f.add(datepick);JButton b = new JButton("统计在D:/eclipse3.1 目录下,修改时间大于控件日期的文件总数");b.setBounds(20, 183, 450, 30);f.add(b);b.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {File folder = new File("D:\\eclipse3.1");Date d = (Date) datepick.getValue();JOptionPane.showMessageDialog(f, "文件总数是:" + countFilesModifiedDateGreaterThan(folder, d));}});f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setVisible(true);}private static DatePicker getDatePicker() {final DatePicker datepick;// 格式String DefaultFormat = "yyyy-MM-dd HH:mm:ss";// 当前时间Date date = new Date();// 字体Font font = new Font("Times New Roman", Font.BOLD, 14);Dimension dimension = new Dimension(177, 24);int[] hilightDays = { 1, 3, 5, 7 };int[] disabledDays = { 4, 6, 5, 9 };datepick = new DatePicker(date, DefaultFormat, font, dimension);// datepick.setLocation(137, 83);datepick.setBounds(137, 83, 177, 24);// 设置一个月份中需要高亮显示的日子datepick.setHightlightdays(hilightDays, Color.red);// 设置一个月份中不需要的日子,呈灰色显示datepick.setDisableddays(disabledDays);// 设置国家datepick.setLocale(Locale.CHINA);// 设置时钟面板可见datepick.setTimePanleVisible(true);return datepick;}//递归获取文件public static List<File> scanFolder(File folder){List<File> files = new ArrayList<>();File[] fs = folder.listFiles();for (File f : fs) {if(f.isDirectory()){List<File> subFolderFiles = scanFolder(f);files.addAll(subFolderFiles);}if(f.isFile()){files.add(f);}}return files;}//通过stream过滤文件修改时间大于指定时间的个数public static long countFilesModifiedDateGreaterThan(File folder,Date d){List<File> fs = scanFolder(folder);return fs.stream().filter(f->f.lastModified()>d.getTime()).count();}}

十一.表格

11.1.基本表格

显示一个Table需要两组数据

  • 一维数组: String[] columnName: 表示表格的标题
  • 二维数组: String[][] heros : 表格中的内容
  • 默认情况下,表格的标题不会显示,除非使用了JScrollPane
    @Testpublic void JTable() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);frame.setLayout(new BorderLayout());// 表格上的titleString[] columnNames = new String[]{"id", "name", "hp", "damage"};// 表格中的内容,是一个二维数组String[][] heros = new String[][]{{"1", "盖伦", "616", "100"}, {"2", "提莫", "512", "102"}, {"3", "奎因", "832", "200"}};JTable table = new JTable(heros, columnNames);frame.add(table, BorderLayout.CENTER);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

11.2.使用JScrollPane

JScrollPane: 带滚动条的Panel

  • 把table放进去就可以看到table的title

同样的把textarea放进去,并且textarea内容够长的话,就会看到滚动条

    @Testpublic void JTableShowTitle() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);frame.setLayout(new BorderLayout());// 表格上的titleString[] columnNames = new String[]{"id", "name", "hp", "damage"};// 表格中的内容,是一个二维数组String[][] heros = new String[][]{{"1", "盖伦", "616", "100"}, {"2", "提莫", "512", "102"}, {"3", "奎因", "832", "200"}};JTable table = new JTable(heros, columnNames);// 根据t创建 JScrollPaneJScrollPane scrollPane = new JScrollPane(table);//或则创建一个空的JScrollPane,再通过setViewportView把table放在JScrollPane中// JScrollPane sp = new JScrollPane(table);// sp.setViewportView(table);// 把sp而非JTable加入到JFrame上,frame.add(scrollPane, BorderLayout.CENTER);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);TimeUnit.SECONDS.sleep(1000);}

11.3.设置列宽

      // 设置列宽度table.getColumnModel().getColumn(0).setPreferredWidth(10);

11.4.TableModel

TableMode可以将表格的数据和显示分离

  • 比如对于JTable而言,有数据部分,也有显示部分(比如列宽等信息)。 数据部分可以声明一个叫做TableModel存放要显示的数据。

使用TableModel的方式存放Table需要显示的数据

需要继承AbstractTableModel,进而实现了接口TableModel,在实现类 中提供一个table显示需要的所有信息

  1. getRowCount 返回一共有多少行
  2. getColumnCount 返回一共有多少列
  3. getColumnName 每一列的名字
  4. isCellEditable 单元格是否可以修改
    5.getValueAt 每一个单元格里的值

当图形界面需要渲染第一个单元格的数据的时候,就会调用方法TabelModelgetValueAt(0,0),把返回值拿到并显示

public class HeroTableModel extends AbstractTableModel {private static final long serialVersionUID = -4796525201897896613L;String[] columnNames = new String[]{"id", "name", "hp", "damage"};String[][] heros = new String[][]{{"1", "盖伦", "616", "100"}, {"2", "提莫", "512", "102"}, {"3", "奎因", "832", "200"}};// 返回一共有多少行@Overridepublic int getRowCount() {return heros.length;}// 返回一共有多少列@Overridepublic int getColumnCount() {return columnNames.length;}// 获取每一列的名称@Overridepublic String getColumnName(int columnIndex) {return columnNames[columnIndex];}// 单元格是否可以修改@Overridepublic boolean isCellEditable(int rowIndex, int columnIndex) {return false;}// 每一个单元格里的值@Overridepublic Object getValueAt(int rowIndex, int columnIndex) {return heros[rowIndex][columnIndex];}/*** 自定义TableModel* @throws InterruptedException*/@Testpublic void customTableModel() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);frame.setLayout(new BorderLayout());//创建一个TableModelHeroTableModel htm = new HeroTableModel();//根据 TableModel来创建 TableJTable t = new JTable(htm);JScrollPane sp = new JScrollPane(t);frame.add(sp, BorderLayout.CENTER);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);TimeUnit.SECONDS.sleep(1000);}
}

也可以直接通过JTable的构造方法传入标题以及数据创建表格

JTable(Object[][] rowData, Object[] columnNames)

11.5.TableModel 与 数据库结合(使用List模拟数据库操作)

只需要修改HeroTableModel,引入Dao层,无需修改展示。 这正好演绎了Model设计思想中的数据分离的好处,当只需要数据发生变化的时候,修改Model即可,界面GUI部分,不需要做任何改动

由于时间关系 下面的代码都是使用内存来模拟数据库的增删改查, 实际上操作数据库也只是把 HeroDAO代码改为操作数据库代码而已

public class HeroTableModelDB extends AbstractTableModel {private static final long serialVersionUID = -4796525201897896613L;// 使用从DAO返回的List作为TableModel的数据public List<Hero> heros = new HeroDAO().list(0,10);public String[] columnNames = new String[]{"id", "name", "hp", "damage"};// heros.size返回一共有多少行@Overridepublic int getRowCount() {return heros.size();}@Overridepublic int getColumnCount() {return columnNames.length;}@Overridepublic String getColumnName(int columnIndex) {return columnNames[columnIndex];}@Overridepublic boolean isCellEditable(int rowIndex, int columnIndex) {return false;}// 先通过heros.get(rowIndex)获取行对应的Hero对象// 然后根据columnIndex返回对应的属性@Overridepublic Object getValueAt(int rowIndex, int columnIndex) {Hero h = heros.get(rowIndex);if (0 == columnIndex) {return h.id;}if (1 == columnIndex) {return h.name;}if (2 == columnIndex) {return h.hp;}if (3 == columnIndex) {return h.damage;}return null;}@Data@AllArgsConstructor@NoArgsConstructorstatic class Hero {private String id;private String name;private String hp;private String damage;}//静态内部类HeroDAO,通过操作内存来模拟对数据库的增删改查static class HeroDAO {//保存模拟数据容器public static List<Hero> heroList = new ArrayList<>();//模拟数据static {Hero h1 = new Hero("1", "盖伦", "616", "100");Hero h2 = new Hero("2", "提莫", "512", "102");Hero h3 = new Hero("3", "奎因", "832", "200");Hero h4 = new Hero("4", "后羿", "822", "500");Hero h5 = new Hero("5", "盖伦", "616", "100");Hero h6 = new Hero("6", "提莫", "512", "102");Hero h7 = new Hero("7", "奎因", "832", "200");Hero h8 = new Hero("8", "后羿", "822", "500");Hero h9 = new Hero("9", "盖伦", "616", "100");Hero h10 = new Hero("10", "提莫", "512", "102");Hero h11 = new Hero("11", "奎因", "832", "200");Hero h12 = new Hero("12", "后羿", "822", "500");heroList.add(h1);heroList.add(h2);heroList.add(h3);heroList.add(h4);heroList.add(h5);heroList.add(h6);heroList.add(h7);heroList.add(h8);heroList.add(h9);heroList.add(h10);heroList.add(h11);heroList.add(h12);}/*** 模拟查询数据库方法** @return*/public List<Hero> list() {return heroList;}/*** 模拟查询数据库方法* @return*/public List<Hero> list(int start, int number) {return page(heroList,start,number);}/*** 模拟新增* @return*/public void add(Hero hero) {heroList.add(0, hero);}/*** 根据id删除* @param id*/public void delete(String id) {heroList.removeIf(hero -> StringUtils.equals(id, hero.getId()));}/*** 获取数据总量* @return*/public int getTotal() {return heroList.size();}/*** 根据id更新数据* @param hero*/public void update(Hero hero) {heroList.forEach(e -> {if (StringUtils.equals(e.getId(), hero.getId())) {e.setName(hero.getName());e.setHp(hero.getHp());e.setDamage(hero.getDamage());}});}/*** 循环截取某页列表进行分页* @param dataList 分页数据* @param pageSize  页面大小* @param currentPage   当前页面*/public static List<Hero> page(List<Hero> dataList,int currentPage, int pageSize ) {List<Hero> currentPageList = new ArrayList<>();if (dataList != null && dataList.size() > 0) {int currIdx = (currentPage > 1 ? (currentPage - 1) * pageSize : 0);for (int i = 0; i < pageSize && i < dataList.size() - currIdx; i++) {Hero data = dataList.get(currIdx + i);currentPageList.add(data);}}return currentPageList;}}
}

11.5.1.测试展示数据库内容

    @Testpublic void customTableModelDB() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);frame.setLayout(new BorderLayout());//创建一个TableModelHeroTableModelDB htm = new HeroTableModelDB();//根据 TableModel来创建 TableJTable table = new JTable(htm);JScrollPane sp = new JScrollPane(table);frame.add(sp, BorderLayout.CENTER);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

11.5.2.监听table选中项的变化

通过table可以获取一个 TableSelectionModel,专门用于监听jtable选中项的变化

    @Testpublic void getSelectionModel() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);frame.setLayout(new BorderLayout());final HeroTableModelDB htm = new HeroTableModelDB();final JTable table = new JTable(htm);// 准备一个Panel上面放一个Label用于显示哪条被选中了JPanel panel = new JPanel();final JLabel label = new JLabel("暂时未选中条目");panel.add(label);JScrollPane sp = new JScrollPane(table);// 使用selection监听器来监听table的哪个条目被选中table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {// 当选择了某一行的时候触发该事件@Overridepublic void valueChanged(ListSelectionEvent e) {// 获取哪一行被选中了int row = table.getSelectedRow();// 根据选中的行,到HeroTableModel中获取对应的对象HeroTableModelDB.Hero h = htm.heros.get(row);// 更新标签内容label.setText("当前选中的英雄是: " + h.getName());}});frame.add(panel, BorderLayout.NORTH);frame.add(sp, BorderLayout.CENTER);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

11.5.3.输入项校验

如果用户输入的名称为空,或者血量不是小数,在提交数据的时候弹出提示。

    @Testpublic void checkTable() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);frame.setLayout(new BorderLayout());final HeroTableModelDB htm = new HeroTableModelDB();final JTable table = new JTable(htm);// 增加 一个 panel用于放置名称,血量输入框和增加 按钮JPanel panel = new JPanel();final JLabel lName = new JLabel("名称");final JTextField tfName = new JTextField("");final JLabel lHp = new JLabel("血量");final JTextField tfHp = new JTextField("");JButton bAdd = new JButton("增加");tfName.setPreferredSize(new Dimension(80, 30));tfHp.setPreferredSize(new Dimension(80, 30));panel.add(lName);panel.add(tfName);panel.add(lHp);panel.add(tfHp);panel.add(bAdd);// 为增加按钮添加监听bAdd.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {HeroDAO heroDAO = new HeroDAO();String name = tfName.getText();// 通过name长度判断 名称是否为空if (name.length() == 0) {// 弹出对话框提示用户JOptionPane.showMessageDialog(frame, "名称不能为空");// 名称输入框获取焦点tfName.grabFocus();return;}String hp = tfHp.getText().trim();try {// 把hp转换为浮点型,如果出现异常NumberFormatException表示不是浮点型格式Float.parseFloat(hp);} catch (NumberFormatException e1) {JOptionPane.showMessageDialog(frame, "血量只能是小数 ");tfHp.grabFocus();return;}// 根据输入框数据创建一个Hero对象Hero hero = new Hero();hero.setName(name);hero.setHp(hp);hero.setId(System.currentTimeMillis() + "");hero.setDamage("0");// 通过dao把该对象加入到数据库heroDAO.add(hero);// 通过dao更新tablemodel中的数据htm.heros = heroDAO.list();// 调用JTable的updateUI,刷新界面。// 刷新界面的时候,会到tableModel中去取最新的数据// 就能看到新加进去的数据了table.updateUI();}});JScrollPane scrollPane = new JScrollPane(table);frame.add(panel, BorderLayout.NORTH);frame.add(scrollPane, BorderLayout.CENTER);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

11.5.4.选中指定行

  • table初始化后,默认选中第一行
  • 新增数据后,默认选中新增的这一条
    @Testpublic void selectLine() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(200, 200);frame.setLayout(new BorderLayout());final HeroTableModelDB htm = new HeroTableModelDB();final JTable table = new JTable(htm);// 设置选择模式为 只能选中一行table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);// 选中第一行 (基本0)table.getSelectionModel().setSelectionInterval(0, 0);// 增加 一个 panel用于放置名称,血量输入框和增加 按钮JPanel panel = new JPanel();final JLabel lName = new JLabel("名称");final JTextField tfName = new JTextField("");final JLabel lHp = new JLabel("血量");final JTextField tfHp = new JTextField("");JButton bAdd = new JButton("增加");tfName.setPreferredSize(new Dimension(80, 30));tfHp.setPreferredSize(new Dimension(80, 30));panel.add(lName);panel.add(tfName);panel.add(lHp);panel.add(tfHp);panel.add(bAdd);// 为增加按钮添加监听bAdd.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {HeroDAO heroDAO = new HeroDAO();String name = tfName.getText();// 通过name长度判断 名称是否为空if (name.length() == 0) {// 弹出对话框提示用户JOptionPane.showMessageDialog(frame, "名称不能为空");// 名称输入框获取焦点tfName.grabFocus();return;}String hp = tfHp.getText().trim();try {// 把hp转换为浮点型,如果出现异常NumberFormatException表示不是浮点型格式Float.parseFloat(hp);} catch (NumberFormatException e1) {JOptionPane.showMessageDialog(frame, "血量只能是小数 ");tfHp.grabFocus();return;}// 根据输入框数据创建一个Hero对象Hero hero = new Hero();hero.setName(name);hero.setHp(hp);hero.setId(System.currentTimeMillis() + "");hero.setDamage("0");// 通过dao把该对象加入到数据库heroDAO.add(hero);// 通过dao更新tablemodel中的数据htm.heros = heroDAO.list();// 调用JTable的updateUI,刷新界面。// 刷新界面的时候,会到tableModel中去取最新的数据// 就能看到新加进去的数据了table.updateUI();// 选中 第一行 ,因为 DAO是按照 ID倒排序查询,所以第一行就是新加入的数据table.getSelectionModel().setSelectionInterval(0, 0);}});JScrollPane scrollPane = new JScrollPane(table);frame.add(panel, BorderLayout.NORTH);frame.add(scrollPane, BorderLayout.CENTER);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);TimeUnit.SECONDS.sleep(1000);}

11.6.练习:表格增删改查(使用List模拟数据库操作)

public class TableCrudDemo {static HeroTableModelDB htm = new HeroTableModelDB();static JTable table = new JTable(htm);// 把分页按钮放在这里,后面监听器好访问static JButton bFirst = new JButton("首页");static JButton bPre = new JButton("上一页");static JButton bNext = new JButton("下一页");static JButton bLast = new JButton("末页");static JComboBox<Integer> goPageCheckBox = new JComboBox<>();//跳转页数static int pageSize = 10;// 每页显示10个static int pageNum = 1;// 开始的页码private static boolean cbListenerEnabled = true;public static void main(String[] args) {//WebLookAndFeel.install ();final JFrame frame = new JFrame("LoL");frame.setSize(400, 340);frame.setLocation(200, 200);//默认选中第0行table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);table.getSelectionModel().setSelectionInterval(0, 0);JPanel pOperation = new JPanel();JButton bAdd = new JButton("增加");JButton bDelete = new JButton("删除");JButton bEdit = new JButton("编辑");pOperation.add(bAdd);pOperation.add(bDelete);pOperation.add(bEdit);JPanel pPage = new JPanel();pPage.add(bFirst);pPage.add(bPre);pPage.add(goPageCheckBox);pPage.add(bNext);pPage.add(bLast);//新增按钮事件addBtnListener(frame, bAdd);//编辑按钮事件editBtnListener(frame, bEdit);//删除按钮事件deleteBtnListener(frame, bDelete);//分页按钮事件pageBtnListener();//跳转按钮事件goPageBtnListener(goPageCheckBox);JScrollPane scrollPane = new JScrollPane(table);frame.setLayout(null);scrollPane.setBounds(0, 0, 394, 200);pOperation.setBounds(0, 200, 394, 50);pPage.setBounds(0, 250, 394, 200);frame.add(scrollPane);frame.add(pOperation);frame.add(pPage);updateButtonStatus();frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);}/*** 跳转按钮事件*/private static void goPageBtnListener(JComboBox<Integer> goPageCheckBox) {//跳转按钮事件goPageCheckBox.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if (!cbListenerEnabled) {return;}int currentPage = (int) goPageCheckBox.getSelectedItem();pageNum = currentPage;//当前页数=跳转页数updateTable();updateButtonStatus();}});}/*** 新增按钮事件*/private static void addBtnListener(JFrame frame, JButton bAdd) {bAdd.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {new AddDialog(frame).setVisible(true);updateButtonStatus();}});}/*** 编辑按钮事件*/private static void editBtnListener(JFrame frame, JButton bEdit) {bEdit.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {// 判断是否选中int index = table.getSelectedRow();if (-1 == index) {JOptionPane.showMessageDialog(frame, "编辑前需要先选中一行");return;}// 获取选中的对象HeroTableModelDB.Hero hero = htm.heros.get(index);// 显示编辑DialogEditDialog ed = new EditDialog(frame);ed.tfName.setText(hero.getName());ed.tfHp.setText(hero.getHp());ed.setVisible(true);}});}/*** 删除按钮事件*/private static void deleteBtnListener(JFrame frame, JButton bDelete) {bDelete.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {// 判断是否选中int index = table.getSelectedRow();if (-1 == index) {JOptionPane.showMessageDialog(frame, "删除前需要先选中一行");return;}// 进行确认是否要删除if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(frame, "确认要删除?")) {return;}// 获取idHeroTableModelDB.Hero hero = htm.heros.get(index);String id = hero.getId();// 删除new HeroTableModelDB.HeroDAO().delete(id);// 更新tablepageNum = 1;updateTable();updateButtonStatus();}});}/*** 分页按钮事件*/private static void pageBtnListener() {bFirst.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {pageNum = 1;updateTable();updateButtonStatus();}});bPre.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {pageNum = pageNum - 1;updateTable();updateButtonStatus();}});bNext.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {pageNum = pageNum + 1;updateTable();updateButtonStatus();}});bLast.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {pageNum = calcTotalPage();updateTable();updateButtonStatus();}});}/*** 更新按钮状态*/private static void updateButtonStatus() {int totalPage = calcTotalPage();// 是否有上一页if (1 != pageNum) {bFirst.setEnabled(true);bPre.setEnabled(true);}// 是否是第一页if (1 == pageNum) {bFirst.setEnabled(false);bPre.setEnabled(false);}// 是否是最后一页if (pageNum == totalPage) {bLast.setEnabled(false);bNext.setEnabled(false);}// 是否有下一页if (pageNum < totalPage) {bLast.setEnabled(true);bNext.setEnabled(true);}cbListenerEnabled = false;goPageCheckBox.removeAllItems();//总共的页数for (int i = 0; i < totalPage; i++) {goPageCheckBox.addItem(i + 1);}cbListenerEnabled = true;goPageCheckBox.setSelectedItem(pageNum);}/*** 根据表格数据*/public static void updateTable() {htm.heros = new HeroTableModelDB.HeroDAO().list(pageNum, pageSize);table.updateUI();if (!htm.heros.isEmpty()) {table.getSelectionModel().setSelectionInterval(0, 0);}}/*** 检查输入框空** @param tf* @param msg* @return*/private static boolean checkEmpty(JTextField tf, String msg) {String value = tf.getText();if (0 == value.length()) {JOptionPane.showMessageDialog(null, msg + " 不能为空");tf.grabFocus();return false;}return true;}/*** 检查输入框空与非数字** @param tf* @param msg* @return*/private static boolean checkNumber(JTextField tf, String msg) {String value = tf.getText();if (0 == value.length()) {JOptionPane.showMessageDialog(null, msg + " 不能为空");tf.grabFocus();return false;}try {Integer.parseInt(value);} catch (NumberFormatException e) {JOptionPane.showMessageDialog(null, msg + " 只能是整数");tf.grabFocus();return false;}return true;}/*** 计算总页数** @return*/private static int calcTotalPage() {int total = new HeroTableModelDB.HeroDAO().getTotal();int last = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;return last;}/*** 新增对话框*/static class AddDialog extends JDialog {JLabel lName = new JLabel("名称");JLabel lHp = new JLabel("血量");JTextField tfName = new JTextField();JTextField tfHp = new JTextField();JButton bSubmit = new JButton("提交");AddDialog(JFrame frame) {super(frame);this.setModal(true);int gap = 50;this.setLayout(null);JPanel pInput = new JPanel();JPanel pSubmit = new JPanel();pInput.setLayout(new GridLayout(2, 2, gap, gap));pInput.add(lName);pInput.add(tfName);pInput.add(lHp);pInput.add(tfHp);pSubmit.add(bSubmit);pInput.setBounds(50, 20, 200, 100);pSubmit.setBounds(0, 130, 300, 150);this.add(pInput);this.add(pSubmit);this.setSize(300, 200);this.setLocationRelativeTo(frame);bSubmit.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if (checkEmpty(tfName, "名称")) {if (checkNumber(tfHp, "hp")) {String name = tfName.getText();String hp = tfHp.getText();HeroTableModelDB.Hero h = new HeroTableModelDB.Hero();h.setName(name);h.setHp(hp);h.setId(System.currentTimeMillis() + "");h.setDamage("0");new HeroTableModelDB.HeroDAO().add(h);JOptionPane.showMessageDialog(frame, "提交成功 ");AddDialog.this.setVisible(false);pageNum = 1;updateTable();}}}});}}/*** 编辑对话框*/static class EditDialog extends JDialog {JLabel lName = new JLabel("名称");JLabel lHp = new JLabel("血量");JTextField tfName = new JTextField();JTextField tfHp = new JTextField();JButton bSubmit = new JButton("提交");EditDialog(JFrame frame) {super(frame);this.setModal(true);int gap = 50;this.setLayout(null);JPanel pInput = new JPanel();JPanel pSubmit = new JPanel();pInput.setLayout(new GridLayout(2, 2, gap, gap));pInput.add(lName);pInput.add(tfName);pInput.add(lHp);pInput.add(tfHp);pSubmit.add(bSubmit);pInput.setBounds(50, 20, 200, 100);pSubmit.setBounds(0, 130, 300, 150);this.add(pInput);this.add(pSubmit);this.setSize(300, 200);this.setLocationRelativeTo(frame);bSubmit.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if (checkEmpty(tfName, "名称")) {if (checkNumber(tfHp, "hp")) {// 获取idint index = table.getSelectedRow();String id = htm.heros.get(index).getId();String name = tfName.getText();String hp = tfHp.getText();HeroTableModelDB.Hero h = new HeroTableModelDB.Hero();h.setName(name);h.setHp(hp);h.setId(id);h.setDamage("0");new HeroTableModelDB.HeroDAO().update(h);JOptionPane.showMessageDialog(frame, "提交成功 ");EditDialog.this.setVisible(false);updateTable();}}}});}}
}

十二.Swing中的线程

12.1.三种线程

在Swing程序的开发中,有3种线程的概念

  1. 初始化线程
    用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了。

  2. 事件调度线程
    Swing是一个事件驱动的模型,所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)中进行的。比如按钮的点击时间,ActionListener.actionPerformed中的代码,就是在事件调度线程 Event Dispatch Thread中执行的。

  3. 长耗时任务线程
    有时候需要进行一些长时间的操作,比如访问数据库,文件复制,连接网络,统计文件总数等等。 这些操作就不适合放在事件调度线程中进行,因为占用时间久了,会让使用者感觉界面响应很卡顿。 为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的 长耗时任务线程中进行

12.2.事件调度线程是单线程的

为什么 事件调度线程是单线程的呢?

因为 Swing的各种组件,比如JTextField,JButton 都不是线程安全的,这就意味着,如果有多个线程,那么同一个JTextField的setText方法,可能会被多个线程同时调用,这会导致线程安全问题)的发生。

  • 如果把组件类设计成为线程安全的,由于Swing事件调度的复杂性,就很有可能导致死锁的发生。

为了规避同步问题,以及降低整个Swing设计的复杂度,提高Swing的相应速度,Swing中的 事件调度线程被设计成为了单线程模式,即只有一个线程在负责事件的响应工作。

12.3.初始化线程

如代码所示,我们初始化一个图形界面的时候,都会直接在主方法的主线程里,直接调用如下代码来进行初始化

new TestFrame().setVisible(true);

如果是简单程序这没有什么问题,如果是复杂的程序就有可能产生问题了。

  • 因为这里有两个线程在同时访问组件:1. 主线程 2. 事件调度线程。 如果是复杂的图形界面程序,就有可能出现这两个线程同时操作的情况,导致同步问题的产生。

为了规避这个问题的产生,创建和显示界面的工作,最好也交给事件调度线程,这样就保证了只有一个线程在访问这些组件


public class TestGUI {public static void main(String[] args) {//交给主线程创建和显示界面// new TestFrame().setVisible(true);//交给事件调度线程创建和显示界面SwingUtilities.invokeLater(new Runnable() {public void run() {new TestFrame().setVisible(true);}});}static class TestFrame extends JFrame {public TestFrame() {setTitle("LoL");setSize(400, 300);setLocation(200, 200);setLayout(null);JButton btn = new JButton("一键秒对方基地挂");btn .setBounds(50, 50, 280, 30);add(btn );setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setVisible(true);System.out.println("当前线程是否是 事件调度线程: " + SwingUtilities.isEventDispatchThread());}}
}

12.4.事件调度线程

以 按钮监听 中的代码为例,ActionListener.actionPerformed 中的代码,就是事件调度线程执行的。

可以借助SwingUtilities.isEventDispatchThread() 判断是否是事件调度线程

    @Testpublic void eventDiapatcherThread() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(400, 300);frame.setLocation(580, 200);frame.setLayout(null);final JLabel label = new JLabel();ImageIcon i = new ImageIcon("E:\\data\\gareen.jpg");label.setIcon(i);label.setBounds(50, 50, i.getIconWidth(), i.getIconHeight());JButton b = new JButton("隐藏图片");b.setBounds(150, 200, 100, 30);b.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {label.setVisible(false);System.out.println("当前使用的是事件调度线程:" + SwingUtilities.isEventDispatchThread());}});frame.add(label);frame.add(b);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}

点击按钮

12.5.长耗时任务线程

有时候需要执行长耗时任务,比如数据库查询,文件复制,访问网络等。

  • 这些操作一般都会在事件响应后发起,就会自动进入事件调度线程。 而事件调度线程又是单线程模式,其结果就会是在执行这些长耗时任务的时候,界面就无响应了

  • 如图所示,当点击第一个按钮的时候,会在其中进行一个5秒钟的任务,这个期间,第一个按钮会保持按下状态,其他按钮也无法点击,出现了无响应了状态。

为了解决这个问题,Swing提供了一个SwingWorker类来解决。SwingWorker是一个抽象类,为了使用,必须实现方法 doInBackground,在doInBackground中编写我们的任务,然后执行SwingWorker的execute方法,放在专门的工作线程中去运行。

    @Testpublic void longConsumingTask() throws InterruptedException {JFrame frame = new JFrame("LoL");frame.setSize(300, 300);frame.setLocation(200, 200);frame.setLayout(new FlowLayout());JButton b1 = new JButton("在事件调度线程中执行长耗时任务");JButton b2 = new JButton("使用SwingWorker执行长耗时任务");JLabel label = new JLabel("任务执行结果");frame.add(b1);frame.add(b2);frame.add(label);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);b1.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {label.setText("开始执行完毕");try {Thread.sleep(5000);} catch (InterruptedException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}label.setText("任务执行完毕");}});b2.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {@Overrideprotected Void doInBackground() throws Exception {System.out.println("执行这个SwingWorder的线程是:" + Thread.currentThread().getName());label.setText("开始执行完毕");try {Thread.sleep(5000);} catch (InterruptedException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}label.setText("任务执行完毕");return null;}};worker.execute();}});frame.setVisible(true);//设置休眠时间,是为了延缓主线程销毁TimeUnit.SECONDS.sleep(1000);}


12.6.练习:搜索指定目录下的所有Java文件

public class SearchJavaFile {private static int FOUND_COUNT = 0;public static void main(String[] args) {// 为了规避线程安全问题的产生,创建和显示界面的工作,使用调度线程初始化窗口,这样就保证了只有一个线程在访问这些组件SwingUtilities.invokeLater(() -> {WebLookAndFeel.install();new SearchFrame().setVisible(true);});}/*** 递归搜索文件内容,是否包含指定关键字文件个数** @param file   目录* @param search 关键字* @param suffix 文件后缀*/public static void search(File file, String search, String suffix) {if (file.isFile()) {if (file.getName().toLowerCase().endsWith(suffix)) {String fileContent = readFileConent(file);if (fileContent.contains(search)) {System.out.println(fileContent);FOUND_COUNT++;}}}if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {search(f, search, suffix);}}}/*** 递归搜索文件内容,是否包含指定关键字文件个数** @param file   目录* @param search 关键字* @param suffix 文件后缀*/public static int searchPlus(File file, String search, String suffix) {int foundCount = 0;if (file.isFile()) {if (file.getName().toLowerCase().endsWith(suffix)) {String fileContent = readFileConent(file);if (fileContent.contains(search)) {System.out.println(fileContent);foundCount += 1;}}}if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {foundCount += searchPlus(f, search, suffix);}}return foundCount;}/*** 读取文件文本内容** @param file* @return*/public static String readFileConent(File file) {try (FileReader fr = new FileReader(file)) {char[] all = new char[(int) file.length()];fr.read(all);return new String(all);} catch (IOException e) {e.printStackTrace();return null;}}/*** 查询窗口初始化*/static class SearchFrame extends JFrame {JLabel lLocation = new JLabel("查询目录");JLabel lSearch = new JLabel("文件内容");JTextField tfLocation = new JTextField("e:/data");JTextField tfSearch = new JTextField("g");JButton bSubmit = new JButton("搜索");/*** 容器初始化*/SearchFrame() {int gap = 50;this.setLayout(null);JPanel pInput = new JPanel();JPanel pSubmit = new JPanel();pInput.setLayout(new GridLayout(2, 2, gap, gap));pInput.add(lLocation);pInput.add(tfLocation);pInput.add(lSearch);pInput.add(tfSearch);pSubmit.add(bSubmit);pInput.setBounds(50, 20, 200, 100);pSubmit.setBounds(0, 130, 300, 150);this.add(pInput);this.add(pSubmit);this.setSize(300, 200);this.setLocationRelativeTo(null);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);bSubmit.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {FOUND_COUNT = 0;String location = tfLocation.getText();String search = tfSearch.getText();if (0 == location.length()) {JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");tfLocation.grabFocus();return;}if (0 == search.length()) {JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");tfSearch.grabFocus();return;}File folder = new File(location);if (!folder.exists()) {JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不存在");tfLocation.grabFocus();return;}if (!folder.isDirectory()) {JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不是一个文件夹");tfLocation.grabFocus();return;}//正在搜索按钮freeze();SwingWorker worker = new SwingWorker() {@Overrideprotected Object doInBackground() throws Exception {//work hereint foundCount = searchPlus(folder, search, ".java");JOptionPane.showMessageDialog(SearchFrame.this, "总共找到满足条件的文件: " + foundCount + " 个");//搜索按钮unfreeze();return null;}};worker.execute();}});}/*** 搜索按钮*/private void unfreeze() {bSubmit.setText("搜索");bSubmit.setEnabled(true);tfLocation.setEnabled(true);tfSearch.setEnabled(true);}/*** 正在搜索按钮*/private void freeze() {bSubmit.setText("正在搜索");bSubmit.setEnabled(false);tfLocation.setEnabled(false);tfSearch.setEnabled(false);}}
}

十三.界面风格

Java提供了非常便捷的方式切换界面风格

引入依赖

        <dependency><groupId>com.weblookandfeel</groupId><artifactId>weblaf-ui</artifactId><version>1.2.13</version></dependency>

加入这一行代码就行了

 WebLookAndFeel.install();

使用前:

使用后:

网上还有很多Swing风格包,大家可以自己去网上搜索

【Java基础】swing-图形界面学习(下)相关推荐

  1. java swing 示例_JAVA简单Swing图形界面应用演示样例

    JAVA简单Swing图形界面应用演示样例 package org.rui.hello; import javax.swing.JFrame; /** * 简单的swing窗体 * @author l ...

  2. java swing 案例详解_《Java Swing图形界面开发与案例详解》PDF_IT教程网

    资源名称:<Java Swing图形界面开发与案例详解>PDF 内容简介: <Java Swing图形界面开发与案例详解>全书共20章,其中第1-2章主要介绍有关Swing的基 ...

  3. java怎么开发图形界面_Java Swing 图形界面开发简介

    1. Swing简介 Swing 是 Java 为图形界面应用开发提供的一组工具包,是 Java 基础类的一部分. Swing 包含了构建图形界面(GUI)的各种组件,如: 窗口.标签.按钮.文本框等 ...

  4. Java/Swing 图形界面范例

    package com.myth; import javax.swing.JButton; import javax.swing.JFrame;public class JFrameExample1 ...

  5. java swing 目录,java swing图形界面开发目录

    java swing图形界面开发目录,做swing图形开发要学习哪些知识,难不难呢?请看以下的目录你就知道要学习哪些了: 目录 第1章 Java Swing概述 1 1.1 什么是Java Swing ...

  6. Java Swing 图形界面开发总结(完整版)

    最近在学习Java图像处理,发现还有好多不清除的知识点,在CSDN上查了好久,找到一篇前辈整理的关于Java Swing 图形界面开发的文章,感觉对自己的帮助非常大,在这里转载推荐一下,和大家一起学习 ...

  7. java swing图形界面开发 java.swing简介

    最近在看YouTube上面的视频的时候,虽然学着做了一点界面和一点可以运行的东西,但是里面用到的库文件我还是不明就里的.所以我打算在制作游戏之前,先花几天的时间大概地研究一下关于java.swing的 ...

  8. Java Swing 图形界面开发(目录)

    本文链接: https://blog.csdn.net/xietansheng/article/details/72814492 0. JavaSwing 简介 Java Swing 图形界面开发简介 ...

  9. Java Swing 图形界面开发教程(目录)

    参考文章:Java Swing 图形界面开发(目录) 0. JavaSwing 简介 Java Swing 图形界面开发简介 1. JavaSwing 布局管理器 avaSwing_1.1: Flow ...

  10. 一文带你深入理解【Java基础】· 面向对象编程(下)②代码块、final和abstract

    写在前面 Hello大家好, 我是[麟-小白],一位软件工程专业的学生,喜好计算机知识.希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!! ...

最新文章

  1. hdu 3068 最长回文(manacheramp;最长回文子串)
  2. Caml Query语句的所有条件
  3. Java并发-UncaughtExceptionHandler捕获线程异常信息并重新启动线程
  4. 也做SQL查询:班级总成绩 前三名,总成绩有相等的情况
  5. struts2之单个文件上传
  6. Coursera吴恩达《神经网络与深度学习》课程笔记(4)-- 浅层神经网络
  7. 进阶篇-安卓系统:5.安卓手机位置传感器
  8. Wikioi 1081 线段树成段更新单点查询
  9. 1080*1920 下看网站很爽
  10. net.sf.ezmorph.bean.MorphDynaBean cannot be cast to com.console.demo.web.model.XXX
  11. Rust: codewars 的Count of positives / sum of negatives
  12. 241.为运算表达式设计优先级(力扣leetcode) 博主可答疑该问题
  13. VS2018 查看类图 安装-属性,快捷键
  14. CDHD驱动器——ServoStudio配置高创伺服速度模式不转
  15. 企业微信有什么用,介绍企业微信6个强大的功能
  16. M1 MacBook的Parellel Desktop(PD)使用问题记录
  17. perl 产生随机数
  18. 报盘、还盘的英语短句(建议收藏)
  19. 1 面向对象设计模式与原则
  20. margin 重叠问题的理解

热门文章

  1. 爬虫虎牙斗鱼网站主播人气和姓名
  2. 2018年网易实习生笔试题集合之牛牛的闹钟
  3. 关于python的常量和变量下列描述正确的是_下面关于数据类型说法正确的是?
  4. 聚力赋能·共享共创|吉鼎科技与厦门6大高校“敏捷开发教学研讨会”圆满成功!
  5. 教你使用华为Ensp模拟器配置OSPF路由协议(二)
  6. FTP服务器架设教程之管理篇
  7. android elevation 白色,关于android:elevation的疑问
  8. 小程序scroll-view 实现两行滑动金刚区,滑动进度条
  9. 姚芊羽整容过度脸变残 娱乐圈假面女神大盘点
  10. Python之路(基本数据类型及操作)