JavaSE(6):java界面编程技术


六、java界面编程技术

 

1、图形用户界面概念:

GUIGraphical User Interface,图形用户接口,图形方式直观显示操作。

CLICommand Line User Interface,命令行用户接口,如常见的dos操作。

 

2、AWTSwing;

java.AwtAbstract Window Toolkit (抽象窗口工具包),需要调用本地的系统方法实现功能,是重量级控件。

javax.Swing:轻量级控件。组件的实现不包含任何本地代码,不受硬件平台的限制。

当组件区域有重叠时,重量级组件总是显示在上面,所以通常不同时使用。

 

3、设计和实现图形用户界面的内容:

①创建图形界面需要的元素,进行相应布局;

②定义界面元素对于用户事件的响应以及对事件的处理。

 

Awt组件继承图

 

4、常用容器类与容器布局管理

 

1、顶层容器 —— JFrame

容器:组件必须包含在某个容器中,处于嵌套层次最外层的是顶层容器,Swing提供了四种顶层容器:JFrame、JApplet、JDialog、JWindow。通常应用程序创建是以JFrame为顶层容器的。JFrame类创建的窗体期初不可见的,需要通过调用show方法或是setVisible(true)才能使其可见。

在定义窗口框架后,用add方法加入组件之前,需要获取内容窗格,类似windows编程中获取窗口设备上下文DC。然后将内容添加到内容窗格中,通常有两种方法:

一:先获取窗格,然后在窗格中加入内容,如

jframe.getContentPane().add(button,BorderLayout.CENTER);

二:创建一个窗格,在窗格上布置内容,再将窗格放到窗体中,类似windows程序中的双缓冲绘图的缓冲DC:

JPanel contentPane = new JPanel();

contentPane.setLayout(new BorderLayout());

contentPane.add(button,BorderLayout.CENTER);

frame.setContentPane(contentPane);

但是在J2SE1.5之后可以直接用顶层容器add方法添加组件,等价于通过顶层容器的内容窗格添加组件,注意,JFrame才有contentPane,而Frame类没有

 

2、中间容器 —— JPanel、JscorllPane

JPanel不能独立存在,必须添加到其他容器中,且本身可以嵌套,JPanel是无边框的,不能被移动、放大、缩小,自身支持双缓冲功能。通常先创建对象,再设置排列方式,然后加入依托面板中。

JscorllPane是一个滚动窗口容器,带滚动条,具体用法见api文档。

 

3、布局管理器

每个容器都有默认的布局管理器,同时可以通过setLayout方法人工设置需要的布置模式。

FlowLayout(流式布局,最常用的布局,Panel默认布局,Applet缺省布局,可自定义对齐方式如左对齐FlowLayout.LEFT)、BorderLayout(边界布局,分东西南北中五区,是顶层容器如JFrame的默认布局,若不指定位置,组件add默认添加到中间区域CENTER)、GridLayout(网格式布局,指定行列分布)、CardLayout(卡片布局管理)、GridBagLayout(网络包布局管理器)。

注:设计界面时也可以不使用布局管理器,setLayout(null),此时可通过组件的setBounds()方法精确设置组件在容器中的位置、大小

创建界面过程:创建JFrame(frame)窗体 -> 设置窗口大小、布局、位置 -> 定义组件 -> add方法添加组件到窗体中 -> 显示窗体,setVisible(true)方法。

MyWindow.java(窗口的创建(两种方法,拥有Frame类属性,或继承Frame类)、contentPane的使用、及布局管理器的使用)

 

package blog6;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * 窗口的创建(两种方法,拥有Frame类属性,或继承Frame类)、
 * contentPane的使用(此属性是JFrame的,Frame类没有)、及布局管理器的使用
 */
public class MyWindow {
	
	/*
	//测试1、Frame作为属性的窗口
	public static void main(String[] args) {
		new Window1();
	}
	*/
	
	/*
	//测试2、类继承Frame类
	public static void main(String[] args){
		new Window2("我的第二个窗口");
	}
	*/
	
	//测试3、JFrame及contentPane的用法
	public static void main(String[] args) {
		new Window3("我的第三个窗口");
	}
	
}

class Window1{
	private Frame f;
	public Window1() {
		f = new Frame("我的第一个窗口");
		
		//自定义组件位置,不使用layout
		f.setLayout(null);
		Button bt = new Button("按钮");
		bt.setBounds(100, 100, 50, 50); //bt出现在f窗口的100,100位置
		f.add(bt);
		
		f.setBounds(200, 200, 400, 400); //窗口大小,位置
		f.setVisible(true);  //此时窗口没有任何事件监听及处理
	}
}

@SuppressWarnings("serial")
class Window2 extends Frame{
	
	public Window2(String name) {
		super(name);
		setBounds(100, 100, 400, 300);
		setWindow(); //插入布局设置自定义函数
		setVisible(true);
	}
	
	//布局管理器的使用
	public void setWindow(){
		//Frame类的默认布局是BorderLayout,Panel类默认为FlowLayout
		setLayout(new BorderLayout()); //此句无用,因为本身默认就是BorderLayout布局了
		add(new Button("按钮A")); //不指定布局位置的话,默认置于CENTER位置
		add(new Button("按钮B"),BorderLayout.EAST);
		add(new Button("按钮C"),BorderLayout.SOUTH);
	}
}

@SuppressWarnings("serial")
class Window3 extends JFrame{
	public Window3(String name) {
		super(name);
		//法一
		// getContentPane().add(new JButton(),BorderLayout.EAST);
		
		//法二
		JPanel contentPane = new JPanel(new BorderLayout()); //默认为流式布局,设为边界
		contentPane.add(new Button("A按钮"));
		contentPane.add(new Button("B按钮")); //此处AB按钮都会默认放到中央区域,会覆盖吗?会覆盖
		contentPane.add(new Button("C按钮"),BorderLayout.NORTH);
		setContentPane(contentPane);
		
		//其实不用contentPane窗格内容也同样可以使用的
		setBounds(new Rectangle(100,100,400,400));
		setVisible(true);
	}
}

 

5、事件处理机制

 

事件处理模型如下图所示:

 

事件处理中的几个关键元素:

1、确定事件源:组件或容器

2、通过事件源对象的addXXXXListener()方法将监听器注册到该事件源上,表示该事件源能够监听到这类事件的发生。

3、在监听器注册方法中,接收XXXListener的子类对象,做出相应的处理反应。一般以匿名内部类表示。

4、在覆盖方法时,方法的参数一般是XXXEvent类型的变量。事件触发后会将事件打包成对象传递给该变量(其中包括了事件源信息,通过getSource或者getCompopent获取)。

WindowEvent.java(三种事件监听实现,创建内部类实现ActionListener接口、窗口类本身实现ActionListener接口、匿名内部类)

 

package blog6;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

/**
 * 三种事件监听实现 1 创建内部类实现ActionListener接口、 2 窗口类本身实现ActionListener接口、 3 匿名内部类
 */
public class MyWindowEvent {

	/*
	// 测试1、内部类 作为事件监听者 
	public static void main(String[] args) { 
		  new EventOne(); 
	}
	*/ 

	/*
	// 测试2、组件类本身实现
	public static void main(String[] args) {
		new EventTwo();
	}
	*/
	
	// 测试3、匿名内部类实现事件监听,最常用方法,但是需要为每个事件源都独立建立其对应的监听器
	public static void main(String[] args) {
		new EventThree();
	}
}

// 测试一的类
@SuppressWarnings("serial")
class EventOne extends JFrame {
	private JButton jbt;

	public EventOne() {
		super("the first window");

		jbt = new JButton("按钮A");
		add(jbt, BorderLayout.SOUTH);
		jbt.addActionListener(new JbtActionListener()); // 为按钮注册监听器

		setBounds(100, 100, 500, 500);
		setVisible(true);
	}

	// 创建内部类,实现按钮的监听方法
	class JbtActionListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			// 重写接口提供的处理动作事件的方法
			if (e.getSource() == jbt) { // 一个监听器可以为多个事件源服务,getSource可以获取事件源
				JOptionPane.showMessageDialog(EventOne.this, "按钮A被按下");
				// 弹出一个个信息提示对话框
			}
		}
	}
}

// 测试二的类
@SuppressWarnings("serial")
class EventTwo extends JFrame implements ActionListener {
	
	private JButton jbt;

	public EventTwo() {
		super("the second window");

		jbt = new JButton("按钮B");
		add(jbt, BorderLayout.NORTH);
		jbt.addActionListener(this); // 为按钮注册监听器,以本类实例自身为监听器

		setBounds(100, 100, 500, 500);
		setVisible(true);
	}

	@Override
	/* 在窗口类中实现监听接口,本类作为事件监听器 */
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == jbt) {
			JOptionPane.showMessageDialog(EventTwo.this, "按钮B被按下");
		}
	}
}

// 测试三的类
@SuppressWarnings("serial")
class EventThree extends Frame {
	
	private Button jbt;

	public EventThree() {
		super("the thrid window");

		jbt = new Button("按钮C");
		add(jbt, BorderLayout.WEST);
		
		//此处可以将所有的事件监听处理集成一个方法在此处调用注册
		jbt.addActionListener(new ActionListener(){
			@Override /* 匿名内部类 */
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(EventThree.this, "按钮C被按下");
			}} ); // 为按钮注册监听器,以本类实例自身为监听器

		setBounds(100, 100, 500, 500);
		setVisible(true);
	}
}


事件适配器:为了进行事件处理需要创建实现对应接口的类,而在这些接口中往往声明了很多抽象方法,但是通常,我们只需要处理某几种指定的方法,此时对应两个以上方法的监听者接口会有一个XXXXAdapter类,提供了接口中每个方法的缺省实现,用户只需要重写需要处理的方法即可。适配器通常以匿名内部类实现。

KeyMouseEvent.java(键盘鼠标事件适配器实现的实例)

 

package blog6;

import java.awt.FlowLayout;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

/**
 * 本例任务,完成一个鼠标和键盘的动作采集。
 * 对于鼠标、键盘事件的处理,采用事件适配器
 */
@SuppressWarnings("serial")
public class KeyMouseEvent extends JFrame{	
	private JLabel keyInfo,mouseX,mouseY,mouseClikCount;
	private JTextField mouseXInfo,mouseYInfo; 
	private int clickCount = 0;
	
	public KeyMouseEvent() {
		this.setLayout(new FlowLayout()); //设置成流式布局
		
		keyInfo = new JLabel("你按下的键盘键是: ");
		
		mouseX = new JLabel("x坐标");
		mouseY = new JLabel("y坐标");
		mouseClikCount = new JLabel("点击鼠标  次");
		mouseXInfo = new JTextField(10); //指定文本框长度
		mouseYInfo = new JTextField(10);
		mouseXInfo.setEditable(false);
		mouseYInfo.setEditable(false); //因为是显示作用的, 所以设置成不可编辑

		add(keyInfo);
		add(mouseX);
		add(mouseXInfo);
		add(mouseY);
		add(mouseYInfo);
		add(mouseClikCount);
		
		MyKeyMouseEvent();

		setBounds(100, 100, 600, 400);
		setVisible(true); 
		setTitle("键盘、鼠标捕捉程序");
		
		this.requestFocus();//此句重要,若不设置焦点,默认焦点在TextField中,按键不会有反应的

		setResizable(false); // 设置大小不可变 resizable is false
		
	}
	
	public void MyKeyMouseEvent(){
		//确定事件源,本窗口,事件监视器,处理部分都是在各个文本框或是JLabel中
		this.addKeyListener(new KeyAdapter() {
			//键盘事件适配器,本程序只监听键盘按下事件,所以重写键盘按下事件
			public void keyTyped(KeyEvent e){
				char c = e.getKeyChar();
				keyInfo.setText("你按下的键盘键是:" + c + "");
			}
			
			public void keyPressed(KeyEvent e){
				if(e.getKeyCode()==27){
					System.exit(0);
				}
			}
		});
		
		//窗口事件监听、关闭窗口
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e){
				System.exit(0);
			}
		});
		
		//鼠标事件监听,点击
		this.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e){
				int x = e.getX();
				int y = e.getY();
				mouseXInfo.setText(String.valueOf(x));
				mouseYInfo.setText(String.valueOf(y));
				clickCount++;
				mouseClikCount.setText("点击鼠标" + clickCount + "次");
			}
		});
		
		//鼠标监听事件,移动
		this.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent e){
				int x = e.getX();
				int y = e.getY();
				mouseXInfo.setText(String.valueOf(x));
				mouseYInfo.setText(String.valueOf(y));
			}
		});
		
		//窗口的焦点监听,使用原因在总结里说。
		this.addFocusListener(new FocusAdapter(){
			@Override
			public void focusLost(FocusEvent e) {
				KeyMouseEvent.this.requestFocus();
			}
		});
	}
	
	public static void main(String[] args) {
		new KeyMouseEvent();
	}
}

/**
 * 程序问题:反应较慢,而且鼠标的移动并没有捕捉到,只监听到鼠标的点击
 * 编写过程中的问题:
 * 1、开始时我采用,两个子Panel分别放鼠标和键盘的信息显示,但是运行失败了;
 * 2、后来测试发现是因为,程序一运行就把焦点定在了JTextField上,虽然我将文本框设置成了不可编辑,还是没用
 * 3、于是我开始控制焦点,构造器中将焦点设置在窗口Frame上,然后添加一个焦点监控器,一旦Frame发生focusLost
 * 	  就将焦点重新绑定到Frame中。
 * 4、但是也许是焦点操作的问题,使得我的鼠标移动的事件没有能够监控到........
 * 
 * 5、原来如此:找到了问题的根源,一开始我将所有鼠标的监控都写在了mouseAdapter中
 * 		事实上,鼠标的点击事件在MouseListener(或mouseAdapter)中,
 * 		其处理的是MouseEvent;
 * 		但是鼠标的移动应该在MouseMotionListener(对应MouseMotionAdapter)中,
 * 		其处理的是MouseMotionEvent
 */


 

6、 Dialog/JDialog:构造一个最初不可见,指定所有者Frame、标题和模式的对话框,模式参数选择True则为模式对话框,false为非模态对话框。

 文件对话框:JFileChooser。通过showOpenDialog和showSaveDialog显示文件“打开”或文件“保存”对话框,提供几种有关文件的操作geSelectedFile。

 

7、 菜单

先创建菜单栏MenuBar,再创建菜单(文件、视图、编辑等菜单小项目)Menu,再在每一个菜单Menu中添加菜单项MenuItem,也可以将菜单添加到菜单中,作为子菜单。最后通过setMenuBar方法,将菜单栏添加到Frame中。

弹出式菜单:JPopupMenu,必须调用show才能显示。

菜单项和菜单的使用会产生AcitonEvent事件,需要addActionListener为菜单注册监听器,以便对事件进行处理。

AddMenu.java(添加菜单实例、并完成菜单项的响应事件、弹出菜单实例)

 

package blog6;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;

/**
 * 菜单的添加、弹出式菜单的实现
 * 选中菜单项时发生ActionEvent事件,通过addActionListener添加监听器
 */
@SuppressWarnings("serial")
public class AddMenu extends JFrame{
	
	private JMenuBar menuBar;
	private JMenu menuFile,menuEdit,menuHelp,subFileMenu;
	private JMenuItem fileOpen,fileSave,fileClose,editCopy,editPaste,helpAbout,helpUser;
	private JMenuItem subFileMenu1,subFileMenu2;
	
	private JPopupMenu popMenu;
	
	public AddMenu() {
		super("带菜单的窗口");
		
		//初始化各菜单项
		menuBar = new JMenuBar();
		
		menuFile = new JMenu("文件");
		fileOpen = new JMenuItem("打开");
		fileSave = new JMenuItem("保存");
		fileClose = new JMenuItem("关闭");
		subFileMenu = new JMenu("文件子菜单");
		subFileMenu1 = new JMenuItem("子菜单选项1");
		subFileMenu2 = new JMenuItem("子菜单选项2");
		subFileMenu.add(subFileMenu1);
		subFileMenu.add(subFileMenu2);
		menuFile.add(fileOpen); 
		menuFile.add(fileSave); 
		menuFile.add(subFileMenu);
		menuFile.add(fileClose);
		
		menuEdit = new JMenu("编辑");
		editCopy = new JMenuItem("复制");
		editPaste = new JMenuItem("粘贴");
		menuEdit.add(editCopy);
		menuEdit.add(editPaste);
		
		menuHelp = new JMenu("帮助");
		helpAbout = new JMenuItem("关于");
		helpUser = new JMenuItem("使用帮助");
		menuHelp.add(helpAbout);
		menuHelp.add(helpUser);
		
		menuBar.add(menuFile);
		menuBar.add(menuEdit);
		menuBar.add(menuHelp);
		
		this.setJMenuBar(menuBar);
		
		//弹出式菜单
		popMenu = new JPopupMenu();
		popMenu.add(new JMenuItem("弹出菜单1"));
		popMenu.add(new JMenuItem("弹出菜单2"));
		popMenu.addSeparator();//增加菜单分割线
		popMenu.add(new JMenuItem("弹出菜单C"));
		
		this.setBounds(100, 100, 600, 400);
		this.setVisible(true);
		
		theEvent(); //弹出菜单本身需要在鼠标事件响应中显示,本例不对弹出菜单项做事件监听
	}
	
	//事件处理
	public void theEvent(){
		//窗口的关闭事件WindowEvent
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e){
				System.exit(0);
			}
		});
		
		//菜单选项事件,每一个选项都写太麻烦,写一个共用的内部类(因为本例中没个菜单的功能都一样:弹出对话框)
		OnMenu onMenu = new OnMenu(); //创建一个类对象为大家共用,不用匿名类那么浪费空间了
		fileOpen.addActionListener(onMenu);
		fileSave.addActionListener(onMenu);
		subFileMenu1.addActionListener(onMenu);
		subFileMenu2.addActionListener(onMenu);
		editCopy.addActionListener(onMenu);
		editPaste.addActionListener(onMenu);
		helpAbout.addActionListener(onMenu);
		helpUser.addActionListener(onMenu);
		
		//单独为“关闭”菜单选项写一个监视器的匿名内部类,为之注册到选项上(事件源)
		fileClose.addActionListener(new ActionListener() {	
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		
		this.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e){
				if( e.getButton()== MouseEvent.BUTTON3){//判断是右键点击
					popMenu.show(e.getComponent(),e.getX(),e.getY());
					//让弹出式菜单在鼠标事件发生的容器面板的当前点击位置显示 
				}
			}
		});
	}
	
	//为菜单项写的公共的监听器,每个菜单项都可以用
	class OnMenu implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			JMenuItem obj;
			obj = (JMenuItem) e.getSource(); //强转定位到事件源
			String str = obj.getText();
			JOptionPane.showMessageDialog(AddMenu.this,("你点击的菜单是:" +  str));
		}
	}
	
	public static void main(String[] args) {
		new AddMenu();
	}
	
}

 


8、常用的Swing组件

①JLabel,标签,每一个标签可以显示一行静态文本和图标。

 

②JTextField(文本框,单行)、JTextArea(文本域,多行),JTextField类只引发ActionEvent事件,当用户在文本框中按回车时引发。JTextArea因为是多行,通常放置在滚动面板JScrollPane中,该组件事件相应位DocumentEvent和UndoableEvent事件,对文本的增删改等操作是,触发DocumentEvent;当用户撤销了之前所做的增删改操作时,触发UndoableEvent事件。

此外还有一个JPasswordField密码框供特殊情况使用。

TextFieldTest.java(文本框实例)

 

package blog6;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

/**
 * 文本框的运用:第一个框中输入正整数,回车,把1到该数的总和输出在第二个文本上
 */
@SuppressWarnings("serial")
public class TextFieldTest extends JFrame implements ActionListener{
	
	private JLabel lb1,lb2;
	private JTextField t1,t2;
	
	public TextFieldTest() {
		this.setLayout(new FlowLayout());
		lb1 = new JLabel("请输入第一个正整数");
		lb2 = new JLabel("1到该数的和为");
		t1 = new JTextField(10);
		t2 = new JTextField(10);
		t2.setEditable(false); //文本框2不可编辑
		this.add(lb1);
		this.add(t1);
		this.add(lb2);
		this.add(t2);
		
		this.setBounds(100, 100, 200, 200);
		this.setVisible(true);
		this.setTitle("加法器");
		this.setResizable(false);
		
		//事件监听器
		t1.addActionListener(this);
		
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e){
				System.exit(0);
			}
		});
	}
	
	@Override //本类实现监听器接口,事件源为t1文本框,在文本框中按下回车会触发ActionEvent!!!!
	public void actionPerformed(ActionEvent e) {
		int n = Integer.parseInt(t1.getText());
		int sum = 0;
		for(int i=0;i<n;i++){
			sum +=i;
		}
		t2.setText(String.valueOf(sum));
	}
	
	public static void main(String[] args) {
		new TextFieldTest();
	}
}

 

③JButton(按钮),JCheckBox(复选框),JRadioButton(选项按钮),三种按钮式组件。JButton引发ActionEvent事件。JCheckBox触发ActionEvent和ItemEvent(addItemListener)事件,JRadioButton事件同JCheckBox。

 

④JComboBox(组合框),下拉列表框,选取下拉列表项时触发ItemEvent事件,当直接输入选项并回车选择时,触发ActionEvent事件。

⑤JList列表框,与JComboBox的区别是,一次可以选择多项或一项。双击列表框选项产生MouseEvent,打击某一选项选中时触发ListSelectionEvent事件。

⑥JTable(表格),十分复杂但对大规模数据处理较方便的组件。

 

 

本章综合练习:

MyNotpad.java(记事本文件,融入:窗口编写、菜单加入、事件监听、文件操作、文件对话框)。

 

package blog6;

import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

/**
 * 一个简单的记事本,作为对java GUI综合运用总结
 * 遇到的难题:通过网络学习解决如下:
 * 1、textArea通过JScrollPane添加到JFrame中的方法,使用new JScrollPane(textArea)构造方法
 * 2、剪贴板内容判断、textArea剪贴复制操作,textComponent都有cut、copy、paste方法
 * 
 * 遗留问题:稍微复杂一点:就是撤销操作,undo,还未学习实现
 */
@SuppressWarnings("serial")
public class MyNotepad extends JFrame{
	//本记事本包含资源:菜单(文件(打开dialog,保存dialog,关闭)、帮助)、弹出式菜单(粘贴、复制、剪贴)
	//一个滚动面板,用于放置文本域
	
	private JMenuBar menuBar;
	private JPopupMenu popMenu;
	private JMenu menuFile,menuHelp;
	private JMenuItem fileOpen,fileSave,fileClose,helpAbout,helpUser,popCopy,popCut,popPaste;
	private JScrollPane pane;
	private JTextArea textArea;
	
	public MyNotepad() {
		super("MyNotepad");
		
		//菜单栏
		menuBar = new JMenuBar();
		menuFile = new JMenu("文件");
		menuHelp = new JMenu("帮助");
		fileOpen = new JMenuItem("打开");
		fileSave = new JMenuItem("保存");
		fileClose = new JMenuItem("关闭");
		helpUser = new JMenuItem("帮助");
		helpAbout = new JMenuItem("关于");
		menuFile.add(fileOpen);
		menuFile.add(fileSave);
		menuFile.add(fileClose);
		menuHelp.add(helpUser);
		menuHelp.add(helpAbout);
		menuBar.add(menuFile);
		menuBar.add(menuHelp);
		this.setJMenuBar(menuBar);
		
		//弹出菜单
		popMenu = new JPopupMenu();
		popCopy = new JMenuItem("复制");
		popPaste = new JMenuItem("黏贴");
		popCut = new JMenuItem("剪切");
		popMenu.add(popCopy);
		popMenu.add(popCut);
		popMenu.add(popPaste);
		
		//滚动面板、文字域,这里注意:使用JScrollPane的时候,不要用无参的构造器
		//直接将要显示的textArea对象以参数形式传进JScrollPane的构造器中
        textArea = new JTextArea(); 
        textArea.setLineWrap(true);//激活自动换行功能 
        textArea.setWrapStyleWord(true);//激活断行不断字功能 
        textArea.setTabSize(4);  //设置table为4个字符长度
        /*下面这种表现形式很重要,否则用无参构造器再add添加textArea组件会不显示textArea*/
        pane = new JScrollPane(textArea);
        this.add(pane);
      
		this.setBounds(100,100,800,600);
		this.setVisible(true);
		//添加事件监听器
		MyEvent();
	}
	
	public void MyEvent(){
		//窗体事件
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e){
				System.exit(0);
			}
		});
		
		//菜单事件,可以集成在一个Jmenu中处理,也可以分开到每一个JMenuItem事件中,本例采用后者
		//打开
		fileOpen.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				//以当前目录建一个文件选择对话框,参数为获取的当前目录路径
				JFileChooser fileDialog = new JFileChooser(System.getProperty("user.dir"));
				//显示一个打开对话框,以当前JFrame为父窗口
				int select = fileDialog.showOpenDialog(MyNotepad.this);
				//判断当在代开对话框中选择了确定时
				if(select == JFileChooser.APPROVE_OPTION){
					//根据选择的文本创建文件对象
					File f = fileDialog.getSelectedFile();
					//将文件内容通过IO流输入到TextArea中
					try{
						BufferedReader bufr = new BufferedReader(new FileReader(f));
						String line = null;
						textArea.setText("");//加载文件内容前,先清空当前内容,使用setText方法
						while((line=bufr.readLine())!=null)
						{
							textArea.append(line+"\r\n");
						}
						bufr.close();
					}catch (IOException ex){
						JOptionPane.showMessageDialog(MyNotepad.this, "打开失败...");
					}
				}
			}
		});
		
		//保存
		fileSave.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				//以当前目录建一个文件选择对话框,参数为获取的当前目录路径
				JFileChooser fileDialog = new JFileChooser(System.getProperty("user.dir"));
				//显示一个保存对话框,以当前JFrame为父窗口
				int select = fileDialog.showSaveDialog(MyNotepad.this);
				//判断当在代开对话框中选择了确定时
				if(select == JFileChooser.APPROVE_OPTION){
					//根据选择的文本创建文件对象
					File f = fileDialog.getSelectedFile();
					//将文件内容通过IO流从TextArea输出到文件中
					try{
						BufferedWriter bufw  = new BufferedWriter(new FileWriter(f));
						String text = textArea.getText();//一次性全部保存,在大型文件的时候效率捉急
						bufw.write(text);
						bufw.flush(); //刷新缓冲
						bufw.close();
					}catch (IOException ex){
						JOptionPane.showMessageDialog(MyNotepad.this, "保存失败...");
					}
				}
			}
		});
		
		fileClose.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		
		helpAbout.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(MyNotepad.this, "关于本软件\n详情登陆本人博客...");	
			}
		});
		
		helpUser.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(MyNotepad.this, "这不科学...\n竟然没有使用帮助...");	
			}
		});
		
		//为文本域添加右键弹出菜单
		textArea.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e){
				if(e.getButton()== MouseEvent.BUTTON3){
					//若是右键,弹出菜单,e.getComponet获取事件发生的组件
					if(isClipboardString()){ //当前剪贴板有无内容,决定了弹出菜单的paste项能否可用
						popPaste.setEnabled(true);
					}else{
						popPaste.setEnabled(false);
					}
					popMenu.show(e.getComponent(), e.getX(), e.getY());
				}
			}
		});
		
		//弹出菜单选项事件,文本的编辑操作:粘贴复制....
		popCopy.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				textArea.copy();
			}
		});
		popCut.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				textArea.cut();
			}
		});
		popPaste.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				textArea.paste();
			}
		});
		
	}
	
  /**
   * 自定义方法、判断当前剪贴板中有无String内容
   * 使用到getSystemClipboard方法返回一个clipboard对象,访问其getContents方法获取内容。
   */
	public boolean isClipboardString() {
		boolean b = false;
		Clipboard clipboard = this.getToolkit().getSystemClipboard();
		Transferable content = clipboard.getContents(this);
		try {
			if (content.getTransferData(DataFlavor.stringFlavor) instanceof String) {
				b = true;
			}
		} catch (Exception e) {
		}
		return b;
	}

	public static void main(String[] args) {
		new MyNotepad();
	}
}


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值