25、Mac开发绘图基础:NSColor、NSView及相关绘图技术详解

Mac开发绘图基础:NSColor、NSView及相关绘图技术详解

1. NSColor基础

NSColor类功能强大,下面介绍其基础用法。

1.1 基本颜色常量

可以使用以下代码获取基本颜色常量:

NSColor* color1 = [NSColor redColor];
NSColor* color2 = [NSColor greenColor];
NSColor* color3 = [NSColor blueColor];
NSColor* color4 = [NSColor purpleColor];
NSColor* color5 = [NSColor yellowColor];
NSColor* color6 = [NSColor orangeColor];
1.2 透明颜色

使用 clearColor 获取完全透明的颜色:

NSColor* color7 = [NSColor clearColor];
1.3 通过通道设置颜色

可以通过指定红、绿、蓝和透明度通道的值来创建颜色,每个通道的值范围是0.0到1.0:

NSColor* color8 = [NSColor colorWithCalibratedRed: 0.25
                                            green: 0.30
                                             blue: 0.45
                                            alpha: 1.0];

如果参考颜色的RGBA值范围是0到255,可以通过除以255将其转换为0.0到1.0的浮点数:

NSColor* color = [NSColor colorWithCalibratedRed: (122.0/255.0)
                                           green: (224.0/255.0)
                                            blue: (185.0/255.0)
                                           alpha: (128.0/255.0) ];
1.4 设置颜色和绘制

获取颜色后,通常使用 -[NSColor set] 方法设置颜色,然后绘制形状或文本。也可以分别设置描边和填充颜色:

NSColor* color1 = [NSColor redColor];
[color1 set];
NSColor* color2 = [NSColor greenColor];
[color2 setStroke];
NSColor* color3 = [NSColor blueColor];
[color3 setFill];
2. 子类化NSView

创建一个名为“BasicCocoaDrawing”的新Cocoa项目,并将其放在 ~/CocoaBook/ch10/ 目录下。然后向项目中添加一个Objective - C类,该类是NSView的子类。

在创建类时,第二屏将文件命名为 ShapesAndColorsView.m ,确保勾选“Also create ShapesAndColorsView.h”和Targets部分的“BasicCocoaDrawing”项。同时,要从“Subclass of”下拉菜单中选择NSView,否则视图将无法正常工作。

Xcode会创建一个基本的类实现,如下所示:

#import "ShapesAndColorsView.h"
@implementation ShapesAndColorsView
- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
    }
    return self;
}
- (void)drawRect:(NSRect)dirtyRect {
    // Drawing code here.
}
@end

-initWithFrame: 方法类似于大多数类中的 -init 方法,但它接受一个框架作为视图的初始位置和大小。可以将其格式调整为更熟悉的形式:

- (id)initWithFrame:(NSRect)frame {
    if ( self = [super initWithFrame:frame] ) {
    }
    return self;
}

-drawRect: 方法是进行实际绘图的地方。可以使用 NSRectFill() 函数进行绘制,以下是一个重写的 -drawRect: 方法示例:

- (void)drawRect:(NSRect)dirtyRect {
    NSColor* backgroundColor = [NSColor orangeColor];
    NSColor* foregroundColor = [NSColor yellowColor];
    NSRect bounds = self.bounds;    
    [backgroundColor set];
    NSRectFill ( bounds );
    CGFloat insetX  = ( NSWidth  (bounds) * 0.25 );
    CGFloat insetY  = ( NSHeight (bounds) * 0.25 );    
    NSRect  shape   = NSInsetRect ( bounds, insetX, insetY );
    [foregroundColor set];
    NSRectFill ( shape );
}

这里通过将视图的边界向内缩进25%,使得绘制的形状大小为视图的一半。

3. 何时进行绘制

作为应用程序开发者,不需要直接告诉视图进行绘制,Cocoa会处理这个过程。只需等待 -drawRect: 方法被调用即可。该方法可能每秒被调用多次,因此要确保其效率。例如,如果有计算对象位置的代码,应该只计算一次并将结果保存为实例变量进行缓存。

如果影响视图的数据发生变化(如对象的位置),更新缓存,然后调用 [myView setNeedsDisplay:YES] 通知Cocoa需要更新。视图系统会收集所有这些请求,并在需要绘制时将它们合并为一次对 -drawRect: 方法的调用。

注意,不要直接调用 -drawRect: 方法,否则可能导致视图绘制不正确或应用程序崩溃。

4. 实例化视图

有两种方法可以将视图添加到窗口中,这里先介绍代码实现的方法。修改 BasicCocoaDrawingAppDelegate.m 文件如下:

#import "BasicCocoaDrawingAppDelegate.h"
#import "ShapesAndColorsView.h"
@implementation BasicCocoaDrawingAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSRect viewFrame = [self.window.contentView bounds];
    ShapesAndColorsView* shapeView;
    shapeView = [[ShapesAndColorsView alloc] initWithFrame:viewFrame];
    [self.window.contentView addSubview:shapeView];
    [shapeView release];
}
@end

确保在文件顶部添加 #import "ShapesAndColorsView.h" 语句以避免编译错误。

5. Bounds和Frames

在上面的代码中,使用了窗口内容视图的边界(bounds)作为 ShapesAndColorsView 的框架(frame)。这是一种让视图填充整个窗口的简单方法,但也可以使用内容视图的框架。不过,使用框架时可能不会得到预期的结果。

视图的框架是相对于其父视图的,因此原点(10, 10)是父视图中的位置,而不是窗口中的位置。通常使用父视图的边界更好,因为其原点通常为(0, 0)。

例如:

NSRect frame = NSMakeRect ( 10, 10, 100, 100 );
NSView* parentView = [[NSView alloc] initWithFrame:frame];
// 这将完全填充父视图,因为原点是0,0
NSView* childView = [[NSView alloc] initWithFrame:parentView.bounds];
[parentView addSubview:childView];

而如果使用父视图的框架:

NSRect frame = NSMakeRect ( 10, 10, 100, 100 );
NSView* parentView = [[NSView alloc] initWithFrame:frame];
// 这将在左侧和底部留下10个像素的空白
NSView* childView = [[NSView alloc] initWithFrame:parentView.frame];
[parentView addSubview:childView];

虽然边界的原点并不总是(0, 0),但在简单情况下这是最常见的行为。

保存文件并运行项目,会在窗口中间看到一个大正方形。但此时如果调整窗口大小,视图不会随之调整。可以通过代码使用NSView的 autoresizingMask 属性来解决这个问题。

6. 在代码中设置调整大小的值

autoresizingMask 属性使用位掩码值,这是一种较低级的C技术,用于将多个“标志”组合成一个值。可以使用按位或运算符( | )组合多个值:

NSInteger bitmask = ( FirstValue | SecondValue | ThirdValue );

NSView.h 文件顶部列出了 autoresizingMask 属性的可能值:

enum {
    NSViewNotSizable      =  0,
    NSViewMinXMargin      =  1,
    NSViewWidthSizable    =  2,
    NSViewMaxXMargin      =  4,
    NSViewMinYMargin      =  8,
    NSViewHeightSizable   = 16,
    NSViewMaxYMargin      = 32
};

这些值的含义如下表所示:
| 值 | 描述 |
| ---- | ---- |
| NSViewNotSizable | 完全不调整大小 |
| NSViewMinXMargin | 左边缘可调整大小,将视图固定在右侧 |
| NSViewMaxXMargin | 右边缘可调整大小,将视图固定在左侧 |
| NSViewMinYMargin | 底部边缘可调整大小,将视图固定在顶部 |
| NSViewMaxYMargin | 顶部边缘可调整大小,将视图固定在底部 |
| NSViewWidthSizable | 视图的宽度随父视图变化 |
| NSViewHeightSizable | 视图的高度随父视图变化 |

在这个例子中,要让视图随窗口调整大小,可以这样做:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSRect viewFrame = [self.window.contentView bounds];
    ShapesAndColorsView* shapeView;
    shapeView = [[ShapesAndColorsView alloc] initWithFrame:viewFrame];
    [self.window.contentView addSubview:shapeView];
    // 随窗口调整大小
    NSInteger resizingMask = ( NSViewWidthSizable | NSViewHeightSizable );
    [shapeView setAutoresizingMask:resizingMask];
    [shapeView release];
}

保存文件并重新运行项目,视图现在会随窗口调整大小。

7. 图形上下文

在Cocoa中学习绘图时, -[NSColor set] 方法可能会让人困惑。实际上,这里涉及到一个名为图形上下文(graphics context)的概念,它是 NSGraphicsContext 的实例。

图形上下文跟踪影响绘图的设置,包括当前颜色。调用 NSColor -set 方法会将颜色分配给图形上下文。当调用 NSRectFill() 函数时,会使用该颜色进行绘制。

图形上下文是堆叠的,可以使用 +saveGraphicsState 创建当前上下文的副本,进行一些更改,绘制内容,然后使用 +restoreGraphicsState 恢复原始上下文,示例如下:

// 将当前颜色设置为蓝色
[[NSColor blueColor] set];
NSRectFill ( outerRectangle );
[NSGraphicsContext saveGraphicsState];
    // 将当前颜色设置为白色
    [[NSColor whiteColor] set];
    NSRectFill ( innerRectangle );
[NSGraphicsContext restoreGraphicsState];
// 恢复后,当前颜色再次变为蓝色
NSRectFill ( innerRectangle );

要注意, +saveGraphicsState +restoreGraphicsState 必须成对使用,否则会在控制台看到错误信息,视图可能无法正确绘制。

当视图类的 -drawRect: 方法被调用时,Cocoa会自动为你设置图形上下文。该方法传入的矩形表示视图中需要重绘的无效区域。可以重绘整个视图,但只绘制无效区域可能更高效。不过,确定如何只重绘该区域可能比较困难,在某些情况下,计算所需的时间可能比重绘整个视图还要长,因此这个矩形更像是绘制位置的指导,而不是规则。

8. Bezier路径

只有非常简单的形状可以用矩形描述,而可以使用 NSBezierPath 创建任何想象中的2D形状。Bezier路径是Cocoa矢量绘图系统的核心,它允许在不依赖特定分辨率的情况下进行绘制,例如可以在绘图程序中实现缩放功能。与 NSRect 结构体不同, NSBezierPath 实例是完整的对象,可以将自己绘制到视图中。

8.1 绘制多边形

可以使用 NSBezierPath 逐点创建多边形,以下是一个示例:

- (void)drawRect:(NSRect)dirtyRect {
    [[NSColor blueColor] set];
    NSRectFill (self.bounds);
    NSRect  bounds = self.bounds;
    CGFloat width  = bounds.size.width;
    CGFloat height = bounds.size.height;
    NSBezierPath* path = [NSBezierPath bezierPath];
    [path moveToPoint: NSMakePoint(width*0.35, height*0.1)];
    [path lineToPoint: NSMakePoint(width*0.65, height*0.1)];
    [path lineToPoint: NSMakePoint(width*0.65, height*0.6)];
    [path lineToPoint: NSMakePoint(width*0.9,  height*0.6)];
    [path lineToPoint: NSMakePoint(width*0.5,  height*0.9)];
    [path lineToPoint: NSMakePoint(width*0.1,  height*0.6)];
    [path lineToPoint: NSMakePoint(width*0.35, height*0.6)];
    [path closePath];
    [[NSColor whiteColor] set];
    [path fill];
}

调用路径的 -fill 方法会在当前图形上下文中绘制内容。

8.2 绘制曲线

NSBezierPath 类有很多方法可以向形状中添加曲线,例如 -curveToPoint: 方法:

- (void)curveToPoint: (NSPoint)endPoint
       controlPoint1: (NSPoint)controlPoint1
       controlPoint2: (NSPoint)controlPoint2;

控制点控制曲线的形状,类似于引力的作用。以下是一个绘制曲线的示例:

- (void)drawRect:(NSRect)dirtyRect {
    [[NSColor colorWithDeviceRed:0.2 green:0.6 blue:0.1 alpha:1.0] set];
    NSRectFill(self.bounds);
    NSBezierPath * path = [NSBezierPath bezierPath];
    path.lineWidth = 6.0;
    NSRect  bounds = self.bounds;
    CGFloat width  = NSWidth  ( bounds );
    CGFloat height = NSHeight ( bounds );
    NSPoint startPoint = NSMakePoint ( width * 0.1, height * 0.1 );
    NSPoint endPoint   = NSMakePoint ( width * 0.9, height * 0.9 );
    [path moveToPoint: startPoint];
    [path curveToPoint: endPoint
         controlPoint1: NSMakePoint ( width * 0.9, height * 0.1 )
         controlPoint2: NSMakePoint ( width * 0.1, height * 0.9 )];
    [[NSColor colorWithDeviceRed:0.2 green:0.8 blue:0.3 alpha:0.9] set];
    [path fill];
    [[NSColor colorWithDeviceWhite:1.0 alpha:1.0] set];
    [path stroke];
}
8.3 添加圆弧

可以使用以下方法向路径中添加圆弧:

- (void)appendBezierPathWithArcWithCenter: (NSPoint)center
                                   radius: (CGFloat)radius
                               startAngle: (CGFloat)startAngle
                                 endAngle: (CGFloat)endAngle
                                clockwise: (BOOL)clockwise;
- (void)appendBezierPathWithArcFromPoint: (NSPoint)point1
                                 toPoint: (NSPoint)point2
                                  radius: (CGFloat)radius;

“from point”方法类似于之前使用的方法,从一个点绘制到另一个点的圆弧;“with center”方法从中心开始,以指定的半径绘制圆。可以绘制圆的部分,以创建饼图样式的形状。以下是一个从中心绘制圆弧的示例:

- (void)drawRect:(NSRect)dirtyRect {
    [[NSColor purpleColor] set];
    NSRectFill(self.bounds);
    NSBezierPath * path = [NSBezierPath bezierPath];
    path.lineWidth = 6;
    NSPoint origin;
    origin.x = NSWidth(self.bounds) * 0.5;
    origin.y = NSHeight(self.bounds) * 0.5;
    CGFloat radius = NSWidth(self.bounds) *0.25;
    [path moveToPoint: origin];
    [path appendBezierPathWithArcWithCenter: origin
                                     radius: radius
                                 startAngle: 0
                                   endAngle: 321
                                  clockwise: NO];
    [[NSColor magentaColor] set];
    [path fill];
    [[NSColor whiteColor] set]; 
    [path stroke];
}
9. 图像

作为Mac开发者,有多种图像类型可供选择,最常见的是 NSImage CGImage (正式名称为 CGImageRef )。在Snow Leopard之后, NSImage 的底层由 CGImage 支持,因此在两者之间切换变得更加容易。虽然不能直接进行类型转换,但可以使用 NSImage 的以下两个方法:

- (id)initWithCGImage: (CGImageRef)cgImage
                 size: (NSSize)size;
- (CGImageRef)CGImageForProposedRect: (NSRect *)rect
                             context: (NSGraphicsContext *)context
                               hints: (NSDictionary *)hints;

虽然 CGImage 看起来可能是主要类型,但它不是Objective - C类,而是一个不透明的结构体类型,不能为其添加类别或使用Cocoa常用的通用编程技术。因此,建议使用更灵活的 NSImage 类,它具有Objective - C的所有优点,并且一些标准的Cocoa视图类已经知道如何显示 NSImage ,无需进行转换。如果使用垃圾回收, NSImage 也能直接工作。唯一的缺点是 NSImage 在iPhone上不存在,但有一个大致等效的类 UIImage

9.1 加载图像数据

加载现有图像数据主要有两种方式:读取现有图像文件的内容,或从Cocoa请求标准图标。如果使用标准图标,可以使用与Mac OS X本身相同的图标,例如颜色选择器或计算机图标。如果从文件加载自定义图像,通常需要将其复制到项目中,因为无法保证用户机器上有哪些文件。

9.2 从项目中加载图像

首先,找到要使用的图像,几乎任何图像都可以,如PNG或JPEG文件。可以从网站下载或使用照片库中的图像,建议使用大小约为1000×1000像素的图像。将文件从Finder拖到项目的Resources文件夹中,当Xcode询问时,确认要复制文件并将其添加到目标中。可以使用之前“子类化NSView”部分的项目作为起点。

通过以上内容,我们详细介绍了Mac开发中NSColor、NSView、Bezier路径以及图像相关的绘图基础和技术,希望能帮助开发者更好地进行自定义视图和绘图开发。

Mac开发绘图基础:NSColor、NSView及相关绘图技术详解

10. 图像操作总结与对比

为了更清晰地了解 NSImage CGImage 的特点,这里对它们进行一个总结对比,如下表所示:
| 特性 | NSImage | CGImage |
| ---- | ---- | ---- |
| 类型 | Objective - C类 | 不透明结构体类型 |
| 编程灵活性 | 可添加类别,使用通用编程技术 | 需作为数据容器,使用C函数操作 |
| 与Cocoa视图兼容性 | 部分标准Cocoa视图类可直接显示 | 需转换才能使用 |
| 垃圾回收支持 | 直接支持 | 无特殊支持 |
| 平台适用性 | Mac可用,iPhone无 | 无平台限制,但需适配 |

从这个表格可以看出, NSImage 在大多数情况下更适合Mac开发,尤其是在需要使用Cocoa特性和简化开发流程时。

11. 绘图性能优化

在绘图过程中,性能是一个重要的考虑因素。以下是一些绘图性能优化的建议:
- 缓存计算结果 :如前面提到的,如果有计算对象位置等操作,应该只计算一次并将结果保存为实例变量进行缓存。例如,在 -drawRect: 方法中,如果需要计算某个图形的位置,可以将计算结果保存下来,避免每次调用 -drawRect: 都进行重复计算。

@interface MyView : NSView {
    NSRect cachedShapeRect;
    BOOL isCacheValid;
}
@end

@implementation MyView

- (void)calculateShapeRect {
    // 进行复杂的计算
    NSRect bounds = self.bounds;
    CGFloat insetX  = ( NSWidth  (bounds) * 0.25 );
    CGFloat insetY  = ( NSHeight (bounds) * 0.25 );    
    cachedShapeRect = NSInsetRect ( bounds, insetX, insetY );
    isCacheValid = YES;
}

- (void)drawRect:(NSRect)dirtyRect {
    if (!isCacheValid) {
        [self calculateShapeRect];
    }
    // 使用缓存的结果进行绘图
    NSColor* foregroundColor = [NSColor yellowColor];
    [foregroundColor set];
    NSRectFill ( cachedShapeRect );
}

@end
  • 只绘制需要更新的区域 :虽然计算只绘制无效区域可能比较困难,但在某些情况下可以显著提高性能。可以通过判断 dirtyRect 与需要绘制的图形的交集,只绘制交集部分。
- (void)drawRect:(NSRect)dirtyRect {
    NSRect shapeRect = [self calculateShapeRect];
    NSRect intersectionRect = NSIntersectionRect(dirtyRect, shapeRect);
    if (!NSIsEmptyRect(intersectionRect)) {
        // 只绘制交集部分
        NSColor* foregroundColor = [NSColor yellowColor];
        [foregroundColor set];
        NSRectFill ( intersectionRect );
    }
}
  • 合理使用图形上下文 :确保 +saveGraphicsState +restoreGraphicsState 成对使用,避免不必要的上下文切换和资源浪费。
12. 绘图流程总结

为了更清晰地展示整个绘图过程,下面是一个mermaid格式的流程图:

graph TD;
    A[创建NSView子类] --> B[重写-initWithFrame:方法];
    B --> C[重写-drawRect:方法];
    C --> D[设置图形上下文和颜色];
    D --> E[绘制基本形状或Bezier路径];
    E --> F[判断是否需要更新绘图数据];
    F -- 是 --> G[更新缓存数据];
    G --> H[调用setNeedsDisplay:YES];
    F -- 否 --> I[等待下次-drawRect:调用];
    H --> I;
    I --> C;

这个流程图展示了从创建视图子类到进行绘图,再到处理数据更新的整个过程。

13. 常见绘图错误及解决方法

在绘图过程中,可能会遇到一些常见的错误,以下是一些常见错误及解决方法:
| 错误现象 | 可能原因 | 解决方法 |
| ---- | ---- | ---- |
| 视图不显示 | 视图未正确添加到窗口,或 -drawRect: 方法未正确实现 | 检查视图添加代码,确保 -drawRect: 方法中有正确的绘图代码 |
| 颜色显示不正确 | 颜色设置错误,或图形上下文未正确管理 | 检查颜色设置代码,确保 +saveGraphicsState +restoreGraphicsState 成对使用 |
| 视图绘制闪烁 | 频繁调用 setNeedsDisplay:YES ,或 -drawRect: 方法效率低下 | 优化数据更新逻辑,减少 setNeedsDisplay:YES 的调用次数,优化 -drawRect: 方法中的计算代码 |
| 应用程序崩溃 | 内存管理问题,或直接调用 -drawRect: 方法 | 检查内存管理代码,避免直接调用 -drawRect: 方法 |

14. 扩展应用:自定义绘图组件

基于前面介绍的绘图技术,可以创建自定义的绘图组件。例如,创建一个自定义的按钮组件,该按钮可以有自定义的形状和颜色。

// CustomButtonView.h
#import <Cocoa/Cocoa.h>

@interface CustomButtonView : NSView

@property (nonatomic, strong) NSColor *buttonColor;

@end

// CustomButtonView.m
#import "CustomButtonView.h"

@implementation CustomButtonView

- (instancetype)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.buttonColor = [NSColor blueColor];
    }
    return self;
}

- (void)drawRect:(NSRect)dirtyRect {
    [self.buttonColor set];
    NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:self.bounds xRadius:10 yRadius:10];
    [path fill];
}

@end

在这个例子中,创建了一个自定义的按钮视图,它可以有自定义的颜色和圆角。可以在其他视图中使用这个自定义按钮视图,如下所示:

// 在某个视图控制器中使用自定义按钮
- (void)viewDidLoad {
    [super viewDidLoad];
    NSRect buttonFrame = NSMakeRect(100, 100, 200, 50);
    CustomButtonView *customButton = [[CustomButtonView alloc] initWithFrame:buttonFrame];
    [self.view addSubview:customButton];
}
15. 总结与展望

通过本文的介绍,我们详细了解了Mac开发中绘图的基础知识,包括 NSColor 的使用、 NSView 的子类化、Bezier路径的绘制、图像的加载和处理等。这些知识为开发者进行自定义视图和绘图开发提供了坚实的基础。

在未来的开发中,可以进一步探索更复杂的绘图技术,如动画效果的实现、3D绘图等。同时,随着技术的不断发展,绘图框架也可能会有新的特性和优化,开发者需要不断学习和跟进,以提高自己的开发水平。希望本文能帮助开发者更好地掌握Mac开发中的绘图技术,创造出更美观、高效的应用程序。

内容概要:本文深入探讨了Django REST Framework(DRF)在毕业设计中的高级应用与性能优化,围绕智能校园系统案例,系统讲解了DRF的核心进阶技术,包括高级序列化器设计、视图集定制、细粒度权限控制、查询优化、缓存策略、异步任务处理以及WebSocket实时通信集成。文章通过详细的代码示例,展示了如何利用DynamicFieldsModelSerializer实现动态字段返回、使用select_related和prefetch_related优化数据库查询、通过Celery实现异步任务、并集成Channels实现WebSocket实时数据推送。同时介绍了基于IP的限流、自定义分页、聚合统计等实用功能,全面提升API性能与安全性。; 适合人群:具备Django和DRF基础,正在进行毕业设计或开发复杂Web API的高校学生及初级开发者,尤其适合希望提升项目技术深度与系统性能的学习者。; 使用场景及目标:①构建高性能、可扩展的RESTful API,应用于智能校园、数据分析、实时监控等毕业设计项目;②掌握DRF高级技巧,如动态序列化、查询优化、缓存、异步任务与实时通信,提升项目竞争力;③优化系统响应速度与用户体验,应对高并发场景。; 阅读建议:此资源以实战为导向,建议读者结合代码逐项实践,重点理解性能优化与架构设计思路,同时动手搭建环境测试缓存、异步任务和WebSocket功能,深入掌握DRF在真实项目中的高级应用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值