《Swing用户界面组件.ppt》由会员分享,可在线阅读,更多相关《Swing用户界面组件.ppt(73页珍藏版)》请在三一办公上搜索。
1、Swing用户界面组件,Java,模型-视图-控制器设计模式,每个组件有三个要素内容,如按钮的状态(是否按下),或者文本域的文本;外观,颜色,大小等;行为,对事件的反应;三要素之间的关系复杂,Java,模型-视图-控制器设计模式,Swing设计者采用了一种著名的设计模式:模型-视图-控制器(model-view-controller)模式来实现组件的设计。实现一个组件有三个独立的类:模型(model):存储内容;视图(view):显示内容;控制器(controller):处理用户输入;,Java,模型-视图-控制器设计模式,模型只存储内容,必须实现改变内容和查找内容的方法。如,一个文本模型中的
2、方法有:在当前文本中添加或者删除字符以及把当前文本作为一个字符串返回等;模型完全不可见,视图显示存储在模型中的数据;控制器负责处理用户输入事件,如点击鼠标和敲击键盘。然后决定是否把这些事件转化为对模型或视图的改变;,Java,模型-视图-控制器设计模式,Java,模型、视图、控制器对象之间的交互,模型-视图-控制器设计模式,Swing按钮的模型-视图-控制器分析对于大多数组件,模型类实现了名字结尾为Model的接口,如:按钮就实现了ButtonModel接口;可以通过查看ButtonModel接口中的方法来获知按钮模型维护的是哪种类型的数据;,Java,模型-视图-控制器设计模式,Swing库
3、中包含了一个名为DefaultButtonModel的类,该类实现了ButtonModel接口,是JButton的模型类;该类同时也可作为单选按钮、复选框等的模型类;视图和感观相关联,不同感观对应的视图有所不同;当使用Metal观感时,JButton类用BasicButtonUI类作为其视图,用ButtonUIListener类作为控制器;JButton是一个继承了JComponent的包装器类,包含了一个DefaultButtonModel对象,一些视图数据以及一个负责按钮视图的BasicButtonUI对象(Metal感观);JButton button=new JButton(“Blue
4、”);ButtonModel model=button.getModel();ButtonUI ui=button.getUI();,Java,布局管理器,容器内的所有组件的排放由一个布局管理器(layout manager)进行管理。java.awt.Containercontainer.setLayout(LayoutObject);,Java,布局管理器,常用布局管理器流布局管理器(FlowLayout)边界布局管理器(BorderLayout)网格布局管理器(GridLayout),Java,布局管理器,流布局管理器(Flow Layout)其特点是在一行上水平排列组件,直到没有足够的
5、空间为止,再开始新的一行;面板(JPanel)的默认布局管理器;当用户缩放容器时,布局管理器自动地调整组件的位置使其填充可用的空间;但容器中组件的大小不会变;默认情况下,组件是在一行上居中显示。程序员可以设置容器中的组件按左对齐或者右对齐的方式排列;panel.setLayout(new FlowLayout(FlowLayout.LEFT);,Java,布局管理器,流布局管理器(Flow Layout)java.awt.Layout;FlowLayout();FlowLayout(int align);FlowLayout(int align,int hgap,int vgap);常用的行对
6、齐方式 FlowLayout.CENTER;FlowLayout.LEFT;FlowLayout.RIGHT;,Java,布局管理器,边界布局管理器(BorderLayout)将整个容器分为中部、北部、南部、东部或者西部五个区域;程序员可以选择将组件放在哪个区域;默认放在中部;JFrame的内容窗格的默认布局管理器;frame.add(button,BorderLayout.SOUTH);,Java,布局管理器,边界布局管理器(BorderLayout)边界布局管理器在安放组件时,会先放入边缘组件,剩余的可用空间由中间组件占用,如果有某个边缘组件空缺,其它组件会填充该边缘位置;容器缩放时,边界
7、布局会扩大所有组件的尺寸以便填充可用空间,但边缘组件的厚度不会改变,长度会有所改变,而中间组件的大小会发生变化;,Java,布局管理器,java.awt.BorderLayout类BorderLayout();BorderLayout(int hgap,int vgap);,Java,布局管理器,将一个组件放在边界布局管理器的某个部分时,其会填充整个部分的空间,如果此时再放入另一个组入到相同的部分,则后放入的组件会覆盖前一个组件;public class BorderLayoutTest public static void main(String args)JFrame f=new JFra
8、me(BorderLayout Test);f.add(new JButton(South),BorderLayout.SOUTH);f.add(new JButton(South1),BorderLayout.SOUTH);f.setSize(200,200);f.setVisible(true);,Java,布局管理器,可利用中间容器(JPanel)把多个组件放在边界布局管理器的同一个部分;public class BorderLayoutTest public static void main(String args)JFrame f=new JFrame(BorderLayout Te
9、st);JPanel p=new JPanel();f.add(p,BorderLayout.SOUTH);p.add(new JButton(South);p.add(new JButton(South1);f.setSize(200,200);f.setVisible(true);,Java,布局管理器,网格布局管理器(GridLayout)将容器按行列平均划分;容器上的组件添加从第一行的第一列开始,然后是第一行的第二列,以此类推;,Java,布局管理器,Java.awt.GridLayoutGridLayout();GridLayout(int rows,int cols);GridLa
10、yout(int rows,int cols,int hgap,int vgap);,Java,举例:计算器程序,制作一个如图所示的计算器程序。使用面板嵌套技术:,布局管理器,import java.awt.*;import java.awt.event.*;import javax.swing.*;public class Calculator public static void main(String args)CalculatorFrame frame=new CalculatorFrame();frame.setDefaultCloseOperation(JFrame.EXIT_ON
11、_CLOSE);frame.setVisible(true);,Java,布局管理器,class CalculatorFrame extends JFrame public CalculatorFrame()setTitle(Calculator);CalculatorPanel panel=new CalculatorPanel();add(panel);pack();,Java,布局管理器,class CalculatorPanel extends JPanel private JButton display;private JPanel panel;private double resu
12、lt;private String lastCommand;private boolean start;,Java,布局管理器,public CalculatorPanel()setLayout(new BorderLayout();result=0;lastCommand=;start=true;/add the display display=new JButton(0);display.setEnabled(false);add(display,BorderLayout.NORTH);ActionListener insert=new InsertAction();ActionListe
13、ner command=new CommandAction();,Java,布局管理器,/add the buttons in a 4 x 4 grid panel=new JPanel();panel.setLayout(new GridLayout(4,4);addButton(7,insert);addButton(8,insert);addButton(9,insert);addButton(/,command);addButton(4,insert);addButton(5,insert);addButton(6,insert);addButton(*,command);,Java,
14、布局管理器,addButton(1,insert);addButton(2,insert);addButton(3,insert);addButton(-,command);addButton(0,insert);addButton(.,insert);addButton(=,command);addButton(+,command);add(panel,BorderLayout.CENTER);private void addButton(String label,ActionListener listener)JButton button=new JButton(label);button
15、.addActionListener(listener);panel.add(button);,Java,布局管理器,private class InsertAction implements ActionListener public void actionPerformed(ActionEvent event)String input=event.getActionCommand();if(start)display.setText();start=false;display.setText(display.getText()+input);,Java,布局管理器,private clas
16、s CommandAction implements ActionListener public void actionPerformed(ActionEvent event)String command=event.getActionCommand();if(start)if(command.equals(-)display.setText(command);start=false;else lastCommand=command;,Java,布局管理器,else calculate(Double.parseDouble(display.getText();lastCommand=comma
17、nd;start=true;public void calculate(double x)if(lastCommand.equals(+)result+=x;else if(lastCommand.equals(-)result-=x;else if(lastCommand.equals(*)result*=x;else if(lastCommand.equals(/)result/=x;else if(lastCommand.equals(=)result=x;display.setText(+result);,Java,文本输入,具有用户输入和编辑文本功能的组件javax.swing.te
18、xt.JTextField文本域组件,一般只能接收单行文本输入javax.swing.text.JTextArea文本区组件,可以接收多行文本输入,Java,JTextComponent,JTextField,JTextArea,文本输入,javax.swing.text.JTextComponentvoid setText(String t);string getText();void setEditable(boolean b);,Java,文本输入,javax.swing.text.JTextFieldJTextField();JTextField(int columns);JTextF
19、ield(String text);JTextFiled(String text,int columns);在JTextField的构造器中设定的列宽度并不是用户能输入的字符个数的上限,用户可以输入一个更长的字符串,但是当文本长度超过文本域长度时输入时就会滚动;,Java,文本输入,将文本域添加到窗口中将文本域添加到面板或其他容器中eg:JPanel panel=new JPanel();JTextField textField=new JTextField(“input”,20);panel.add(textField);,Java,文本输入,标签组件标签是容纳文本的组件,不响应用户输入;可
20、以利用标签标识组件,如文本域;用相应的文本构造JLabel组件;将标签组件放置在离要标识的组件足够近的地方;,Java,文本输入,javax.swing.JLabel;JLabel(String text);JLabel(Icon icon);JLabel(String text,int align);JLabel(String text,Icon icon,int align);align可以用SwingConstants接口中的常量来指定,也可以用JLabel来指定,因为其实现了该接口;例:JLabel label=new JLabel(“hours”,SwingConstants.RIG
21、HT);或者 JLabel label=new JLabel(“hours”,JLabel.RIGHT);上面的代码创建了一个label,并指定label的对齐方式为右对齐。,Java,实例:一个可以设置时间的钟,本程序演示了一个可以设置时间的钟。当用户在文本域中输入相应的小时和分钟数值时,时钟就将长针和短针绘制出来。本程序需要监视文本域的变化。文本类组件(继承JTextComponent的组件)由文本模型(Document接口)来捕捉,获得文本模型的方法是:Document getDocument();/JTextComponent再在文本模型安装文档监听器(document listene
22、r)能够监听文本域变化事件。,实例的关键代码,当文本发生改变后,文档监听器(document listener)能够监听到该事件的发生,并调用DocumentListener接口中的下列方法之一:void insertUpdate(DocumentEvent event)void removeUpdate(DocumentEvent event)void changedUpdate(DocumentEvent event)当添加或者删除字符时,应该调用前两个方法。所以文档监听器(document listener)必须实现这三个方法:void insertUpdate(DocumentEven
23、t event)setClock();void removeUpdate(DocumentEvent event)setClock();void changedUpdate(DocumentEvent event),文本区(JTextArea),文本区JTextArea组件可以让用户输入多行文本。在JTextArea组件中,可以指定文本区的行数和列数:textArea=new JTextArea(8,40);/8行40列,一行的结尾为n(回车)符如果文本区的文本超出显示范围,则其余的文本会被剪裁。可以使用换行来避免行过长:textArea.setLineWrap(true);在Swing中,文
24、本区没有滚动条,需要手动安装:JScrollPane scrollPane=new JScrollPane(textArea),演示TextAreaTest.java,选择组件,常见的选择组件有:复选框、单选按钮、选项列表以及滑块等。1).复选框JCheckBox如果想要接收的输入只有“是”或“非”两者的话,可以使用复选框组件。这种组件缺省带有标识标签。,在构造器中可以指定该标签的文本:bold=new JCheckBox(“Bold”);用户点击复选框的动作监听器为实现ActionListener接口。(actionPerformed方法),复选框实例:监听器使用,实例:使用选中的字型显示字
25、符串。bold=new JCheckBox(“Bold”);italic=new JCheckBox(“Italic”);.class CheckBoxListener implement ActionListener public actionPerformed(ActionEvent event)CheckBoxListener listener=new CheckBoxListerner();bold.addActionListener(listener);italic.addActionListener(listener),演示CheckBoxTest.java,复选框JCheckBo
26、x的常用函数,Java.swing.JCheckBox JCheckBox(String label)用给定的标签构造一个复选框 JCheckBox(String label,boolea state)用给定的标签和初始化状态构造一个复选框 boolean isSelected()返回复选框状态 void setSelected(boolean state)为复选框设置状态,2).单选按钮,单选按钮(radio button group):提供一组选择项,用户在任何时刻只能选择一个选择项,每个选项都带有一个标签。在Swing中实现单选按钮对象的步骤:1.创建一个ButtonGroup对象But
27、tonGroup group=new ButtonGroup();2.将JRadioButton对象添加到按钮组中,ButtonGroup对象负责当新按钮被按下时,取消前一个按下的操作。JRadioButton smallButton=new JRadioButton(Small,false);group.add(smallButton);JRadioButton mediumButton=new JRadioButton(Medium,true);group.add(mediumButton);3.将多个单选按钮添加到面板时 要一个个添加 panel.add(smallButton);pan
28、el.Add(mediumButton);,单选按钮实例:监听器使用策略,用户点击单选按钮的动作监听器为实现ActionListener接口(actionPerformed方法)的类对象。方法一:如果各个单选按钮中的按钮的行为相似,可以让它们共享一个监听器。(需要在动作监听器中判断哪一个按钮被选中,然后才能作出响应),方法二:单选按钮的每个按钮对象设置其独有的监听器。(推荐),单选按钮实例:监听器使用,实例:当选中不同的按钮时,使用不同的字体显示字符串。,演示RadioButtonTest.java,请自学边界(Border)组合框(JComboBox)滑块(JSlider)微调控制器(JSp
29、inner),菜单,菜单的创建可以遵循以下步骤:1.创建一个菜单栏JMenuBar menuBar=new JMenuBar();2.将菜单栏放置在frame的顶部。frame.setMenuBar(menuBar);3.为每个菜单建立一个菜单对象JMenu editMenu=new JMenu(“Edit”);4.将顶层菜单添加到菜单栏中menuBar.add(editMenu);,菜单(续),菜单的创建可以遵循以下步骤(续):5.往菜单对象中添加菜单项、分隔符和子菜单JMenuItem pasteItem=new JMenuItem(Paste);editMenu.add(pasteIte
30、m)等价于使用JMenu.add(String)方法将菜单项插入到菜单的末尾 JMenuItem pasteItem=editMenu.add(Paste);editMenu.addSeparator();JMenu optionsMenu=new JMenu(“Options”).;/一个子菜单 editMenu.add(optionsMenu);6.用户点击菜单项的动作监听器为实现ActionListener接口(actionPerformed方法)的类对象或关联一个动作事件:ActionListener listener=.;pasteItem.addActionListener(lis
31、tener);,菜单中的图标,JMenuItem类扩展自AbstractButton类,故菜单项与按钮很相似。有2种方法为菜单项指定一个图标:1.构造器方法JMenuItem cutItem=new JMenuItem(“Cut”,new ImageIcon(“cut.gif”);/默认图形在名称的左侧2.setIcon方法cutItem.setIcon(new ImageIcon(“cut.gif”);,弹出菜单,弹出菜单(pop-up menu):即单击鼠标右键可弹出的快捷菜单。建立弹出菜单的方法与一般菜单相似:(1)创建一个弹出式菜单 JPopupMenu popup=new JPopu
32、pMenu();(2)在菜单中添加菜单项:JMenuItem item=new JMenuItem(Cut);item.addActionListener(listener);popup.add(item);,弹出式触发器,弹出式触发器(pop-up trigger):用户点击鼠标某个键时,弹出菜单。在Windows或者Linux中,弹出式触发器是右键。要想在用户点击某一个组件的时候弹出菜单,就要使用弹出式触发器:component.setComponentPopupMenu(popup);,快捷键,可以为菜单项设置快捷键。在当前菜单打开的情况下,可以按下某菜单项的快捷键,相当于鼠标单击了该菜
33、单项。JMenuItem CutItem=new JMenuItem(“Index”);CutItem.setMnemonic(“I”);快捷键就会自动显示在菜单项中,快捷键下面有一条下划线。,加速器,加速器可以在不打开菜单的情况下选中菜单项的快捷键。例如,很多程序把CTRL+O和CTRL+S关联到菜单中的Open和Save项。使用SetAccelerator方法可以将加速器关联到一个菜单项。该方法使用KeyStroke类型的对象作为参数。例如:openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,InputEvent.CTR
34、L_MASK);当用户按下加速器组合键时,就自动选择了相应的菜单项,同时激活一个动作事件。注意:加速器实际上并不打开菜单,而是直接激活菜单关联的动作事件。,启用和禁用菜单项,在程序运行过程中,经常需要屏蔽某些暂时不适用的命令,待到条件允许时再使之重新可用。屏蔽/启用菜单项的方法:aMenuItem.setEnabled(boolean)当参数值为false时,屏蔽该菜单项;当参数值为true时,启用该菜单项;如果需要动态启用/屏蔽某菜单项,则需要为“menu selected”事件注册监听器。javax.swing.event包定义了MenuListener接口,它有三个方法:void men
35、uSelected(MenuEvent event)void menuDeselected(MenuEvent event)void menuCanceled(MenuEvent event),监听“menu selected”事件,只需要监听“menu selected”事件,当一个菜单被选中时,将菜单中包含的需要被屏蔽的菜单项屏蔽掉即可。例如下列代码可屏蔽/打开Save按钮和Save As按钮。public void menuSelected(MenuEvent event)saveMenuItem.setEnabled(!readonlyItem.isSelected();saveAsM
36、enuItem.setEnabled(!readonlyItem.isSelected();,练习:创建一组菜单,演示MenuTest,补充:Action接口,public interface Action extends ActionListener;接口中包含以下方法:void actionPerformed(ActionEvent event);void setEnabled(boolean b);void putValue(String key,Object value);Object getValue(String key);常用预定义的动作表名称:名称 值 NAME 动作名称;可用
37、来设置按钮和菜单的标签 SMALL_ICON下班 小图标;可用来设置按钮和菜单的图标 SHORT_DESCRIPTION 简要说明,在工具按钮中作为提示 MNEMONIC_KEY 快捷键,用于菜单 action.putValue(Action.NAME,”Blue”);action.putValue(Action.SMALL_ICON,new IamgeIcon(“blue-ball.gif”);,补充:Action接口,利用一个实现了Action接口的对象可以实例化按钮、菜单项等;JButton(Action a);JMenuItem(Action a);AbstractAction类实现了
38、Action接口中除actionPerformed方法之外的所有方法,因此可通过简单地扩展AbstractAction类,并在扩展中实现actionPerformed方法来构造一个实现了Action接口的对象;,工具栏,工具栏是在程序中提供快速访问常用命令的按钮栏。工具栏的特别之处在于可以移动,脱离工具栏或拖拽到框架其他地方,如图所示。,工具栏的创建,创建工具栏时,直接将组件添加到工具栏中,再将工具栏放入框架中:JToolBar bar=new JToolBar();bar.add(blueButton);frame.add(bar,BorderLayout.NORTH);也可以使用添加Act
39、ion对象的方法来填充工具栏:bar.add(blueAction);可以设置工具提示:setToolTipText(String);如果使用Action对象,则可使用putValue方法putValue(Action.SHORT_DESCRIPTION,“”)注意:按钮是工具栏中最常见的组件类型。但其它组件也可以放置在工具栏中,例如复选框等。,工具栏相关的常用方法,javax.swing.JToolBar JToolBar()JToolBar(String titleString)JToolBar(int orientation)/orientation:SwingConstants.HOR
40、IZONTAL或SwingConstants.VERTICAL JButton add(Action a)在工具栏添加与动作a关联的按钮 void addSeparator()在工具栏末尾添加一个分隔符 void setToolTipText(String text)设置当鼠标停留在组件上时显示的工具提示的文本,对话框的种类,对话框分为模式对话框和无模式对话框。模式对话框 在用户结束对它的操作之前,不允许用户与应用程序其它窗口的交互。-模式对话框的作用:在程序继续运行之前,必须获得用户提供的信息。-例如:用户希望读取某个文件,则可以使用模式对话框请求用户提供文件名以及路径。,无模式对话框,无模
41、式对话框允许用户同时在对话框和应用程序的其余窗口中输入信息。例如:工具栏。工具栏可以停靠在任何地方,并且用户可以在需要时与应用程序窗口和工具栏进行交互。下面将会介绍一些常用的对话框:选项对话框、文件对话框、颜色对话框,1).选项对话框,Swing提供了一组简单的对话框,用于收集用户提供的简单信息。JOptionPane有四个用于显示这些简单对话框的静态方法:showMessageDialog:显示一条消息,并等待用户点击OKshowConfirmDialog:显示一条消息,并等待用户点击确认(与OK/Cancel按钮类似)showOptionDialog:显示一条消息,并等待用户作出某一种选择
42、showInputDialog:显示一条消息,并等待用户输入一行信息,选项对话框,假设需要显示一个如图所示的 对话框,要求用户确认或者取消,它是一个确认对话框:int selection=JOptionPane.showConfirmDialog(parent,“Message”,“Title”,/设置要显示的字符串和标题 JOptionPane.OK_CANCEL_OPTION,/设置OK和Cancel按钮 JOptionPane.QUESTION_MESSAGE);/设置图标为问题图标/selection的值指示了用户单击了哪一个按钮 if(selection=JOptionPane.OK
43、_OPTION).具体的参数选择可以参照JAVA API:DEFAULT_OPTION YES_NO_OPTION YES_NO_CANCEL_OPTION OK_CANCEL_OPTION,图标设置参数:ERROR_MESSAGE INFORMATION_MESSAGE WARNING_MESSAGE QUESTION_MESSAGE PLAIN_MESSAGE,示例,不同类型模式对话框示例 OptionDialogTest.java,创建对话框,如何创建一个对话框?可遵循以下几个步骤:1.创建一个派生自JDialog类的子类。2.在子类的构造器中,需要进行以下工作:(1)调用超类的构造器,
44、用于指定对话框的拥有者(owner frame),(也可以设置为null,但这样对话框有可能被隐藏在其它窗口后面),对话框的标题以及是否模式对话框(2)添加对话框的用户界面组件(3)添加相应的事件处理器(5)设置对话框的大小,举例:创建一个About对话框,public AboutDialog extends JDialog public AboutDialog(JFrame owner)/调用超类构造器 super(owner,“About DialogTest”,true);/添加组件和事件监听器,true参数表示该对话框为模式对话框 add(new JLabel(“),BorderLay
45、out.CENTER);JPanel panel=new JPanel();JButton ok=new JButton(Ok);ok.addActionListener(new ActionListener()public void actionPerformed(ActionEvent event)setVisible(false););panel.add(ok);add(panel,BorderLayout.SOUTH);setSize(250,150);/设置对话框大小,演示DialogTest.java,2.文件对话框,文件对话框用于帮助用户打开或者保存文件。下面是建立文件对话框并且
46、获取用户选择信息的步骤:1.建立一个JFileChooser对象。与JDialog类构造器不同,这种对象不需要指定父组件,因而允许在多个框架中重用一个文件选择器:JFileChooser chooser=new JFileChooser();2.调用setCurrentDirectory方法设置当前目录chooser.setCurrentDirectory(new File(.);3.如果要指定一个默认的文件名,可以使用setSelectedFile方法:chooser.setSelectedFile(new File(filename);,文件对话框(续),4.如果要允许用户能同时选中多个文
47、件,可以使用setMultiSelectionEnabled方法:chooser.setMultiSelectionEnabled(true);5.如果希望在对话框中只显示某一类型的文件(如所有.gif文件),则需要文件过滤器。(后面讨论)6.默认情况下只能选中文件,如果希望允许用户选择目录,则需要使用setFileSelectionMode方法,相关参数值为:JFileChooser.FILES_ONLY/JFileChooser.DIRECTORIES_ONLY/JFileChooser.FILES_AND_DIRECTORIES 例如:chooser.setFileSelectionMo
48、de(JFileChooser.FILES_ONLY);,文件对话框(续2),7.调用showOpenDialog或者showSaveDialog方法显示对话框。必须为这些调用指定父组件:int result=chooser.showOpenDialog(parent);int result=chooser.showSaveDialog(parent);/result的值可以为:JFileChooser.APPROVE_OPTION或 JFileChooser.CANCEL_OPTION 或 JFileChooser.ERROR_OPTION8.使用getSelectedFile或者getSe
49、lectedFiles来获得选中的一个或多个文件。这些方法返回一个File对象或者一组File对象。如果需要文件对象名字时,可以调用getPath方法:String filename=chooser.getSelectedFile().getPath();,文件过滤器(1),文件过滤机制:如果要限制只显示某种文件,则需要创建一个扩展自javax.swing.filechooser.FileFilter抽象类的对象。把每个文件传递给文件过滤器,只有文件过滤器接受的文件才被最终显示。FileFilter抽象类中有两个抽象方法必须在子类中实现:public boolean accept(File f
50、);/测试是否接收一个文件public String getDescription();/返回显示在文件对话框中文件类型的说明信息例如创建一个只显示.gif文件的过滤器:public class GifFilter extends FileFilter public boolean accept(File f)return f.getName().toLowerCase().endsWith(.gif)|f.isDirectory();/若f的后缀为.gif或者f是一个目录,则可以显示 public String getDescription()return GIF Image;,文件过滤器(