java基础之GUI

通过GUI,掌握图形编程的常用原理,其他图形编程的原理一样,例如:VC,C#等。

(一)Awt和Swing

java.awt:bstractWindow ToolKit(抽象窗口工具集,需要调用本地系统方法实现功能,属于重量级控件。

java.swing:在AWT的基础上,建立的一套图形界面系统,其中提供更多的组件,而且完全由java实现,增强了移植性,属于轻量级控件。

注意:这里的“重量级”是指跟平台结合紧密,轻量级与之相反。

1、AWT(abstract  Window  Toolkit):java程序是跨平台的,具体实际中的窗口可能是VC或者C++开发的,java在它们基础之上又抽象了一层,在各平台上都可以运行,因此叫做抽象窗口开发包。用于java Application 的GUI(Graphics  User  Interface:图形用户界面)编程。

注意:AWT本身又应用了操作系统的一些特有东西,各自的实现又不一样,因此窗口在不同平台的展示可能不一样,所以AWT有缺陷,已被Swing取代。但是Swing的基础还是依靠AWT。

结论:AWT依赖于本地系统平台,比如颜色样式显示;Swing是跨平台的。

2、GUI的各种元素(如:窗口,按钮,文本框等)通过java类实现。

AWT中的两个核心类是Container(容器)和Component类

 

Label:Label对象是一个可在容器中放置文本的组件。一个标签只显示一行只读文本。文本可由应用程序更改,但是用户不能直接对其进行编辑。(就是显示一行只读文本)

其中Dialog:对话框又分为:有模式的和无模式的

无模式Dialog:程序仍能激活它所依赖的窗口或组件,它也不堵塞线程的执行。

有模式Dialog:程序不能激活它所以依赖的窗口或组件,会使线程停止执行。

(二)component类

Java图形用户界面最基本组成部分是Component,Component类及其子类的对象用来描述以图形化的方式显示在屏幕上并能够与用户进行交互的GUI元素(标签、按钮)。

注意:一般的component对象不能独立的显示出来,必须放在某一Container对象中才可以显示出来。

(三)Container类

Container是Component的子类,Container子类对象可以容纳别的Component对象,因此,Container对象也可以被当作Component对象添加到其他Container对象中。  

Container它有一定的范围和大小,一般都是矩形。也有一定的位置,这个位置可分相对位置和绝对位置。

一个Container中可以包含其他Container,Container中可以嵌套Container,当Container显示时候,它里面的元素也被小时出来,当Container隐藏时或者关闭时,它包含的元素也被隐藏。

注意:Container对象可以使用方法add(..)向其中添加其他Component对象。

1、有两种常用的Container:

Window:Window对象表示自由停泊的顶级窗口。

注意:尽量不要单独显示一个window对象,可以显示他的子类。

Panel:Panel对象可作为容纳其他Component对象,但不能够独立存在,必须被添加到其他Container中,比如说Window或者Frame中。

其中:对于Swing包:和AWT区分,Swing中是JFrame、JPanel和JComponent即:在前面加J,其他的区别不大。

(四)Window类

Window 对象是一个没有边界和菜单栏的顶层窗口。窗口的默认布局是 BorderLayout

构造窗口时,它必须拥有窗体、对话框或其他作为其所有者定义的窗口。

因此,尽量不要单独显示一个window对象,可以显示他的子类。

 

注意:顶层窗口(包括 WindowFrame 和 Dialog)的位置和大小受桌面窗口管理系统的控制。对 setLocationsetSize 和 setBounds 的调用是转发到窗口管理系统的请求(不是指令)。将尽所有努力响应这样的请求。但是,在某些情况下,窗口管理系统可以忽略这样的请求,或修改请求的几何结构,以放置和调整 Window 的大小,使之更好地与桌面设置匹配。

由于本机事件处理的异步特性,在处理完最后一个请求前,getBounds,getLocation,getLocationOnScreen和getSize

返回的结果可能不反映屏幕上窗口的实际几何结构。在处理后续请求的过程中,窗口管理系统满足这些请求时,这些值可能会相应地改变。

window类有两个子类:Frame和Dialog

1、Frame类

Frame 是带有标题和边框的顶层窗口。

常用的构造函数:

Frame()

Frame(String title):创建标题栏为字符串title的窗口。

注意:在创建窗体时,尽量自定义类来继承Frame类创建窗体,继承可以创建自己的成员变量,更加的灵活。

常用方法:

注意:所有配置的边界都是相对于虚拟坐标系,虚拟坐标系的原点位于主物理屏幕的左上角。是否使用负坐标取决于主物理屏幕在虚拟设备中的位置

setBounds(int x,int y, int width, int height):x,y是窗口左上角的坐标,width和height是窗口的宽度和长度。

setSize(int width,int height):设置窗体的大小,width和height分别是宽度和高度。

setLocation(int x,int y):设置窗体的位置,x,y是左上角的坐标

注意:区分开setLocation()和setSize()两个函数,虚拟坐标系的原点位于屏幕的左上角。

setBackground(Color c):设置背景颜色,参数为Color对象。

注意:图形编程中颜色是一个类:Color类,使用RGB模式,其中用1个字节表示一种颜色,即0-255.

setVisible(boolean b):设置是否可见(常用)

setTitle(String name):设置标题

setResizable(boolean b):设置是否可以调整大小。

2、Panel类:

Panel对象可以看成可以容纳Component对象。

注意:Panel对象不能单独存在,必须被添加到其他Container中,比如说Window或者Frame中。

Panel对象可以拥有自己的布局管理器,拥有从其父类继承来的方法即:可以直接使用父类的方法。

常用构造方法:

Panel():使用默认的FlowLayout类布局管理器初始化

Panel(LayoutManger layout):使用指定的布局管理器初始化

Frame f = new Frame("java Frame with Panel");

Panel p = new Panel(null);

f.setLayout(null);

f.setBounds(300,300,500,500);

f.setBackground(new Color(0,0,102));

p.setBounds(50,50,400,400);

p.setBackground(new Color(203,203,255);

f.add(p);

f.setVisible(true);

(五)布局管理器(接口:LayoutManager)

布局管理器:管理Component在Container中的布局,不必直接设置Component位置和大小。即:通过算法自动设置器件的位置和大小,不需要自己进行管理。

每个Container都有一个布局管理器对象,当容器需要对某个组件进行定位或者判断其大小尺寸时,就会调用其对应的布局管理器,调用Container的setLayout(LayoutManager layout)方法改变其布局管理器对象。

注意:若用户需要亲自设定组件大小或位置,则需取消该容器的布局管理器,方法为:setLayout(null)

AWT提供了5中布局管理器类,这5个类都实现了LayoutManager接口:

FlowLayout   BorderLayout    GridLayout   CardLayout    GridBagLayout

一、FlowLayout类布局管理器的特征:流水的布局方式

FlowLayout是Panel类的默认布局管理器。

FlowLayout布局管理器对组件逐行定位,行内从左到右,一行排满后换行。

不改变组件的大小,按组件原有尺寸显示组件,可设置不同的组件间距,行距以及对齐方式。

注意:FlowLayout布局管理器默认的对齐方式是居中。

构造方法:

new  FlowLayout(FlowLayout.RIGHT,20,40);右对齐,组件之间水平间距20个像素,垂直间距40个像素

new  FlowLayout(FlowLayout.LEFT):左对齐,水平和垂直间距默认是5(缺省值)

new  FlowLayout();使用缺省的居中对齐方式,水平和垂直间距为缺省值:5

二、BorderLayout类布局管理器的特征:内部划分为东南西北中五个区域

BorderLayout是Frame类的默认布局管理器。

BorderLayout将整个容器的布局划分成:东(EAST)、西(WEST)、南(SOUTH)、北(NORTH)、中(CENTER)五个区域,组件只能被添加到指定的区域。

注意:如果不指定组件的加入部位,则默认加入到CENTER区,每个区域只能够加入一个组件,若要加入多个,则会覆盖先前加入的组件。

1、容器尺寸缩放原则:

南北两个区域在水平方向缩放

东西两个区域在垂直方向缩放

中部可以在两个方向缩放

2、使用方式:

Frame  f = new Frame("BorderLayout");

Button bn = new Button("BN");

Button bs = new Button("BS");

Button bw = new Button("BW");

Button be = new Button("BE");

f.add(bn,"North");  //North写错,编译不会报错,易出错

f.add(bn,BorderLayout.NORTH);   //推荐这种方式,不容易出错。

三、GridLayout类布局管理器的特征:将空间划分成规则的矩形网格

GridLayout型布局管理器将空间划分成规则的矩形网格,每个单元格区域大小相等。组件被添加到每个单元格中,先从左到右添满一行后换行,一次添加。

构造方法:

GridLayout(行数,列数);构造函数中指定分割的行数和列数

Frame  f = new Frame("GridLayout");

Button b1 = new Button("B1");

Button b2 = new Button("B2");

f.setLayout(new GridLayout(3,2));

f.add(b1);

f.add(b2);

f.pack();  // 调整此窗口的大小,以适合其子组件的首选大小和布局。

f.setVisible(true);

(六)事件模型:事件监听(重点)

在java.awt.event包中

 

事件模型:通过窗体即Frame对象给组件注册一个事件监听器(通过addXxxListener(XxxListener  object)方法),监听组件是否发生指定事件即XxxEvent(事件对象封装了事件源发生事件的相关信息),如果发生指定事件,就调用监听器中指定的事件处理方法,完成指定动作。

事件:用户对组件的一个操作,事件对象封装了事件源发生事件的信息

事件源:发生事件的组件     

监听器:我们需要处理某个事件,就需要在发生事件的组件上添加监听器,也就是java.awt.event包中XxxListener接口的子类。

事件处理器:监听器中的方法。监听器被添加在组件上之后,组件上发生了对应事件就会执行指定方法

一、常用事件分类

1、动作事件,ActionEvent,在某一组件上发生了定义好的动作,例如按钮上鼠标点击或按空格,菜单上鼠标点击或按回车等

TextField事件监听:TextField对象可能发生Ation(光标在文本框内敲回车)事件,该事件对应的事件类是ActionEvent。

用来处理ActionEvent事件的对象:实现了java.awt.event.ActionListener接口的类的对象。

实现的方法:public  void  actionPerformed(ActionEvent  e);该方法用于对事件做处理过程。

使用addActionListener(ActionListener l)方法为组件注册一个监听器对象。

注意:TextField对象的方法:setEchoChar(char  c):设置文本字段的回显字符,对于密码很有用。例如setEchoChar('*'),回显字符以*显示。

ActionEvent对象的方法:getSource()方法:返回最初发生事件的对象。例如 TextField  tf = (TextField)e.getSource();

2、窗体事件,WindowEvent,窗体打开、关闭、正在关闭、激活、最小化等。

抽象类java.awt.event.WindowAdapter实现了WindowListener接口,可以使用其子类作为WindowEvent的监听器,只要重写其对应的方法即可。

3、鼠标事件,MouseEvent,鼠标按下、抬起、进入、移出等。

抽象类java.awt.event.MouseAdapter实现了MouseListener接口,可以使用其子类作为MouseEvent的监听器,只要重写其对应的方法即可。

4、键盘事件,KeyEvent,键盘按下、抬起等。

抽象类java.awt.event.KeyAdapter实现了KeyListener接口,可以使用其子类作为KeyEvent的监听器,只要重写其对应的方法即可。

注意:KeyEvent将键盘上的所有键转换成虚拟的数值码,定义静态常量与虚拟的数码值对应。VK开头(visual  key)。

二、Adapter(适配器模式):通过继承抽象类,避免定义没有必要的空方法。

通常Listener接口中都是有多个抽象方法的,而我们使用的时候有可能不会全部需要,如果定义子类实现接口那么就必须重写所有方法,这是比较麻烦的。Java中为我们提供了一些抽象的实现类,在类中对接口的抽象方法进行了空的实现,我们再定义监听器时只要继承这些抽象类就可以,这样那些不需要的方法就可以不再去重写了。

public classDemo1_Frame{

   public static void main(String[] args) {

      Frame frame = new Frame("这是我的第一个GUI程序");     // 创建一个窗口

      frame.setSize(400,300);                  // 设置大小

      frame.setLocation(800,100);                 // 设置位置

      frame.setLayout(new FlowLayout());              // 设置布局方式

      Button button = new Button("退出");             // 创建一个按钮

      frame.add(button);                        // frame中添加按钮

     

      frame.addWindowListener(new MyWindowListener());  // 给窗口添加一个窗体监听器

//    button.addMouseListener(newMyMouseListener());   // 给按钮添加鼠标监听器

//    button.addKeyListener(newMyKeyListener());       // 给按钮添加键盘监听器

      button.addActionListener(new MyActionListener()); // 给按钮添加动作监听器(默认事件就是鼠标点击和键盘上空格键抬起)  

      frame.setVisible(true);                      // 设置可见

   }

}

class MyWindowListenerextendsWindowAdapter/*implements WindowListener*/ {   // 定义类实现监听器接口或者继承适配器

   public void windowClosing(WindowEvent e) {   // 实现事件处理方法

//    Frameframe = (Frame) e.getSource(); // 获取事件源

//    frame.setVisible(false);          // 隐藏暂时不显示

//    frame.dispose();               // 摧毁释放资源

      System.exit(0);                   // 退出程序

   }

}

class MyMouseListenerextendsMouseAdapter/*implements MouseListener*/ {

   public void mouseClicked(MouseEvent e) {

      System.exit(0);

   }

}

class MyKeyListenerextendsKeyAdapter/*implements KeyListener*/ {

   public void keyReleased(KeyEvent e) {

      if (e.getKeyCode() == KeyEvent.VK_SPACE)  // 如果按下的是空格键

        System.exit(0);

   }

}

class MyActionListenerimplementsActionListener { // 由于只有1个方法没有适配器

   public void actionPerformed(ActionEvent e) {

      System.exit(0);

   }

}

 

(七)内部类

内部类:就是将一个类的定义放在一个类的定义内部。

内部类的好处:

1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,

2.内部类提供了更好的封装,除了该外围类,其他类都不能访问。

3.在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

4.可以方便的访问外围类的元素。

个人觉得第一点是最重要的原因之一,内部类的存在使得Java的多继承机制变得更加完善。接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。(比如,一个类实现多个接口,多个接口中有同一个名字的方法,通过内部类就可以很好地解决)

注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。

(重点用法)

内部类如何使用外部类的属性和方法,以及使用.this与.new。

引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类: OuterClass.InnerClass innerClass = outerClass.new InnerClass();

同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this。

重点注意匿名内部类的用法:

 1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

 2、匿名内部类中是不能定义构造函数的。

 3、匿名内部类中不能存在任何的静态成员变量和静态方法。

 4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

 5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

 6、注意外部类的方法的形参,当所在的方法的形参需要被内部类里面使用时,该形参必须为final。

实例:

  1. public class Outer { 
  2.     public static void main(String[] args) { 
  3.         Outer outer = new Outer(); 
  4.         Inner inner = outer.getInner("Inner""gz"); 
  5.         System.out.println(inner.getName()); 
  6.     } 
  7.  
  8.     public Inner getInner(final String name, String city) { 
  9.         return new Inner() { 
  10.             private String nameStr = name; 
  11.  
  12.             public String getName() { 
  13.                 return nameStr; 
  14.             } 
  15.         }; 
  16.     } 
  17. interface Inner(){
  18. String getName();
  19. }

内部类参考文档:https://www.cnblogs.com/chenssy/p/3388487.html

匿名内部类参考文档:http://android.blog.51cto.com/268543/384844

(八)Graphics类(类似context,该类的对象可以获取当前图形环境的所有相关信息)就像一个画笔。

注意:所有图像实现机制都类似,都有一个特殊的方法用于绘制图形。java是paint(Graphics g)方法。

Graphics 类是所有图形上下文的抽象基类,允许应用程序在组件(已经在各种设备上实现)以及闭屏图像上进行绘制。

每个Component都有一个paint(Graphics g)方法用于实现绘图目的,每次重画该Component时,都自动调用paint()方法。

注意:这里是自动调用paint(Graphics g)方法。一般都会应用多态。

要画图重写paint(Graphics g)方法。

1、Graphics类中提供了很多绘图方法:

drawXxx()方法:绘制图形

fillXxx()方法:填充闭合图形   rect:矩形     oval:椭圆

注意:repaint()方法的使用:内部调用过程:先repaint(),在update(Graphics  g),再paint(Graphics g)。

update(Graphics g)方法用于实现双缓冲。

问题:由于机制是先用背景色覆盖所有内容,再在上面画上新内容。所以看到的闪烁就是背景色与新内容之间的时间差。

解决原理:在画出图片之前将要画的内容全部在内存中准备好,画时一起将内容写上去,且不需要用背景色覆盖。(重写update(Graphics g) 方法)

  1. private Image iBuffer;
  2. private Graphics gBuffer;
  3. public void update(Graphics scr)  
  4. {  
  5.     if(iBuffer==null)  
  6.     {  
  7.        iBuffer=createImage(this.getSize().width,this.getSize().height);  
  8.        gBuffer=iBuffer.getGraphics();  
  9.     }  
  10.        gBuffer.setColor(getBackground());  
  11.        gBuffer.fillRect(0,0,this.getSize().width,this.getSize().height);  
  12.        paint(gBuffer);  
  13.        scr.drawImage(iBuffer,0,0,this);  
  14. }  

 

双缓冲参考文档:http://blog.csdn.net/kai_wei_zhang/article/details/8120382

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一位远方的诗人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值