Java Swing构建实时时钟:初学者项目指导

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Java Swing是一个用于构建桌面应用程序的GUI工具包,本项目旨在通过创建一个实时更新的时钟应用来指导初学者。它将涉及使用JFrame创建窗口、使用JLabel或JTextArea显示时间、以及利用Timer和TimerTask实现时间的实时更新。学习者将了解Swing组件、布局管理、事件处理和定时器使用等基础知识,从而加深对Java Swing API的理解。项目源代码位于src目录,并可在支持Swing的IDE中运行。
java swing实时时钟

1. Java Swing实时时钟的设计与实现

在现代信息社会中,实时时钟是一个非常实用的应用程序,可以集成在多种平台和设备上。Java Swing提供了一套丰富的图形用户界面(GUI)组件库,使得开发者能够快速创建跨平台的桌面应用程序。在本章中,我们将探讨如何使用Java Swing构建一个实时时钟应用程序,介绍其设计思路和实现细节。

1.1 设计思路

设计一个实时时钟应用需要考虑其核心功能:实时时间的获取、显示以及用户交互。我们将使用Swing组件来构建用户界面,包括使用JLabel显示时间,并且通过Timer组件定期更新时间显示,确保时间信息是实时更新的。

1.2 实现步骤概览

  1. 创建JFrame窗口 :设置窗口标题,大小等属性,作为我们的主界面。
  2. 添加时间显示组件 :使用JLabel组件来显示时间,并设置其字体和大小以便阅读。
  3. 使用定时器更新时间 :通过实现 ActionListener 接口来监听Timer事件,并在每次事件发生时更新JLabel的时间显示。
  4. 测试和调试 :运行程序,检查时钟的显示是否准确,以及用户界面是否美观。

我们将在接下来的章节中深入探讨Java Swing的基础知识、具体实现细节以及优化技巧。通过实践本章内容,读者将能够掌握使用Java Swing开发复杂GUI应用程序的基本技能。

2. Java Swing基础知识介绍

2.1 Java Swing的组件与架构

2.1.1 Swing组件概述

Swing是Java的一个图形用户界面工具包,提供了丰富的GUI组件,使得开发者能够快速构建跨平台的应用程序。Swing采用MVC(Model-View-Controller)的设计模式,它允许开发人员将应用的界面与数据处理逻辑分离。

Swing组件大致可以分为三类:

  • 顶层容器 :如JFrame,JDialog,JWindow等,负责提供一个窗口供其他组件排列。
  • 中间层容器 :如JPanel,JScrollpane等,负责容器的布局管理,提供滚动功能等。
  • 具体组件 :如JButton,JTextField,JLabel等,负责界面的显示和用户交互。

Swing组件的一个重要特点就是“轻量级组件”,它们不是直接绘制界面,而是通过与底层的本地组件交互来绘制。这使得Swing组件能够具有更一致的外观和行为,无论是在哪个平台。

2.1.2 Swing与AWT的关系与区别

Swing和AWT(Abstract Window Toolkit)都是Java用于图形用户界面设计的API。AWT是Java最初的GUI工具包,它依赖于本地组件,因此界面风格会受到操作系统的影响,这称为“外观依赖”。

Swing相对于AWT来说,提供了更多丰富的界面组件,并且引入了“外观独立”的概念。在Swing中,组件的外观可以被自定义,使得开发者能够根据自己的需求来设计界面,从而在不同的操作系统上都保持一致的外观和行为。

2.2 Java事件处理机制

2.2.1 事件驱动编程基础

事件驱动编程是一种常见的编程范式,在这种范式下,程序的流程由事件来驱动。在Java中,事件通常是用户与界面交互的结果,如按钮点击、窗口关闭等。

事件处理机制涉及几个核心部分:事件源(Event Source)、事件(Event)、事件监听器(EventListener)和事件适配器(Event Adapter)。当一个事件被触发,它会被封装成一个事件对象,并被发送到事件监听器。事件监听器定义了如何响应这个事件,而事件适配器则提供了一个默认的空实现,便于开发者只覆盖感兴趣的方法。

2.2.2 事件监听器和适配器的使用

在Swing中,可以通过实现特定的事件监听器接口来处理事件,也可以使用事件适配器类来简化事件处理代码。例如,以下是一个简单的按钮点击事件处理例子:

// 定义按钮点击事件处理
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button was clicked!");
    }
});

// 使用适配器简化代码
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // 空实现
    }
});
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button was clicked!");
    }
});

在上面的代码中,我们创建了一个 ActionListener 对象,覆盖了 actionPerformed 方法。当按钮被点击时,这个方法就会被调用。

Swing事件处理机制的实现细节,以及如何有效地使用监听器和适配器,是创建交互式GUI应用程序的关键。以下是一个表格,总结了Swing中常用的一些事件监听器:

事件监听器接口 处理的事件类型
ActionListener 组件被点击事件(如按钮点击)
KeyListener 键盘按键事件
MouseListener 鼠标点击、按下、释放、进入和离开事件
FocusListener 组件获得和失去焦点事件
WindowListener 窗口的打开、关闭、激活等事件

学习和掌握Java Swing的组件与事件处理机制是进行更深入GUI开发的基础。理解了这些基础知识后,我们才能够进一步学习如何使用JFrame进行GUI设计,以及如何应用文本组件来显示时钟信息。在后续章节中,我们将深入了解Swing的这些组件及其在时钟应用程序中的具体应用。

3. JFrame在GUI设计中的应用

3.1 JFrame的基本使用和特性

JFrame是Java Swing库中用于创建顶级窗口的主要组件。它提供了一个可以放置各种Swing组件的容器,并拥有标题栏、边框以及最小化、最大化和关闭按钮等窗口元素。在本章节中,我们将深入了解JFrame的基本使用和其提供的特性。

3.1.1 JFrame的创建与初始化

要创建一个基本的JFrame窗口,首先需要导入Swing相关的类库,并实例化一个JFrame对象。创建时可以指定窗口的标题。

import javax.swing.JFrame;

public class SimpleFrame extends JFrame {
    public SimpleFrame() {
        super("Simple JFrame Example");
        setSize(300, 200); // 设置窗口大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置默认关闭操作
        setLocationRelativeTo(null); // 设置窗口居中
    }

    public static void main(String[] args) {
        SimpleFrame frame = new SimpleFrame();
        frame.setVisible(true); // 显示窗口
    }
}

在上述代码中, super("Simple JFrame Example") 设置了窗口的标题, setSize 方法定义了窗口的宽度和高度, setDefaultCloseOperation 告诉JVM当用户尝试关闭窗口时应该如何处理,而 setLocationRelativeTo 使得窗口能够居中显示在屏幕中。

3.1.2 JFrame的常用属性和方法

JFrame拥有很多属性和方法,允许开发者对窗口进行详细控制。下面列举了部分最常用到的属性和方法:

  • getTitle() , setTitle(String title) : 获取或设置窗口标题。
  • packing() : 使所有组件自动调整到适合大小。
  • setLayout(LayoutManager manager) : 设置窗口的布局管理器。
  • setLocationRelativeTo(Component c) : 设置窗口相对于另一个组件的位置。
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 确保在关闭窗口时程序退出

3.2 JFrame的高级功能和定制

为了提升用户体验,JFrame提供了高级定制功能,允许开发者对窗口的菜单栏、工具栏进行个性化设置,以及改变窗口的装饰。

3.2.1 JFrame的菜单栏和工具栏定制

菜单栏通常位于窗口的顶部,提供了一种分组的方式将功能划分为多个菜单项。工具栏则提供了快速访问最常用功能的快捷方式。

import javax.swing.*;

public class FrameWithMenuAndToolbar extends JFrame {
    public FrameWithMenuAndToolbar() {
        // 设置窗口标题
        setTitle("Frame with Menu and Toolbar Example");
        setSize(400, 300);

        // 创建菜单栏
        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("File");
        JMenuItem exitItem = new JMenuItem("Exit");
        exitItem.addActionListener(e -> System.exit(0));
        fileMenu.add(exitItem);
        menuBar.add(fileMenu);
        // 设置菜单栏并添加到窗口
        setJMenuBar(menuBar);

        // 创建工具栏
        JToolBar toolBar = new JToolBar();
        JButton button = new JButton("Click Me");
        toolBar.add(button);
        add(toolBar, BorderLayout.NORTH);
    }

    public static void main(String[] args) {
        FrameWithMenuAndToolbar frame = new FrameWithMenuAndToolbar();
        frame.setVisible(true);
    }
}

在这个例子中,我们创建了一个简单的菜单栏和工具栏,并为它们添加了组件。

3.2.2 JFrame的窗口装饰和定制

除了菜单和工具栏外,JFrame还允许开发者自定义窗口的外观,包括是否显示标题栏和边框等。JFrame提供了 setUndecorated 方法来实现无装饰窗口。

frame.setUndecorated(true); // 创建一个无标题栏和边框的窗口

通过这种方式,可以创建完全自定义样式的窗口,为应用提供独特的外观和用户体验。

JFrame是Swing GUI设计中不可或缺的一个组件,通过上述章节的学习,我们可以了解到它的基本使用方法、常用属性以及如何进行高级定制,使得创建的窗口能够满足多样化的用户需求。

4. 文本组件在时钟显示中的应用

文本组件在Java Swing GUI设计中扮演着重要的角色,它们能够展示用户友好的文本信息,如标签、按钮的提示信息以及文本域输入等。本章节将聚焦于JLabel和JTextArea组件在时钟显示功能中的具体应用,以及如何通过文本组件的样式和布局管理来改善用户界面。

4.1 JLabel和JTextArea用于显示文本信息

JLabel和JTextArea是Swing库中用于文本显示的两个常用组件。它们具备不同的用途和特性,为不同的显示需求提供解决方案。接下来将详细介绍它们在时钟显示中的应用。

4.1.1 JLabel的文本显示

JLabel是Swing中用于显示简单文本或图标的一个轻量级组件。它通常用于显示不可编辑的文本或图片。在时钟应用中,JLabel非常适合用来显示时间。

JLabel clockLabel = new JLabel("00:00:00");
clockLabel.setFont(new Font("Arial", Font.BOLD, 48));

在上面的代码中,我们创建了一个JLabel实例并设置了它的初始文本为”00:00:00”,同时指定了字体样式和大小。这样的设置使得JLabel非常适合用来展示当前的时间。

代码逻辑解读:

  • 首先,实例化一个 JLabel 对象,并传入初始文本。
  • 使用 setFont 方法设置了字体样式,包括字体类型(Arial)、字体样式(粗体)和字体大小(48)。
  • 这样,JLabel组件就具备了展示时间的基本条件,可以在GUI中显示出来。

4.1.2 JTextArea的多行文本处理

JTextArea组件可以用来处理包含多行文本的情况,非常适合于需要输入或者显示多行文本信息的应用。在时钟应用中,它可以用来展示更新的时间日志或详细的时间信息。

JTextArea timeLog = new JTextArea(5, 20);
timeLog.setEditable(false);

在上面的代码中,我们创建了一个 JTextArea 实例,它能够展示五行文本,每行最多二十个字符。通过设置 setEditable(false) ,使得用户无法修改这部分文本区域的内容。

代码逻辑解读:

  • JTextArea 实例化时接收两个参数,第一个参数指定文本区可以拥有的行数,第二个参数指定每行的最大字符数。
  • 调用 setEditable(false) 方法使得文本区域不可编辑,保证文本区域内容的稳定性。

4.2 文本组件的样式和布局

在Swing中,文本组件的样式和布局管理非常关键,它影响着用户界面的可读性和美观性。为了使得时钟应用的文本组件更加吸引人且易于阅读,下面将介绍字体样式和颜色设置以及文本组件的布局管理方法。

4.2.1 字体样式和颜色设置

在时钟应用中,我们希望时间显示的字体样式和颜色能够清晰地展示给用户,并且符合应用的整体风格。

clockLabel.setForeground(Color.BLUE);
clockLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 50));

在这段代码中,我们通过 setForeground(Color.BLUE) 方法设置了JLabel文本的颜色为蓝色,并通过 setFont 方法设置了字体样式为斜体加粗的Serif字体,字体大小为50。

代码逻辑解读:

  • 调用 setForeground 方法可以改变文本的颜色,使用 Color.BLUE 常量定义了蓝色。
  • setFont 方法用于改变字体样式,其中 Font.BOLD | Font.ITALIC 结合了粗体和斜体属性。

4.2.2 文本组件的布局管理

文本组件的布局管理是指如何将组件在容器中排列,使得界面布局合理、美观。对于时钟应用,合理布局能够使时间显示更突出,更符合用户习惯。

JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(clockLabel);
panel.add(timeLog);

在这段代码中,我们创建了一个 JPanel 对象并设置了布局管理器为 BoxLayout ,这个布局管理器使得组件按垂直方向(Y轴)排列。之后,我们将JLabel和JTextArea组件添加到这个面板中。

代码逻辑解读:

  • 创建一个 JPanel 对象 panel
  • 通过 setLayout 方法设置了布局管理器, BoxLayout.Y_AXIS 表示按垂直方向排列组件。
  • 通过 add 方法,将 clockLabel timeLog 组件添加到 panel 面板中。
  • 这样的布局设置可以使时间显示组件在界面上垂直居中显示,且日志记录组件位于下方,方便查看。

接下来的章节将进一步探讨定时器与时间更新机制,以及如何在Swing中实现动态更新时间的功能。

5. 定时器与时间更新机制

在Java Swing实时时钟的设计与实现中,定时器(Timer)是一个核心组件,用于实现时间的实时更新。本章节将深入探讨如何使用 Timer TimerTask 类来实现这一功能,并分析时间逻辑处理的具体实现。

5.1 Timer和TimerTask实现时间更新

5.1.1 Timer类的基本使用

java.util.Timer 类是Java提供的一个定时执行任务的工具。它可以安排一个任务在未来的某个时间点执行,或者以固定频率重复执行。在Swing应用程序中, Timer 通常与 SwingWorker 结合使用,以避免阻塞事件分发线程(EDT)。

以下是使用 Timer 的基本步骤:

  1. 创建一个 Timer 实例。
  2. 创建一个 TimerTask 实例,该实例定义了要周期性执行的任务。
  3. 调用 Timer schedule(TimerTask task, long delay, long period) 方法来安排任务执行。

示例代码如下:

import java.util.Timer;
import java.util.TimerTask;

public class TimeUpdater extends TimerTask {
    @Override
    public void run() {
        // 更新时钟显示的代码
    }
}

// 在某个组件中初始化定时器
Timer timer = new Timer();
timer.schedule(new TimeUpdater(), 0, 1000); // 每秒执行一次

在这个例子中, TimeUpdater 类继承自 TimerTask ,并重写了 run 方法。 schedule 方法的第一个参数是我们创建的 TimeUpdater 实例,第二个参数 delay 是首次执行任务的延迟时间(以毫秒为单位),第三个参数 period 是任务执行的周期(以毫秒为单位)。

5.1.2 TimerTask的创建和调度

TimerTask 是一个抽象类,它提供了执行定时任务的框架。我们通过创建 TimerTask 的匿名子类,并重写 run 方法,来定义任务执行的具体内容。 TimerTask 是线程安全的,可以被多个线程共享。

schedule 方法有多种重载形式,允许灵活地安排任务的执行时间。例如,我们可以使用以下方式在指定延迟后执行一次任务:

timer.schedule(new TimeUpdater(), 5000); // 5秒后执行一次

或者,我们可以使用 scheduleAtFixedRate scheduleWithFixedDelay 方法来安排固定频率或固定延迟的任务:

// 每隔5秒执行一次,首执行延迟为0
timer.scheduleAtFixedRate(new TimeUpdater(), 0, 5000);

// 每隔5秒执行一次,每次执行完毕后再等待5秒
timer.scheduleWithFixedDelay(new TimeUpdater(), 0, 5000);

5.2 实时时钟的时间逻辑处理

5.2.1 时间的获取和格式化

在实现实时时钟时,需要获取当前时间,并将其格式化为可读的字符串格式。Java的 java.time 包中的 LocalTime 类提供了处理时间和日期的现代方法。

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class TimeUtil {
    public static String getCurrentFormattedTime() {
        LocalTime now = LocalTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        return now.format(formatter);
    }
}

在上述代码中, LocalTime.now() 获取了当前的时间, DateTimeFormatter 定义了时间的显示格式, format 方法将当前时间按照指定格式进行了格式化。

5.2.2 时间更新的实现细节

时间更新的实现需要考虑多线程环境下的线程安全问题。 Swing 组件不是线程安全的,因此不能直接在定时器的任务中更新UI。为了更新时间,我们需要使用 SwingUtilities.invokeLater SwingUtilities.invokeAndWait 方法来确保在EDT中执行UI更新。

import javax.swing.SwingUtilities;

public void updateTimeOnSwingThread(final String formattedTime) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            // 更新Swing组件显示的时间
            timeDisplayLabel.setText(formattedTime);
        }
    });
}

在上述示例中, updateTimeOnSwingThread 方法接受一个格式化的时间字符串,然后使用 invokeLater 在EDT中安排更新操作,以保证线程安全。

此外,还需要考虑用户对时钟显示格式的需求,可能需要为不同地区提供不同格式的时间显示,这通常涉及到 Locale ResourceBundle 的使用,为应用程序提供本地化支持。

总结本章节,我们深入了解了Java中的 Timer TimerTask 类如何用于定时任务的调度,并通过实际代码示例和逻辑分析,掌握了时间更新在Swing应用程序中的安全实现方式。这些技术为实现实时时钟提供了坚实的基础,并能够应对更复杂的定时任务调度需求。

6. 综合实践与开发环境配置

开发任何项目,无论是应用还是工具,都需要良好的实践和优化,尤其是在编程领域,正确的开发环境配置以及项目实践能够大大提高开发效率和程序质量。在本章节中,我们将深入探讨如何创建一个简单的时钟应用—— Clock 类的实现,同时将涉及布局管理器的使用,以及如何配置Java开发环境和IDE。

6.1 Clock类的窗口创建与管理

在设计与实现 Clock 类时,我们首先要确保它能够创建窗口,并对其进行管理,包括添加事件监听器以及响应用户操作。

6.1.1 Clock类的设计与实现

Clock 类将是一个继承自 JFrame 的自定义类,其主要职责是创建一个窗口并在其中显示实时时间。以下是 Clock 类的基础实现:

import javax.swing.JFrame;
import javax.swing.Timer;

public class Clock extends JFrame {
    private JLabel timeLabel;
    public Clock() {
        // 初始化组件和布局管理器
        initUI();
        setTitle("Real Time Clock");
        setResizable(false);
        pack();
        // 设置窗口关闭行为
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }
    private void initUI() {
        timeLabel = new JLabel();
        timeLabel.setHorizontalAlignment(JLabel.CENTER);
        timeLabel.setFont(new Font("SansSerif", Font.BOLD, 48));
        // 添加时间更新的定时器
        Timer timer = new Timer(1000, e -> updateTime());
        timer.start();
        // 使用边框布局管理器
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(timeLabel, BorderLayout.CENTER);
    }
    private void updateTime() {
        timeLabel.setText(getCurrentTime());
    }
    private String getCurrentTime() {
        return java.time.LocalTime.now().toString();
    }
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(() -> {
            Clock clock = new Clock();
            clock.setVisible(true);
        });
    }
}

6.1.2 窗口的事件处理和响应

对于窗口类来说,事件处理是非常重要的一环。我们通过添加 Timer 来周期性地更新时间,并通过匿名内部类的形式处理时间更新事件。

6.2 布局管理器使用与实践

布局管理器允许我们在不关心组件具体大小和位置的情况下,动态地组织窗口中的组件。在本小节中,我们将实践 FlowLayout BorderLayout 两种布局管理器。

6.2.1 FlowLayout的布局实践

FlowLayout 是最简单的布局管理器之一,它按照组件加入的顺序,将它们水平排列,直到一行填满,然后继续在下一行排列。以下是如何在 Clock 类中使用 FlowLayout

// 使用FlowLayout
getContentPane().setLayout(new FlowLayout());
// 添加一个按钮到窗体中
JButton button = new JButton("Click me!");
getContentPane().add(button);

6.2.2 BorderLayout在时钟设计中的应用

BorderLayout 提供了一种更为复杂的布局,它允许你将组件放置在容器的五个区域:北部、南部、东部、西部和中间。下面是如何在 Clock 类中使用 BorderLayout

// 使用BorderLayout
getContentPane().setLayout(new BorderLayout());
// 将时间标签放置在中心
getContentPane().add(timeLabel, BorderLayout.CENTER);
// 添加一个按钮到南边
JButton button = new JButton("Settings");
getContentPane().add(button, BorderLayout.SOUTH);

6.3 Java开发环境和IDE配置要求

良好的开发环境配置对于开发Java程序来说至关重要。选择合适的集成开发环境(IDE)和正确配置Java开发工具包(JDK)是成功的第一步。

6.3.1 开发环境的搭建与配置

首先,我们需要安装JDK,它是运行Java程序所必需的。根据操作系统不同,安装步骤可能有所差异。接着,下载并安装IDE,如IntelliJ IDEA或Eclipse。在IDE中,我们需要配置项目以使用正确的JDK版本,并设置好项目结构,比如源代码文件夹和资源文件夹。

6.3.2 IDE选择及插件安装

选择IDE时,需要考虑个人习惯和项目需求。目前市面上流行的IDE有IntelliJ IDEA、Eclipse和NetBeans等。各种IDE都有自己的优势,比如IntelliJ IDEA以其智能代码辅助和重构而闻名,Eclipse则因其轻量级和高度可定制性而受到许多开发者的青睐。根据个人喜好和项目需求进行选择。

安装IDE之后,还可以根据需要安装一些插件,以增强开发体验。例如,Maven和Gradle插件帮助管理项目依赖,CheckStyle和PMD插件有助于代码质量管理,SonarLint插件提供实时代码质量检查等。

6.4 实践项目对编程技能的提升

进行实践项目是提高编程技能的一个重要方式。在本小节中,我们将讨论项目开发过程中可能遇到的学习与思考,以及编程技能的综合提升与应用。

6.4.1 项目开发过程中的学习与思考

在开发 Clock 项目时,你可能会遇到多种挑战,例如如何优雅地更新时间、如何管理布局以及如何处理用户的交互等。每解决一个问题,都是对编程知识的一次深化和对逻辑思维的一次锻炼。不断学习和尝试新的技术是提高编程技能的关键。

6.4.2 编程技能的综合提升与应用

通过 Clock 项目的开发,你将有机会综合运用Java基础知识,如面向对象编程、异常处理、集合框架等。此外,了解和使用Swing库将提升你的图形用户界面编程能力,而对开发环境的深入了解和优化则有助于提高开发效率和软件质量。

在完成本章的学习后,你将有能力设计并实现自己的Java Swing应用程序,并且能够根据需要配置和优化Java开发环境。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:Java Swing是一个用于构建桌面应用程序的GUI工具包,本项目旨在通过创建一个实时更新的时钟应用来指导初学者。它将涉及使用JFrame创建窗口、使用JLabel或JTextArea显示时间、以及利用Timer和TimerTask实现时间的实时更新。学习者将了解Swing组件、布局管理、事件处理和定时器使用等基础知识,从而加深对Java Swing API的理解。项目源代码位于src目录,并可在支持Swing的IDE中运行。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值