• 201871010128-杨丽霞《面向对象程序设计(java)》第十三周学习总结


    201871010128-杨丽霞《面向对象程序设计(java)》第十三周学习总结

    项目

    内容

    这个作业属于哪个课程

     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程序中鼠标事件处理技术。

     

    随笔博文正文内容包括:

    第一部分:总结第十一章理论知识(35分)

    第十一章 事件处理

    1.事件:当用户在界面上执行一个操作,例如按下键盘、拖动或者单击鼠标时,都将产生一个事件。Java中事件是用来描述不同类型用户操作的对象,Java中有很多不同类型的事件。例如:单击,双击,右击,拖动,键盘的按下、释放,文本域内容改变等。

    事件源:产生事件的组件就是一个事件源。例如,当在一个Button上单击鼠标是,将产生一个ActionEvent类型的事件,而这个Button就是事件源

    事件监听器:就是调用事件处理方法的对象。当界面操作事件产生并被发送到产生事件的组件时,该组件将把事件发送给能接受和处理该事件的监听器。

    2.1.事件编程步骤:

    ①编写事件处理类(事件监听者)

    ②根据需求给事件处理类实现监听接口

    ③在事件处理类中重写(实现),其事件处理函数

    ④在事件源类中指定该事件的监听器(响应者)是谁,即注册监听

    3.事件源与监听器的关系:

     

    4.为简化编程,JDK针对大多数事件监听器接口定义了相应的实现类——事件适配器类,在适配器中,实现了相应监听器接口中的所有的方法,但不做任何事情。

    所以定义的监听器类可以继承事件适配器类,并只重写所需要的方法。

    有如下适配器:

     - ComponentAdapter (组件适配器)

     - ContainerAdapter (容器适配器)

     - FocusAdapter (焦点适配器)

     - KeyAdapter (键盘适配器)

     - MouseAdapter (鼠标适配器)

     - MouseMotionAdapter (鼠标运动适配器)

     - WindowAdapter (窗口适配器)

    1.Java 用匿名类、lambda表达式简化程序

    1) 使用字符串构造按钮对象

    2) 把按钮添加到面板上

    3) 用对应的颜色构造一个动作监听器

    4) 注册动作监听器

    6.Java应用swing改变观感

    Java默认的是Metal观感,可以通过设置属性或者动态来改变观感。本文主要针对方法一中的文件位置问题。

    设置swing.properties

    本方法主要是修正针对jdk6.0,网上的说法都是在JAVA_HOME/jre/lib里有这个文件,但是jdk6.0是没有这个文件的。其实正确的位置应该是在你的安装Java的目录下的jre,按照jdk6.0的,应该是在安装时第二次选路径的那个jre6下的lib文件夹。举例来说,我的jdk目录是:D:/Java/JDK6.0,而我们要找的这个不在D:/Java/JDK6.0/jre/lib,而是在D:/Java/jre6。

     而没有的话要自己建立一个swing.properties文件。

    内容可以写上自己想要的默认观感,例如:swing.defaultlaf = com.sun.java.swing.plaf.windows.WindowsLookAndFeel

    而其实可以如下通过添加或者去除“#”来静态选择观感。

    #swing.defaultlaf = javax.swing.plaf.metal.MetalLookAndFeel
    #swing.defaultlaf = com.sun.java.swing.plaf.motif.MotifLookAndFeel
    swing.defaultlaf = com.sun.java.swing.plaf.windows.WindowsLookAndFeel

    7. Swing包提供了一种非常实用的机制来封装命令,并将它们连接到多个事件源,这就是Action接口。一个动作是一个封装下列内容的对象:

      × 命令的说明(一个文本字符串和一个可选图标);

      × 执行命令所需要的参数(例如,在列举的例子中请求改变的颜色)。

     Action接口包含下列内容:

    1.public void actionPerformed(ActionEvent e);  
    2.public void setEnabled(boolean b);  
    3.public boolean isEnabled();  
    4.public void putValue(String key, Object value);  
    5.public Object getValue(String key);  
    6.public void addPropertyChangeListener(PropertyChangeListener listener);  
    public void removePropertyChangeListener(PropertyChangeListener listener);

    actionPerformed方法是ActionListener接口中的一个:实际上,Action接口扩展于ActionListener接口,因此,可以在任何需要ActionListener对象的地方使用Action对象。

    8.

    1.AWT事件类的继承关系图:

     

    2.Java事件处理采用的是面向对象的方法

    有些swing组件会生成其他类型事件的对象,它们都直接扩展与EventObject,而不是AWTEvent

    事件对象封装了事件源于监听器彼此通信的事件信息。在必要的时候,可以对传递给监听器对象的事件进行分析。

    在按钮例子中,是借助getSourse()和getActionCommand()方法实现对象分析的。

    AWT(Abstract Window Toolkit),中文译为抽象窗口工具包

     

    3.AWT将事件分为底层事件和语义事件。语义事件是表示用户动作的时间。

    ActionEvent是一个语义事件。底层事件时形成那些事件的事件

    调节滚动条是一个语义事件,但是拖动鼠标时一个底层事件

    9.鼠标事件:MouseEvent.

    鼠标监听器接口:MouseListener,MouseMotionListener

    鼠标监听器适配器:MouseAdapter,MouseMotionAdapter

    用户点击鼠标按钮时,会调用三个监听器方法:
    a.鼠标第一次被按下时调用mousePressed方法;
    b.鼠标被释放时调用mouseReleased方法;
    c.两个动作完成之后,调用mouseClicked方法。

    鼠标在组件上移动时,会调用mouseMoved方法。

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

    监听鼠标点击事件,实现MouseListener接口.

     

    第二部分:实验部分

    实验1:测试程序1(5分)

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

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

    lambda表达式简化程序;

    掌握JButton组件的基本API;

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

    package button;
    
    import java.awt.*;
    import javax.swing.*;
    
    /**
     * @version 1.35 2018-04-10
     * @author Cay Horstmann
     */
    public class ButtonTest
    {
       public static void main(String[] args)
       {
          EventQueue.invokeLater(() -> // lambda表达式
          {
             var frame = new ButtonFrame();
             frame.setTitle("ButtonTest");//标题
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             frame.setVisible(true);//可见
          });
       }
    }
    package button;
    
    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 = 300;
       private static final int DEFAULT_HEIGHT = 200;
    
       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);
    
          // 按钮将要执行的操作
          ColorAction yellowAction = new ColorAction(Color.YELLOW);
          ColorAction blueAction = new ColorAction(Color.BLUE);
           ColorAction redAction  = new ColorAction(Color.RED);
    
          // associate actions with buttons
          yellowButton.addActionListener(yellowAction);
          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);
          }
       }
    }
     

    运行截图:

     实验1:测试程序2(5分)

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

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

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

     11-2代码:

    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();
       }
    
       /**
        * 创建一个按钮来更改可插入的外观.
        * @param name the button name
        * @param className the name of the look-and-feel class
        */
       private void makeButton(String name, String className)
       {
          //添加按钮到面板
    
          JButton button = new JButton(name);
          buttonPanel.add(button);
    
          //设置按钮要进行的操作
    
          button.addActionListener(event -> {
             // 按钮操作结果: 切换到新的外观
             try //可能出错的代码放入try子句中
             {
                UIManager.setLookAndFeel(className);
                SwingUtilities.updateComponentTreeUI(this);
                pack();
             }
             catch (Exception e)
             {
                e.printStackTrace();
             }
          });
       }
    }
    1
    2 
     3 import java.awt.*;
     4 import javax.swing.*;
     5 
     6 /**
     7  * @version 1.32 2015-06-12
     8  * @author Cay Horstmann
     9  */
    10 public class PlafTest
    11 {
    12    public static void main(String[] args)
    13    {
    14       EventQueue.invokeLater(() -> {
    15          JFrame frame = new PlafFrame();
    16          frame.setTitle("PlafTest");
    17          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    18          frame.setVisible(true);
    19       });
    20    }
    21 }

    运行截图:

    实验1:测试程序3(5分)

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

    掌握AbstractAction类及其动作对象;

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

    11-3程序:

     1 package Damo;
     2 
     3 import java.awt.*;
     4 import javax.swing.*;
     5 
     6 /**
     7  * @version 1.34 2015-06-12
     8  * @author Cay Horstmann
     9  */
    10 public class ActionTest
    11 {
    12    public static void main(String[] args)
    13    {
    14       EventQueue.invokeLater(() -> {
    15          var frame = new ActionFrame();
    16          frame.setTitle("ActionTest");
    17          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    18          frame.setVisible(true);
    19       });
    20    }
    21 }
     1 package Damo;
     2 
     3 import java.awt.*;
     4 import java.awt.event.*;
     5 import javax.swing.*;
     6 
     7 /**
     8  * A frame with a panel that demonstrates color change actions.
     9  */
    10 public class ActionFrame extends JFrame
    11 {
    12    private JPanel buttonPanel;
    13    private static final int DEFAULT_WIDTH = 300;
    14    private static final int DEFAULT_HEIGHT = 200;
    15 
    16    public ActionFrame()
    17    {
    18       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
    19 
    20       buttonPanel = new JPanel();
    21 
    22       // 定义操作
    23       var yellowAction = new ColorAction("Yellow", new ImageIcon("C:\Users\83583\Desktop\java\corejava\v1ch11\yellow-ball.gif"),
    24             Color.YELLOW);
    25       var blueAction = new ColorAction("Blue", new ImageIcon("C:\Users\83583\Desktop\java\corejava\v1ch11\blue-ball.gif"), Color.BLUE);
    26       var redAction = new ColorAction("Red", new ImageIcon("C:\Users\83583\Desktop\java\corejava\v1ch11\red-ball.gif"), Color.RED);
    27 
    28       // 为这些操作添加按钮
    29       buttonPanel.add(new JButton(yellowAction));//注册
    30       buttonPanel.add(new JButton(blueAction));
    31       buttonPanel.add(new JButton(redAction));
    32 
    33       // 将 panel 添加置 frame
    34       add(buttonPanel);
    35 
    36       // 将Y、B和R键与名称关联
    37       InputMap inputMap = buttonPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    38       inputMap.put(KeyStroke.getKeyStroke("ctrl Y"), "panel.yellow");
    39       inputMap.put(KeyStroke.getKeyStroke("ctrl B"), "panel.blue");
    40       inputMap.put(KeyStroke.getKeyStroke("ctrl R"), "panel.red");
    41 
    42       // 将名称与操作关联
    43       ActionMap actionMap = buttonPanel.getActionMap();
    44       actionMap.put("panel.yellow", yellowAction);
    45       actionMap.put("panel.blue", blueAction);
    46       actionMap.put("panel.red", redAction);
    47    }
    48    
    49    public class ColorAction extends AbstractAction
    50    {
    51       /**
    52        * Constructs a color action.
    53        * @param name the name to show on the button
    54        * @param icon the icon to display on the button
    55        * @param c the background color
    56        */
    57       public ColorAction(String name, Icon icon, Color c)
    58       {
    59          putValue(Action.NAME, name);
    60          putValue(Action.SMALL_ICON, icon);
    61          putValue(Action.SHORT_DESCRIPTION, "Set panel color to " + name.toLowerCase());
    62          putValue("color", c);
    63       }
    64 
    65       public void actionPerformed(ActionEvent event)
    66       {
    67          var color = (Color) getValue("color");
    68          buttonPanel.setBackground(color);
    69       }
    70    }
    71 }

    运行截图:

     

     实验1:测试程序1(5分)

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

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

    源代码:

    1 package Damo;
     2 
     3 import java.awt.*;
     4 import javax.swing.*;
     5 
     6 /**
     7  * @version 1.35 2018-04-10
     8  * @author Cay Horstmann
     9  */
    10 public class MouseTest
    11 {
    12    public static void main(String[] args)
    13    {
    14       EventQueue.invokeLater(() -> {
    15          var frame = new MouseFrame();
    16          frame.setTitle("MouseTest");
    17          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    18          frame.setVisible(true);
    19       });
    20    }
    21 }
     1 package Damo;
     2 
     3 import javax.swing.*;
     4 
     5 /**
     6  * A frame containing a panel for testing mouse operations
     7  */
     8 public class MouseFrame extends JFrame
     9 {
    10    public MouseFrame()
    11    {
    12       add(new MouseComponent());
    13       pack();
    14    }
    15 }
     
    
      1 package Damo;
      2 
      3 import java.awt.*;
      4 import java.awt.event.*;
      5 import java.awt.geom.*;
      6 import java.util.*;
      7 import javax.swing.*;
      8 
      9 /**
     10  * A component with mouse operations for adding and removing squares.
     11  */
     12 public class MouseComponent extends JComponent
     13 {
     14    private static final int DEFAULT_WIDTH = 300;
     15    private static final int DEFAULT_HEIGHT = 200;
     16 
     17    private static final int SIDELENGTH = 10;
     18    private ArrayList<Rectangle2D> squares;
     19    private Rectangle2D current; // 包含鼠标光标的正方形
     20 
     21    public MouseComponent()
     22    {
     23       squares = new ArrayList<>();
     24       current = null;
     25 
     26       addMouseListener(new MouseHandler());
     27       addMouseMotionListener(new MouseMotionHandler());
     28    }
     29 
     30    public Dimension getPreferredSize() 
     31    {
     32       return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
     33    }   
     34    
     35    public void paintComponent(Graphics g)
     36    {
     37       var g2 = (Graphics2D) g;
     38 
     39       // 绘制所有正方形
     40       for (Rectangle2D r : squares)
     41          g2.draw(r);
     42    }
     43 
     44    /**
     45     * Finds the first square containing a point.
     46     * @param p a point
     47     * @return the first square that contains p
     48     */
     49    public Rectangle2D find(Point2D p)
     50    {
     51       for (Rectangle2D r : squares)
     52       {
     53          if (r.contains(p)) return r;
     54       }
     55       return null;
     56    }
     57 
     58    /**
     59     * Adds a square to the collection.
     60     * @param p the center of the square
     61     */
     62    public void add(Point2D p)
     63    {
     64       double x = p.getX();
     65       double y = p.getY();
     66 
     67       current = new Rectangle2D.Double(x - SIDELENGTH / 2, y - SIDELENGTH / 2,
     68          SIDELENGTH, SIDELENGTH);
     69       squares.add(current);
     70       repaint();
     71    }
     72 
     73    /**
     74     * Removes a square from the collection.
     75     * @param s the square to remove
     76     */
     77    public void remove(Rectangle2D s)
     78    {
     79       if (s == null) return;
     80       if (s == current) current = null;
     81       squares.remove(s);
     82       repaint();
     83    }
     84 
     85    private class MouseHandler extends MouseAdapter
     86    {
     87       public void mousePressed(MouseEvent event)
     88       {
     89          // 如果光标不在正方形内,则添加新的正方形
     90          current = find(event.getPoint());
     91          if (current == null) add(event.getPoint());
     92       }
     93 
     94       public void mouseClicked(MouseEvent event)
     95       {
     96          // 如果双击,则删除当前正方形
     97          current = find(event.getPoint());
     98          if (current != null && event.getClickCount() >= 2) remove(current);
     99       }
    100    }
    101 
    102    private class MouseMotionHandler implements MouseMotionListener
    103    {
    104       public void mouseMoved(MouseEvent event)
    105       {
    106          // 如果鼠标光标位于矩形内,请将其设置为十字光标
    107 
    108          if (find(event.getPoint()) == null) setCursor(Cursor.getDefaultCursor());
    109          else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
    110       }
    111 
    112       public void mouseDragged(MouseEvent event)
    113       {
    114          if (current != null)
    115          {
    116             int x = event.getX();
    117             int y = event.getY();
    118 
    119             // 拖动当前矩形使其居中(x,y)
    120             current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH, SIDELENGTH);
    121             repaint();
    122          }
    123       }
    124    }   
    125 
    126}

    运行截图:

     

     实验2:结对编程练习包含以下4部分:(20分)

    1)   程序设计思路简述;

    2)   符合编程规范的程序代码;

    3)   程序运行功能界面截图;

    4)   结对过程描述,提供两人在讨论、细化和编程时的结对照片(非摆拍)。

    利用班级名单文件、文本框和按钮组件,设计一个有如下界面(图1)的点名器,要求用户点击开始按钮后在文本输入框随机显示2018级计算机科学与技术(1)班同学姓名,如图2所示,点击停止按钮后,文本输入框不再变换同学姓名,此同学则是被点到的同学姓名,如图3所示。

    代码如下:

    import java.awt.EventQueue;
    
    import javax.management.Query;
    import javax.swing.JFrame;
    
    public class Main {
        public static void main(String[] args) {
            EventQueue.invokeLater(()->{
                ButtonFrame buttonFrame = new ButtonFrame();
                buttonFrame.setVisible(true);
                buttonFrame.setTitle("点名器");
                buttonFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                
            });
        }
    }
    
    

    import java.awt.event.ActionListener;
    import java.io.BufferedInputStream;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.StringBufferInputStream;
    import java.util.ArrayList;
    import java.util.Timer;
    import java.util.TimerTask;
    
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    
    
    public class ButtonFrame extends JFrame {
        private JPanel buttonPanel;
        private static final int DEFAULT_WIDTH = 300 * 2;
        private static final int DEFAULT_HEIGHT = 200 * 2;
        private JButton jButton;
        private JLabel jLabel;
        private ArrayList<String> arrayList;
    
    
        public ButtonFrame()//构造器
        {
            setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
            buttonPanel = new JPanel();
            buttonPanel.setLayout(null);
            add(buttonPanel);
            jLabel = new JLabel("点名器");
            jButton = new JButton("开始");
            jButton.setBackground(Color.gray);//设置背景颜色
            jLabel.setBounds(100, 50, 60, 30);
            jButton.setBounds(100, 120, 60, 30);
            arrayList = new ArrayList<>();
            //读文件
            File file= new File("D:/studentnamelist.txt");
            FileInputStream fis;
            try //可能出错的程序放入try子句中
            {
                fis = new FileInputStream(file);
                InputStreamReader in = new InputStreamReader(fis);
                BufferedReader buf = new BufferedReader(in);
                String readLine;
                while ((readLine = buf.readLine())!=null) {
                    arrayList.add(readLine);
                   
                }
            } catch (FileNotFoundException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
           
            jButton.addActionListener(new ActionListener() {
                Timer timer;
    
    
                public void actionPerformed(ActionEvent e) {
                    if (jButton.getText().equals("开始")) {
                        timer = new Timer();;
                        TimerTask timerTask = new TimerTask() {
                            public void run() {
                                jButton.setText("停止");
                                jButton.setBackground(Color.red);
                                jLabel.setText(arrayList.get((int) (Math.random() * 43)));
                            }
    
    
                        };
                        timer.schedule(timerTask, 0, 10);
                    }
                    if (jButton.getText().equals("停止")) {
                        timer.cancel();
                        jButton.setText("开始");
                        jButton.setBackground(Color.gray);
                    }
                }
            });
            buttonPanel.add(jLabel);
            buttonPanel.add(jButton);
            add(buttonPanel);
    
    
        }
    }
    
    

    运行截图:

     

    结队编程照片:

    实验总结:(20分)

    这周学习了第十一章事件处理,了解了事件源,事件监听器,事件对象,鼠标事件等的理论知识,可通过编程设置参数对图形用户界面进行控制,实验课学长的演示,了解了GUI界面组件观感设置方法,GUI程序中鼠标事件处理技术,用lambda表达式以及匿名类简化程序。随机点名器的结对编程,让我体会到结对编程的好处,两个人同时去这同一个程序,可以通过讨论,相互提出问题,解决问题,可达到事半功倍的效果。也是对编程能力的一种提升。这次结对编程内容同时也是对这次新知识的一种应用,实现了对新知识的巩固,利用。以后要多通过自己实践,去应用理论知识。
  • 相关阅读:
    swoole学习(四):websocket
    LeetCode--SQL 查询:体育馆的人流量
    LeetCode--SQL 查询:有趣的电影
    centos7下mysql5.7忘记密码
    LeetCode--SQL 查询:删除重复的电子邮箱。
    swoole学习(三):HTTP服务
    swoole学习(二):TCP/UDP服务器-客户端
    swoole学习(一):初识swoole
    LeetCode--SQL 查询:查找部门工资前三高的职员
    报文、报文段、分组、包、数据报、帧、数据流的概念区别
  • 原文地址:https://www.cnblogs.com/ylxzjh/p/11923105.html
Copyright © 2020-2023  润新知