Creating Interactive GUIs with Swing's MVC Archite

http://www.developer.com/java/ent/article.php/3336761

 

 

Part 1: Model-View-Controller and Swing

Many GUI-based applications, client interfaces, and widget toolkits use the Model-View-Controller (MVC) architecture as a primary design pattern to present, manipulate, and store data for the end users. Java's Swing toolkit is no exception. Every visual component in Swing follows the MVC pattern to achieve its task. Model-View-Controller was build into Swing from the very beginning. In this article, I'll show how model-view-controller architecture is intertwined with Swing and how its robustness and strengths can be used to create extremely flexible and powerful graphical user interfaces that require minimal effort to modify once all components are in place.

This material is split in to two articles: This part describes the underlying implementation of Model-View-Controller in Swing and the second part demonstrates a concrete example of the MVC pattern's strength by showing how use three fundamental mechanisms in Java to alter data presentation at run time without changing the correlating model. But, so you don't think that I left all the good stuff for last, the first part of this article will come with fully working code that you can apply to an enterprise level UI project, with minimal changes.

Swing and MVC Design

In a commercial application, there are often requirements to have a table of information populated from a database, or some other real-time data source. Constant revising or requesting data on every change to its presentation (such as a sort or filter) would be extremely expansive and prone to performance bottlenecks. A great number of different caching techniques have been devised to improve performance in these cases, but there is a much simpler way to populate table once, show it, and then modify the presentation only, without touching the underling data. This technique can be applied to any visual components, including lists, drop-downs, and trees. And those UI components that don't have a lot of data, such as buttons, can still benefit from the ability to change their 'look and feel' without actually changing their functionality.

Well, as it turns out, Model-View-Controller in Swing does just that! It decouples presentation from data and from operation on that data. The controller is actually collapsed into a delegate object and is not truly separated from view or is not a separate class; this is why MVC in Swing is sometimes called "separable model architecture." The designers of Swing did this because it was "difficult to write a generic controller that didn't know specifics about the [particular] view."

All graphical components in Swing, and I do mean all , follow Model-View-Controller architecture. Therefore, according to the Swing specification and package structure, every component has a presentation class, model class, and a public interface. There is a separation, hoverer, between a GUI-based state model and an application-specific data model. The application-specific model is usually a model dealing with or containing real user data, such as a table, list or text area. A GUI state model is usually a model that does not relate directly to the user data, such as model of a button or a 'progress slider' component. In this article, I'll concentrate on application-specific data models.

If you've superficially used Swing to create basic Java GUIs or used any IDE to do it for you, you probably did not even notice that there is a separation between visual components and their data models. That is because Swing will install a default model for every component that you use. For the developer, there is no sense in creating a new model for every Button on the screen and then assign it to the button. The same goes for the complex components, such as Tables. Just creating one and adding text to it works fine and does not require explicit model object creation. However, the existence of a separable model in Swing allows programmers to do much more advanced GUI development, and was added from the initial design of Swing for a very important reason!

The controller, as I've mentioned, is not clearly apparent and is not used as in traditional MVC design pattern, but it does exist. The View layer of the MVC in Swing is objects used to render (or show) components on the screen; all visual components from Buttons to Sliders to Text Areas are part of the view layer and therefore have presentation classes. The way to truly utilize MVC in Swing is to explicitly install a new model object or chains of them. Several presentation classes may use the same Model class to contain the data they are presenting. They don't even have to be of the same type! What I mean is that you can create multiple components, such as text area boxes, and place them in different parts of your application. All of these text areas can have one model object associated with them. Any time one text area is updated—by user typing or deleting text, for example—all other text areas will reflect that change and show same text! Here is sample code that creates a model Object and two View objects, and installs a model into them. By comparison, if you create a View object with a default constructor, a default model would be used.

Document doc  = new



 DefaultStyledDocument();    // MODEL object
JTextArea ta1 = new



 JTextArea(doc);             // VIEW object 1
JTextArea ta2 = new



 JTextArea(doc);             // VIEW object 1


But wait, that is not all. You can have one model object assigned to view objects of different types, such as JTextArea and JTextField and they will both use it. For example, this code is legal:

Document doc  = new



 DefaultStyledDocument();       // MODEL object
JTextArea ta  = new



 JTextArea(doc);                // VIEW object 1
JTextField tf = new



 JTextField(doc, "Name"

, 1);    // VIEW object 2


 

 

 

 

Model Layer

The ability to separate presentation from data also lets developers modify the presentation of the same data. Therefore, issues such as sorting of filtering of pre-fetched data become very easy to solve and do not require re-fetching of the data on every user requested change in the view. You create one model object and install it into one (or many) view object(s), but you can also subclass the Model object and create your own custom models. Swing provides an abstract model class and a default concrete model for every visual component. See Listing 1 for a table of presentation classes and their corresponding models. Models usually have methods to get and set data and some specific methods correlating to the functionality of the view class, such as getColumnCount in the JTable model class. Therefore, the abstract model class only defines these required methods and the concrete one actually has their implementations (it extends the abstract class, as you probably have guessed).

For instance, the JTable view class has AbstractTableModel and DefaultTableModel . If you want to create a custom model for your JTable, with custom table header columns and their names, you can subclass DefaultTableModel and overwrite only the methods relating to the header info. See Listing 2 .

You also can extend AbstractTableModel and implement all methods yourself. DefaultTableModel keeps data in a Vector of Vectors data structure, so you'll need to create a compatible memory storage. The same is true for every default Swing model; they all keep data in some kind of generic collection class. All models also have methods to get data and in some cases its index position, if it's applicable.

JList and JTable both have methods to insert and access data based on the index within the internal memory storage. This is a very important feature because it can be used to manipulate the presentation without changing the underling data.

The trick is to use three fundamental features of the Java language: inheritance , passing objects by reference , and event/listener mechanism .

Models in Swing have no information of any views; therefore, if you use a custom model, any changes in its data are conveyed to the view by an event/listener mechanism. Because Java passes all complex objects by reference, you can create a chain of custom models, all containing a reference to one main model with data and their own indexes of that data. The custom models would inherit all required methods and overwrite index methods. In the event that a user requests a change in the view, an index would be recalculated and an action event would be propagated to all interested models. This would result in a change in the view but not the model! (Please see the second part of this article for a full implementation of this technique.)

Presentation and View Layer

The view layer is what the end user sees on the screen; because we already know that its data (what is represents) is separated, the Swing development team was kind enough to make it interchangeable and even extendable to custom looks-and-feels. This is completely different from the method I've just described to change data presentation. It is an application-specific GUI modification and not data specific. What that means is that you can switch the way your GUI looks, make a Java application look like a Solaris Motif program on Windows XP, for instance. Code like this (before any Swing components' initialization) would do the trick:

try {
UIManager.setLookAndFeel("
          com.sun.java.swing.plaf.motif.MotifLookAndFeel");
} catch...


And, even though your JTextArea and JTree would look different, it would still contain the same data and work in the same way; you can still use the same data-specific filtering and sorting techniques. The mechanism by which it works is called a Pluggable Look and Feel (PLAF) and is beyond the scope of this article, but you can find a great tutorial on Sun Microsystems' site about PLAF (see "A Swing Architecture Overview" in the references section).

Conclusion

In this part, I've described the fundamentals of Swing separable model architecture. I've gone over the structure of Model objects and their correlation to the View objects, and the way you can install custom models into your UI components. I also briefly described how the power of separation can let developers change the presentation without touching the corresponding information, both by means of event mechanism and by the Pluggable Look and Feel feature of Swing. By using custom models, you can achieve great flexibility and superior user functionality, at the same time avoiding unnecessary requests of data, caching, or other methods of altering presentation.

The included source code contains the full JTable example from Part 2 of this article with several custom models to filter and sort data.

Source Code

Download the source code here.

Listing 1

Component Model Interface Model Type
JButton ButtonModel GUI
JToggleButton ButtonModel GUI/data
JCheckBox ButtonModel GUI/data
JRadioButton ButtonModel GUI/data
JMenu ButtonModel GUI
JMenuItem ButtonModel GUI
JCheckBoxMenuItem ButtonModel GUI/data
JRadioButtonMenuItem ButtonModel GUI/data
JComboBox ComboBoxModel data
JProgressBar BoundedRangeModel GUI/data
JScrollBar BoundedRangeModel GUI/data
JSlider BoundedRangeModel GUI/data
JTabbedPane SingleSelectionModel GUI
JList ListModel data
JList ListSelectionModel GUI
JTable TableModel data
JTable TableColumnModel GUI
JTree TreeModel data
JTree TreeSelectionModel GUI
JEditorPane Document data
JTextPane Document data
JTextArea Document data
JTextField Document data
JPasswordField Document data

Listing 2

class



 PlanetTableModel extends



 DefaultTableModel {
   private static



 String[] planetModelNames
      = new



 String[] { "Moon Name"

, "Planet"

, "Atmosphere"

,
                       "Population"

 };

   private



 String filter = "All"

;

   public int



 getColumnCount()
   {
      return



 planetModelNames.length;
   }

   public



 String getColumnName(int



 c) {
   return



 planetModelNames[c];
   }

...




References

Core Swing Advanced Programming
By Kim Topley
ISBN: 0130832929
Publisher: Prentice Hall; December, 2000

Core Java 2, Volume II: Advanced Features (5th Edition)
By Cay Horstmann and Gary Cornell
Publisher: Prentice Hall; 5th edition (December 10, 2001)

A Swing Architecture Overview
http://java.sun.com/products/jfc/tsc/articles/architecture

Advanced MVC
http://www.ifi.unizh.ch/richter/Classes/oose2/03_mvc/02_mvc_java/02_mvc_java.html#4%20Models%20as%20Proxies

Swing Model Filtering
By Mitch Goldstein
http://www-106.ibm.com/developerworks/java/library/j-filters/

About the Author

Vlad Kofman is a System Architect working on projects under government defense contracts. He has also been involved with enterprise-level projects for major Wall Street firms and the U.S. government. His main interests are object-oriented programming methodologies and design patterns.

隔离型全桥LLC谐振变化器建模及参数设计资料【仿真+设计报告+计算书】 内容概要:本文档系统性地提供了隔离型全桥LLC谐振变换器的建模、仿真与参数设计全套技术资料,涵盖Simulink/Plecs仿真模型、详细设计报告及Mathcad计算书,内容涉及开环与闭环控制、软启动过程、恒压恒流双闭环竞争控制等多种工况。资料还拓展至多相交错LLC、双向CLLC拓扑以及V2G(Vehicle-to-Grid)电动汽车充放电等前沿应用场景,并融合状态估计算法(如卡尔曼滤波、扩展卡尔曼滤波、粒子滤波)与神经网络技术进行系统性能优化,配套PPT说明与高水平EI期刊复现案例,为电力电子领域的科研与工程设计提供完整的技术支撑; 适合人群:适用于电力电子、电气工程及其自动化等相关专业的高校研究生、科研人员,以及从事高频DC-DC变换器、新能源电源系统开发的工程师,具备扎实电路理论基础和Matlab/Simulink仿真能力者尤佳; 使用场景及目标:①支撑LLC谐振变换器相关的科研课题、学位论文或项目开发;②开展高频高效电源的参数设计、效率优化与动态响应分析;③学习并复现高水平期刊中先进的控制策略,如变频控制、混合移相控制及智能优化算法;④掌握Simulink/Matlab在电力电子系统建模、控制算法实现与状态估计中的综合应用; 阅读建议:建议结合仿真模型、Mathcad计算书与设计报告三位一体同步研习,重点理解主电路参数设计与闭环控制逻辑之间的耦合关系,优先运行典型算例并调试关键模块,有条件者可进一步推进至硬件原型验证,并积极探索状态估计与人工智能算法在电力系统动态建模中的融合应用。 立即下载

相关推荐

标准材质转物理材质 对应脚本:标准转物理材质转x.ms 一、插件用途 将 3ds Max 场景中的 StandardMaterial 标准材质转换为 PhysicalMaterial 物理材质

标准材质转物理材质 对应脚本:标准转物理材质转x.ms 一、插件用途 将 3ds Max 场景中的 StandardMaterial 标准材质转换为 PhysicalMaterial 物理材质,并递归处理 Multi/Sub 多维子材质及 Blend 混合材质中的标准材质。 二、转换内容 脚本尝试进行以下参数映射: - 漫反射颜色 → Physical Material 基础颜色。 - 漫反射贴图及启用状态 → 基础颜色贴图。 - 凹凸贴图、启用状态及强度 → 物理材质凹凸槽;强度会按脚本比例换算。 - Opacity 贴图 → 物理材质透明颜色贴图。 - 标准材质自发光颜色和强度 → 物理材质发光颜色和发光强度。 - 反射贴图强度 → 粗糙度的反向换算。 - Glossiness → Metalness 数值换算。 三、使用方法 1. 在 3ds Max 中运行脚本。 2. 出现“赵山河材质转换”小窗口。 3. 点击“标准材质转物理材质”。 4. 脚本扫描整个场景的材质库,并将场景物体所使用的标准材质转换为物理材质。 5. 转换完成后刷新视口。 四、处理范围 - 不是只处理当前选择对象,而是扫描整个场景的材质库。 - 对 Multi/Sub 材质递归转换其中的标准材质子项。 - 对 Blend 材质递归转换 map1、map2 中的材质。 - 非 StandardMaterial 的材质会按脚本逻辑保留,不会强制全部转成 PhysicalMaterial。 五、注意事项 - 这是参数近似转换,不是渲染结果完全一致的材质转换。 - 标准材质中的高光、反射、透明度、特殊贴图和高级参数不一定有一一对应关系。 - 脚本把 Glossiness 映射到 Metalness,属于

和文本框有关的练习(JTextArea和document)

本来帮朋友做一个课设,中途在考虑UI界面数据的获取时遇到了一些问题,于是花了点时间学习了一下。 本次主要涉及三个类:JTextArea、PlainDocument和正则表达式。 在swing里面,有几个和文本相关的类,这次我使用JTextArea,而在研究这个文本的一些技巧的时候,发现要用到PlainDocument这个类,这个类网上资料还真不多,只能自己琢磨了一番。正则表达式就是复习一下,好久

lsjweiyi的博客 1234

C#操作SQLite数据库帮助类——SQLiteHelper

using System; using System.Collections.Generic; using System.Data.SQLite; using System.Data; namespace Com.ZCWL.Rock.Helper { public class SQLiteHelper { private static string connectionString =

跟健哥出去走走 2万+

java中JTextArea类_Swing JTextArea类

JTextArea类是一个显示纯文本的多行区域。类声明以下是javax.swing.JTextArea类的声明 -public class JTextAreaextends JTextComponent类构造函数编号构造函数描述1JTextArea()构造一个新的TextArea。2JTextArea(Document doc)使用给定的文档模型构造一个新的JTextArea,并为所有其他参数(n...

weixin_39966922的博客 2102

ROS2理论与实践_宇树机器人Go2开发指南(文字)

本文介绍了Unitree Go2机器人ROS2开发环境的安装与配置流程。主要内容包括:1)从GitHub克隆unitree_ros2仓库;2)安装ROS2 Foxy版本依赖项;3)编译特定版本的CycloneDDS(0.10.2);4)编译unitree_go和unitree_api功能包;5)网络配置,包括设置静态IP和修改环境变量脚本setup.sh以指定网络接口。文章还提供了本地回环测试的替代方案,并建议配置完成后重启电脑。整个流程涉及ROS2环境切换、网络适配和编译安装等多个技术环节。

Li_Dongyi的博客 4205

LTSPICE使用教程:导入第三方库模型进行仿真

LTSPICE导入第三方SPICE模型进行仿真,以英飞凌的第三方.lib模型为实例进行详解

焊武大帝的专栏 3万+

探究 Z-Order

Z-order,也有人称为 Zorder,指的是对象之间的层次关系。举个简单的例子:在使用 PowERP oint 制作文件时,当你把文件上被其它对象盖住的某个对象「上推至最顶层」,你就是在改变它的 Z-order。通常 Z-order 高者置于 Z-order 低者的「上面」。 通常 GUI 程序设计都会用到 Z-order 的...

topinking的专栏 2114

中文实体关系抽取数据集大盘点:从百度Duie2.0到医疗CBLUE,哪个更适合你的NLP项目?

本文全面分析了中文实体关系抽取领域的主流数据集,包括百度DuIE 2.0、医疗CBLUE等,帮助开发者根据项目需求选择最适合的数据集。文章详细对比了各数据集的特点、适用场景及优化策略,为自然语言处理项目提供实用指南。

weixin_29284657的博客 121

swing 框架之Render(一)

http://fifa899.iteye.com/blog/201604   swing 框架之Render(一)   Swing组件根据其所操作的数据类型分为两种,一种是标量数据类型的组件,一类是复合数据类型的组件。标量数据类型的组件操作的是基本类型的数据,如字 符串、布尔、数字等,此类型组件包括JTextField、JCheckBox、JLabel、JButton等。复合数据类型的组件操作的是...

topinking的专栏 972

智能音箱麦克风阵列实战:Python实现宽带GSC波束形成(附完整代码)

本文从工程实战角度,详细介绍了如何在智能音箱中利用Python实现宽带GSC波束形成算法。文章深入解析了广义旁瓣消除器(GSC)的核心架构,包括固定波束形成、阻塞矩阵与自适应滤波路径,并提供了完整的仿真代码、关键参数(如LMS步长)调优方法,以及面向嵌入式部署的计算优化与移植考量,旨在帮助开发者构建在真实噪声环境下高效工作的语音增强系统。

opencv7vision的博客 342

自研半导体Fab缺陷分析系统:从Klarf解析到Defect Map可视化的实战替代方案

本文分享了自研半导体Fab缺陷分析系统的实战经验,重点解析了Klarf文件格式与坐标系转换的核心技术,并详细介绍了从数据解析到Defect Map可视化的完整实现路径。该系统旨在为成本敏感或追求技术自主的团队提供一套功能完备的Klarity Defect平替方案,有效降低对昂贵商业软件的依赖,实现缺陷数据的自主分析与可视化。

y7z8a的博客 706

[Swing]JTable 表头显示问题收藏

http://blog.youkuaiyun.com/sting2005/archive/2007/03/13/1527953.aspx    JTable组件显示数据时,如果直接将其放置在Frame的contentPane中则表头一行会显示不出来,如果将其放置在JScrollPane中显示数据的话,表头会自动显示出来。 引用Sun的原话为: It's easy to put a table in a scr...

topinking的专栏 478

Altera FPGA与高速ADS4249和DAC3482的LVDS接口设计

引言:本文以TI的ADS4249(ADC)和DAC3482(DAC)之间的接口为例,介绍Altera FPGA与ADC/DAC之间的DDR LVDS接口设计以及时序约束详细设计。本文介绍的实例可方便扩展到具有类似接口格式的其他高速数据转换器设计。

FPGA_Designer 1899

Swing框架之Component

  Swing框架之Component          昨天晚上写完Swing的模型和渲染器之后,觉得对Swing的体系结构还是没有说清楚。Swing的基础体系结构中的四大基本对象Component、 Model、UI Delegate以及Renderer都值得详细解释。Swing的树状组件结构(虽然这是用户界面工具通有的特征)也值得详细解释,因为这是完成某些复 杂Swing组件,尤其像JT...

topinking的专栏 302

深入解析逆时偏移(RTM)算法及其在C++中的实现

逆时偏移(Reverse Time Migration, RTM)是一种高级地震成像技术,广泛应用于地震勘探中。RTM通过逆时传播波场来构建地震子波,能够提供更高分辨率和更精确的地下构造图像。本文将详细介绍RTM的基本原理,并通过C++代码示例,深入解析如何实现和优化RTM算法。希望这篇文章能够为地球物理学家和C++开发者提供有价值的参考。

qq_38334677的博客 1526

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Swing模型与渲染器

关键字: swing 本 文承接Swing/AWT/SWT比较一文,概要叙述Swing的体系结构,解释了Swing架构关键概念:模型与渲染器,解释如何使用渲染对象扩展该体 系架构来支持大数据量的组件。后面的文章还会简要概述SWT的体系结构,为Swing/AWT和SWT的比较做一铺垫。 ===================================== Java 基础...

topinking的专栏 211

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

POI读写Excel文件(转)- -

http://wwbf1983.blogdriver.com/wwbf1983/1256690.html     POI读写Excel文件(转)- - 一、Excel基础二、HSSF概况 三、通过usermodel读取文件 四、通过usermodel写入文件 五、通过eventusermodel读取文件 六、...

topinking的专栏 203

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Swing框架之Component:续文三

Swing框架之Component:续文三 Swing事件与事件处理器模型          Component在Swing模型中是事件触发源。前一篇文章在描述Swing的事件处理模型时就已经提到了这个事件处理过程。简单来说,Swing组 件在侦听到原生事件并处理后,往往产生新的逻辑事件。逻辑事件是某些组件所特有的、具有特定语义的事件,比如JButton按下时产生 ActionEvent...

topinking的专栏 202

Swing框架之Model之一

Swing框架之Model之一      构建应用程序应该 以数据为中心,而不是以用户界面为中心,这是一个良好的编程习惯。为支持这种编程范式,Swing为每种带有逻辑数据或值的组件定义了独立的模型接口,这种分割使程序可以选择向Swing组件中嵌入自己的模型实现。        下面表格列出Swing中组件及其模型的映射关系: 组件 Model接口 Model类型 JBut...

topinking的专栏 198

Swing框架之Model之二

Swing框架之Model之二 轻量级通知        下面Swing中的模型使用轻量级通知,它们是基于 ChangeListener、ChangeEvent接口的: Model Listener Event BoundedRangeModel ChangeListener ChangeEvent ButtonModel ChangeListener ChangeE...

topinking的专栏 191

通过Java Swing看透MVC设计模式

http://www.yesky.com/374/1802374.shtml 通过Java Swing看透MVC设计模式 2004-05-28 14:50 作者:vipcowrie编译 出处:Matrix 责任编辑:方舟   一个好的用户界面(GUI)的设计通常可以在现实世界找到相应的表现。例如,如果...

topinking的专栏 174

Understanding Swing’s Model

Understanding Swing’s Model   经常用 Swing 开发 Java GUI 程序的人一定听过这样的说法 ,Swing 控件是按 MVC结构设计的。更准确地说, Swing是 Model-driven的结构。但不同 Swing控件的 Model,其作用是否相同呢?比如当你在使用 JButton时,你很少需要关心 ButtonModel的存在,但在 JTable使用时,...

topinking的专栏 167

Swing框架之Component:续文二

2008 - 05 - 06 Swing框架之Component:续文二          Swing的事件处理过程为:事件调度线程(Event Dispatch Thread)从事件队列(EventQueue)中获取底层系统捕获的原生事件,如鼠标、键盘、焦点、PAINT事件等。接着调用该事件源组件的 dispachEvent。该方法过滤出特殊事件后,调用processEvent进行处理...

topinking的专栏 157

Swing table

http://www.iteye.com/topic/80270 http://blog.sina.com.cn/s/blog_4b6047bc0100073l.html

topinking的专栏 143

MVC模式

http://www.itisedu.com/phrase/200604231324325.html    MVC模式 是"Model-View-Controller"的缩写,中文翻译为"模式 -视图 -控制器"。MVC 应用程序 总是由这三个部分组成。Event(事件)导致Controller改变Model或View,或者同时改变两者。只要Controller改变了Models的数据或者属...

topinking的专栏 127
上一篇: Swing框架之Model之二
下一篇: Understanding Swing’s Model
topinking
博客等级 码龄20年 15粉丝 242原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值