201777010217-金云馨《面向对象程序设计(java)》第十三周学习
阅读原文时间:2023年07月10日阅读:2

项目

内容

这个作业属于哪个课程

https://www.cnblogs.com/nwnu-daizh/

这个作业的要求在哪里

https://www.cnblogs.com/nwnu-daizh/p/11888568.html

作业学习目标

(1) 掌握事件处理的基本原理,理解其用途;

(2) 掌握AWT事件模型的工作机制;

(3) 掌握事件处理的基本编程模型;

(4) 了解GUI界面组件观感设置方法;

(5) 掌握WindowAdapter类、AbstractAction类的用法;

(6) 掌握GUI程序中鼠标事件处理技术。

第一部分:知识梳理

11.1事件处理基础

1)事件源(event source):能够产生事件的组件对象都可以成为事件源,如文本框、按钮等。事件源能够注册监听器并向监听器发送事件对象。
2)事件监听器(event listener):事件监听器接收事件源发送的通告(事件对象)后,对发生的事件作出响应。一个事件监听器就是一个实现了专门监听器接口的类实例,该类必须实现接口中的方法,这些方法当事件发生时,被自动执行。
3) 事件对象(event object) : Java将事件 的相关信息封装在事件对象中,不同的事件源可以产生不同类别的事件。所有的事件对象都派生于java. util. Event0bject类。

11.2监听器接口的实现

1)监听器类必须实现与事件源相对应的监听器接口,即必须提供接口中方法的实现。

2)监听器接口中方法实现
     class Mylistener implements ActionListener
     {
          public void actionPerformed (ActionEvent event)

{ ….. }

}

11.3命令按钮Jbutton主要API (教材444页)
1)创建按钮对象
      JButton类常用构造方法:

l JButton(String text):创建-一个 带文本的按钮。

l JButton(Icon icon):创建一-个 带图标的按钮。

l JButton(String text, lcon icon) :创建-一个带 文本和图标的按钮。

2)按钮对象的常用方法
getLabel( ):返回按钮的标签字符串:

l setLabel(String s):设置按钮的标签为字符串s

l 进一步把ColorAction改为匿lambda表达式
public void makeButton(String name , Color ackgroundColor)

{

JButton button=new JButton(name);
    buttonPanel,add(button);
    button.addActionListener( (e)->{
           buttonPanel.setBackground(backgroundColor);
    });

}

11.4改变观感的方法
Swing程序默认使用Metal观感,采用两种方式改变观感。

第一种:在Java安装的子目录jre/1ib下的文件sw ing. properties中,将属性swing. defaultlaf设置为所希望的观感类名。
        wing.defaultlaf =com.sun.java. swing.plaf.motif.MotifL ookAndFeel
第二种:调用静态的UIManager. setLookAndFee1方法动态地改变观感,提供所想要的观感类名,再调用静态方法SwingUtiliti es. updateComponentTreeUI来刷新全部的组件集。

11.5适配器类
当程序用户试图关闭一个框架窗口时,JFrame对象就是Wi ndowEvent的事件源。

1)捕获窗口事件的监听器:
         WindowListener listene…..:
         frame.addWindowListener(listener);
2)窗口监听器必须是实现WindowListener接口的类的一个对象,WindowListener接 口中有七个方法,它们的名字是自解释的。

(1)WindowListener接口

public interface WindowListener

{
              vold windowOpened(WindowEvent e);

void windowClosing(WindowEvente);

void windowClosed(WindowEvente);

void windowlconified(WindowEvent e);

void windowDeiconified(WindowEvente);

void windowActivated(WindowEvent e);

void windowDeactivated(WindowEvente);

}

(2)适配器类
●鉴于代码简化的要求,对于不止一个方法的AWT监听器接口都有一个实现了它的所有方法,但却不做任何工作的适配器类。
       例: WindowAdapter类。
●适配器类动态地满足了Java中实现监视器类的技术要求。
●通过扩展适配器类来实现窗口事件需要的动作。

(3)扩展WindowAdapter类
●扩展WindowAdapter类,继承六个空方法,并覆盖WindowClosing ()方法:
         class Terminator extends WindowAdapter

{
                   public void windowClosing(WindowEvent e)

{
                             System.exit(0);

}

}

(4)注册事件监听器
●可将一个Terminator对象注册为事件监听器:
            WindowListener listener=new Terminator();

frame.addWindowListener(listener);
●只要框架产生-一个窗口事件,该事件就会传递给监听器对象。
●创建扩展于WindowAdapter的监听器类是很好的
改进,但还可以进一步将.上面语句也可简化为:

frame.addWindowListener(new Terminator());

(5)用匿名类简化
frame.addWindowListener(new WindowAdapter()
{

publlc void windowClosing(WindowEvente)

{
              System.exit(0);
       }
});

11.6动作事件

1)动作事件
●激活一个命令可以有多种方式,如用户可以通过菜单、击键或工具栏上的按钮选择特定的功能。

●在AWT事件模型中,可设置用不同方式下达命令: (如:点击按钮、菜单选项、按下键盘),其操作动作都是一-样的。
2)动作接口及其类
●Swing包提供了非常实用的机制来封装动作命令,并将它们连接到多个事件源,这就是Action接口。

●动作对象是一个封装下列内容的对象:
        1.命令的说明: 一个文本字符串和一个可选图标;

2.执行命令所需要的参数。

●Action扩展于ActionListener接口,包含下列方法:
       void act ionPerformed (Act ionEvent event)void setEnabled(bool ean b);
       void isEnabled();
       void putValue (String key, 0bject va lue);
       void getValue (String key);
       void addPropertyChangeListner (ProperChangeL istener listener);
       void removePropertyChangeListener (Pr operChangeListener listener);

●Action是一个接口,而不是一个类,实现这个接口的类必须要实现它的7个方法。
●AbstractAction类实现了Action接口中除actionPerformed方法之外的所有方法,这个类存储了所有名/值对,并管理着属性变更监听器。

●在动作事件处理应用中,可以直接扩展AbstractAction类,并在扩展类中实现act ionPerformed方法。

3) 击键关联映射
●将一个动作对象添加到击键中,以便让用户敲击键盘命令来执行这个动作。
●将动作与击键关联起来,需生成KeyStroke类对象。
           KeyStroke ctrBKey = KeyStroke.getKeyStroke("Ctrl B");

11.7鼠标事件

●鼠标事件
   - MouseEvent

●鼠标监听器接口
   - MouseListener
   - MouseMotionListener

●鼠标监听器适配器
  - MouseAdapter
  - MouseMotionAdapter

● 鼠标点击监听器接口
public interface MouseListener extends EventListener {
            public void mouseClicked(MouseEvent e);

public void mousePressed(MouseEvent e);

public void mouseReleased(MouseEvent e);

public void mouseEntered(MouseEvent e);

public void mouseExited(MouseEvent e);
}
● 鼠标移动监听器接口
public interface MouseMotionListener extends EventListener {
           public void mouseDragged(MouseEvent e);

public void mouseMoved(MouseEvent e);

}

●用户点击鼠标按钮时,会调用三个监听器方法:
            -鼠标第一次被按下时调用mousePressed方法;

-鼠标被释放时调用mouseReleased方法;
            -两个动作完成之后,调用mouseClicked方法。
●鼠标在组件上移动时,会调用mouseMoved方法。
●如果鼠标在移动的时候还按下了鼠标,则会调用mouseDragged方法。

●鼠标事件返回值
            鼠标事件的类型是MouseEvent,当发生鼠标事件时:MouseEvent类自动创建一个事件对象,以及事件发生位置的x和y坐标,作为事件返回值。

●MouseEvent类中的重要方法
          - public int getX( );

- public int getY( ); 
          - public Point getPoint( );

- public int getClickCount( );

●设置光标
- java.awt.Component;
         public void setCursor(Cursor c);

- java.awt.Cursor;
        public static Cursor getDefaultCursor( );
        public static Cursor getPredefinedCursor(int type);

11.8AWT事件继承层次

●所有的事件都是由java.util包中的EventObject类扩展而来。
●AWTEevent 是所有AWT事件类的父类,也是EventObject的直接子类。
●有些Swing组件生成其他类型的事件对象,一般直接扩展于Event0bject,而不是AWTEvent,位于javax. swing. event. *。
●事件对象封装了事件源与监听器彼此通信的事件信息。在必要的时候,可以对传递给监听器对象的事件对象进行分析。

第二部分:实验部分

实验1: 导入第11章示例程序,测试程序并进行代码注释。

测试程序1:

● 在elipse IDE中调试运行教材443页-444页程序11-1,结合程序运行结果理解程序;

● 在事件处理相关代码处添加注释;

● 用lambda表达式简化程序;

● 掌握JButton组件的基本API;

● 掌握Java中事件处理的基本编程模型。

原代码代码如下:

ButtonFrame类

package button;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
* 带有按钮面板的框架
*/
public class ButtonFrame extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = ;
private static final int DEFAULT_HEIGHT = ;

public ButtonFrame()
{

   //用于设置框架大小  
  setSize(DEFAULT\_WIDTH, DEFAULT\_HEIGHT);

  // 创建按钮对象  
  var yellowButton = new JButton("Yellow");  
  var blueButton = new JButton("Blue");  
  var redButton = new JButton("Red");

  //创建面板对象  
  buttonPanel = new JPanel();

  //将三个按钮添加到面板中  
  buttonPanel.add(yellowButton);  
  buttonPanel.add(blueButton);  
  buttonPanel.add(redButton);

  // 将面板添加到框架中  
  add(buttonPanel);

  // 创建按钮动作  
  //为每种颜色创建一个对象,并将这些对象设置为按钮监听器  
  var yellowAction = new ColorAction(Color.YELLOW);  
  var blueAction = new ColorAction(Color.BLUE);  
  var redAction = new ColorAction(Color.RED);

  // 将动作和按钮联系起来  
  yellowButton.addActionListener(yellowAction);  
  blueButton.addActionListener(blueAction);  
  redButton.addActionListener(redAction);  

}

/**
* 一个动作监听器用于设置面板的背景颜色.
*/
private class ColorAction implements ActionListener
{
private Color backgroundColor;

  public ColorAction(Color c)  
  {  
     backgroundColor = c;  
  }

  public void actionPerformed(ActionEvent event)  
  {  
     buttonPanel.setBackground(backgroundColor);  
  }  

}
}

ButtonTest类

package Damo;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
* A frame with a button panel.
*/
public class ButtonFrame extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = ;
private static final int DEFAULT_HEIGHT = ;

public ButtonFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);//设定大小

  // create buttons  
  var yellowButton = new JButton("Yellow");//设置组件  
  var blueButton = new JButton("Blue");  
  var redButton = new JButton("Red");

  buttonPanel = new JPanel();//面板

  // add buttons to panel  
  buttonPanel.add(yellowButton);//组件添加至面板之中  
  buttonPanel.add(blueButton);  
  buttonPanel.add(redButton);

  // add panel to frame  
  add(buttonPanel);//再将面板添加至frame中

  // create button actions  
  var yellowAction = new ColorAction(Color.YELLOW);//设置事件监听器  
  var blueAction = new ColorAction(Color.BLUE);  
  var redAction = new ColorAction(Color.RED);

  // associate actions with buttons  
  yellowButton.addActionListener(yellowAction);//注册  .addActionListener(对象)  
  blueButton.addActionListener(blueAction);  
  redButton.addActionListener(redAction);  

}

/**
* An action listener that sets the panel's background color.
*/
private class ColorAction implements ActionListener
{
private Color backgroundColor;

  public ColorAction(Color c)  
  {  
     backgroundColor = c;  
  }

  public void actionPerformed(ActionEvent event)//响应事件改变背景颜色  
  {  
     buttonPanel.setBackground(backgroundColor);  
  }  

}
}

ButtonFrame

改进代码:

package button;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
* A frame with a button panel.
*/
public class ButtonFrame1 extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = ;
private static final int DEFAULT_HEIGHT = ;

public ButtonFrame1()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
//创建一个面板对象
buttonPanel = new JPanel();

  //连续调用三次makeButton方法  
  makeButton("yellow",Color.YELLOW);  
  makeButton("blue",Color.BLUE);  
  makeButton("red",Color.RED);  
  //将面板添加至框架中  
  add(buttonPanel);

}

/*没有显示地定义一个类,每次调用这个方法时,他会建立实现了ActionListener接口的一个类的实例。
* 它的actionPerformed动作会引用实际上随监听器对象存储的backGroundColor值。不过,所有这些会自动
* 完成,而无需显示定义随监听器类、实例对象或设置这些变量的构造器。
*/
public void makeButton(String name,Color backgroundColor)
{
JButton button=new JButton(name);
buttonPanel.add(button);
button.addActionListener(event->
buttonPanel.setBackground(backgroundColor));
}
}

运行截图:

测试程序2:

● 在elipse IDE中调试运行教材449页程序11-2,结合程序运行结果理解程序;

● 在组件观感设置代码处添加注释;

● 了解GUI程序中观感的设置方法。

代码如下:

PlafFrame类:

package plaf;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
*
*/
public class PlafFrame extends JFrame
{

private JPanel buttonPanel;

public PlafFrame()  
{  
    //创建一个面板对象  
    buttonPanel=new JPanel();

    //列举安装的所有观感实现  
    UIManager.LookAndFeelInfo\[\] infos=UIManager.getInstalledLookAndFeels();

    //得到每一种观感的名字和类名,并创建按钮  
    for(UIManager.LookAndFeelInfo info:infos)  
    {  
        makeButton(info.getName(),info.getClassName());  
    }

    //将按钮面板添加到框架上  
    add(buttonPanel);  
    pack();  
}

private void makeButton(String name, String className) {  
    JButton button=new JButton(name);  
    buttonPanel.add(button);

    button.addActionListener(event->{  
        //捕捉异常  
        try  
        {  
            //设置观感  
            UIManager.setLookAndFeel(className);  
            //刷新全部的组件  
            SwingUtilities.updateComponentTreeUI(this);  
            pack();  
        }  
        catch(Exception e)  
        {  
            e.printStackTrace();  
        }  
    });

}

}

PlafTest类:

package plaf;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class PlafFrame extends JFrame
{
private JPanel buttonPanel;

public PlafFrame()
{
buttonPanel = new JPanel();

  UIManager.LookAndFeelInfo\[\] infos = UIManager.getInstalledLookAndFeels();//获得一个用于描述已安装的所有观感实现的对象数组  
  for (UIManager.LookAndFeelInfo info : infos)  
     makeButton(info.getName(), info.getClassName());//得到每一种观感的名字和类名

  add(buttonPanel);  
  pack();//调整窗口大小  

}

private void makeButton(String name, String className)
{
// 添加按钮

  JButton button = new JButton(name);  
  buttonPanel.add(button);

  // 按钮动作

  button.addActionListener(event -> {  
     try  
     {  
        UIManager.setLookAndFeel(className);//设置当前观感  
        SwingUtilities.updateComponentTreeUI(this);//刷新全部组件集  
        pack();//调整窗口大小  
     }  
     catch (Exception e)  
     {  
        e.printStackTrace();  
     }  
  });  

}
}

运行结果如图:

测试程序3:

● 在elipse IDE中调试运行教材457页-458页程序11-3,结合程序运行结果理解程序;

● 掌握AbstractAction类及其动作对象;

● 掌握GUI程序中按钮、键盘动作映射到动作对象的方法。

代码如下:

ActionFrame类:

package action;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
* 带有一个面板的框架,用于演示颜色更改操作。
*/
public class ActionFrame extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = ;
private static final int DEFAULT_HEIGHT = ;

public ActionFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

  buttonPanel = new JPanel();

  // 定义动作  
  var yellowAction = new ColorAction("Yellow", new ImageIcon("yellow-ball.gif"),  
        Color.YELLOW);  
  var blueAction = new ColorAction("Blue", new ImageIcon("blue-ball.gif"), Color.BLUE);  
  var redAction = new ColorAction("Red", new ImageIcon("red-ball.gif"), Color.RED);

  // 为这些动作添加按钮  
  buttonPanel.add(new JButton(yellowAction));  
  buttonPanel.add(new JButton(blueAction));  
  buttonPanel.add(new JButton(redAction));

  // 将面板添加到框架中  
  add(buttonPanel);

  // 将Y,B,R这些键与名称联系起来  
  /\*  
   \*可以使用getInputMap方法从组件中得到输入映射  
   \* WHEN\_ANCESTOR\_OF\_FOCUSED\_COMPONENT条件意味着在当前组件包含了拥有键盘焦点的组件时会查看这个映射  
   \*/  
  InputMap inputMap = buttonPanel.getInputMap(JComponent.WHEN\_ANCESTOR\_OF\_FOCUSED\_COMPONENT);  
  inputMap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow");  
  inputMap.put(KeyStroke.getKeyStroke("ctrl B"), "panel.blue");  
  inputMap.put(KeyStroke.getKeyStroke("ctrl R"), "panel.red");

  // 将名称和动作联系起来  
  /\*  
   \* InputMap不能直接地将KeyStroke对象映射到Action对象。而是先映射到任意对象上,然后由ActionMap类实现  
   \* 将对象映射到动作上。  
   \*/  
  ActionMap actionMap = buttonPanel.getActionMap();  
  actionMap.put("panel.yellow", yellowAction);  
  actionMap.put("panel.blue", blueAction);  
  actionMap.put("panel.red", redAction);  

}

public class ColorAction extends AbstractAction
{
/**
* 构造颜色动作.
* @param name the name to show on the button
* @param icon the icon to display on the button
* @param c the background color
*/
//存储这个命令的名称,图标和需要的颜色
public ColorAction(String name, Icon icon, Color c)
{
//将所有信息存储在AbstractAction类提供的名/值对表中
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);

     //SHORT\_DESCRIPTION用为为工具提示设置做简要说明  
     putValue(Action.SHORT\_DESCRIPTION, "Set panel color to " + name.toLowerCase());  
     putValue("color", c);  
  }

  public void actionPerformed(ActionEvent event)  
  {  
      //获取名/值对表中名为color的值并将其强制转换为颜色对象  
     var color = (Color) getValue("color");  
     buttonPanel.setBackground(color);  
  }  

}
}

ActionTest类:

package action;

import java.awt.*;
import javax.swing.*;

/**
* @version 1.34 2015-06-12
* @author Cay Horstmann
*/
public class ActionTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(() -> {
var frame = new ActionFrame();
frame.setTitle("ActionTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}

运行结果如图:

测试程序4:

● 在elipse IDE中调试运行教材462页程序11-4、11-5,结合程序运行结果理解程序;

● 掌握GUI程序中鼠标事件处理技术。

代码如下:

MouseComponent类:

package mouse;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;

/**
* 带有鼠标操作的用于添加和删除正方形的组件。
*/
public class MouseComponent extends JComponent
{
private static final int DEFAULT_WIDTH = ;
private static final int DEFAULT_HEIGHT = ;

//正方形的边长
private static final int SIDELENGTH = ;

//用于存储正方形集合
private ArrayList squares;
private Rectangle2D current; //包含鼠标光标的正方形

public MouseComponent()
{
squares = new ArrayList<>();
current = null;

  addMouseListener(new MouseHandler());  
  addMouseMotionListener(new MouseMotionHandler());  

}

//返回组件的首选大小
public Dimension getPreferredSize()
{
return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}

//描述应该如何绘制自己的组件
public void paintComponent(Graphics g)
{
var g2 = (Graphics2D) g;

  // 遍历正方形集合,并将每个正方形绘制出来  
  for (Rectangle2D r : squares)  
     g2.draw(r);  

}

/**
* 查找包含一个点的第一个正方形
* @param p a point
* @return the first square that contains p
*/
//传入的是事件源组件左上角的坐标
public Rectangle2D find(Point2D p)
{
for (Rectangle2D r : squares)
{
//如何在正方形集合中找到某个正方形的左上角坐标与此坐标相同,则将此正方形对象返回
if (r.contains(p)) return r;
}
//否则返回空
return null;
}

/**
* 增加一个正方形到集合中
* @param p the center of the square
*/
//传入的是事件源组件左上角的坐标
public void add(Point2D p)
{
//获取它的横纵坐标
double x = p.getX();
double y = p.getY();

  //创建一个正方形对象  
  current = new Rectangle2D.Double(x - SIDELENGTH / , y - SIDELENGTH / ,  
     SIDELENGTH, SIDELENGTH);

  //将正方形对象添加到集合中  
  squares.add(current);

  //repaint方法的作用是“尽可能快地”重新绘制组件  
  repaint();  

}

/**
* 从集合中删除一个正方形
* @param s the square to remove
*/
//传入的是一个正方形对象
public void remove(Rectangle2D s)
{
//如果此正方形对象不存则停止删除
if (s == null) return;

  //如果此正方形对象与正在绘制的正方形相同则将current赋为空  
  if (s == current) current = null;

  //否则将执行递归删除  
  squares.remove(s);  
  repaint();  

}

private class MouseHandler extends MouseAdapter
{
//在光标的位置添加正方形
public void mousePressed(MouseEvent event)
{
// 如果光标所在位置在正方形集合中不包含,则添加新的正方形
current = find(event.getPoint());
if (current == null) add(event.getPoint());
}

  // 如果双击,则删除当前正方形  
  public void mouseClicked(MouseEvent event)  
  {  
      //如果光标所在位置在正方形集合中包含并且鼠标点击次数>=2,则删除此位置的正方形  
     current = find(event.getPoint());  
     if (current != null && event.getClickCount() >= ) remove(current);  
  }  

}

private class MouseMotionHandler implements MouseMotionListener
{
public void mouseMoved(MouseEvent event)
{

    // 如果鼠标光标不在正方形内,则鼠标形状为默认值(箭头)  
     if (find(event.getPoint()) == null) setCursor(Cursor.getDefaultCursor());  
    // 如果鼠标光标位于正方形内,请将其设置为十字光标  
     else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR\_CURSOR));  
  }

  public void mouseDragged(MouseEvent event)  
  {  
     if (current != null)  
     {  
        int x = event.getX();  
        int y = event.getY();

        // 拖动当前矩形使其居中(x,y)  
        current.setFrame(x - SIDELENGTH / , y - SIDELENGTH / , SIDELENGTH, SIDELENGTH);  
        repaint();  
     }  
  }  

}
}

MouseFrame类:

package mouse;

import javax.swing.*;

/**
* 包含用于测试鼠标操作的面板的框架
*/
public class MouseFrame extends JFrame
{
public MouseFrame()
{
add(new MouseComponent());
pack();
}
}

MouseTest类:

package mouse;

import java.awt.*;
import javax.swing.*;

/**
* @version 1.35 2018-04-10
* @author Cay Horstmann
*/
public class MouseTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(() -> {
var frame = new MouseFrame();
frame.setTitle("MouseTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}

运行结果如图:

实验总结:

本章我们学习了图形界面事件处理技术的知识,基本内容可以掌握,但目前我还是只能实现课本的例题,不具备编程能力。最近结项后会多花些时间来学习,争取赶上大家的进度。