【IOS 开发学习总结-OC-65】Quartz 2D绘图(4-2)——绘制文本+设置阴影+使用路径

【IOS 开发学习总结-OC-65】Quartz 2D绘图(中)——绘制文本+设置阴影+使用路径

绘制文本

CGContextRef为绘制文字提供了如下函数。

CGAffineTransform CGContextGetTextMatrix(CGContextRef c):获取当前对文本执行变换的变换矩阵。

CGPoint CGContextGetTextPosition(CGContextRef c):获取该CGContextRef中当前绘制文本的位置。

CGContextSelectFont(CGContextRef c, const char *name, CGFloat size, CGTextEncoding textEncoding):设置该CGContextRef当前绘制文本的字体、字体大小。

void CGContextSetCharacterSpacing(CGContextRef c, CGFloat spacing):设置该CGContextRef中绘制文本的字符间距。

void CGContextSetFont(CGContextRef c, CGFontRef font):设置该CGContextRef中绘制文本的字体。

void CGContextSetFontSize(CGContextRef c, CGFloat size):设置该CGContextRef中绘制文本的字体大小。

CGContextSetTextDrawingMode(CGContextRef c, CGTextDrawingMode mode):设置该CGContextRef绘制文本的绘制模式。该函数支持kCGTextFill、kCGTextStroke、kCGTextFillStroke等绘制模式。

void CGContextSetTextMatrix(CGContextRef c, CGAffineTransform t):设置对将要绘制的文本执行指定的变换。

CGContextSetTextPosition(CGContextRef c , CGFloat x, CGFloat y):设置CGContextRef的一个文本的绘制位置。

void CGContextShowText(CGContextRef c,
const char *string,
size_t length):控制CGContextRef在当前绘制点绘制指定文本。

void CGContextShowTextAtPoint (CGContextRef c,
CGFloat x,
CGFloat y,
const char *string,
size_t length):控制CGContextRef在指定绘制点绘制指定文本。

除此之外,系统还为NSString提供了一个UIStringDrawing分类,通过该分类为NSString增加了drawAtPoint:withAttributes:、drawInRect:withAttributes:等绘制方法,通过这些绘制方法可以更方便地绘制字符串。

总结起来,使用CGContextRef绘制文本的步骤如下。

获取绘图的CGContextRef。

设置绘制文本的相关属性,例如,绘制文本所用的绘制方式、字体大小、字体名称等。

如果只是绘制不需要进行变换的文本,直接调用NSString的drawAtPoint:withAttributes:、drawInAttributes:withFont:等方法绘制即可。如果需要对绘制的文本进行变换,则需要先调用CGContextSetTextMatrix()函数设置变换矩阵,再调用CGContextShowTextAtPoint()方法绘制文本。

下面的程序示范了如何利用Quartz 2D绘制文本。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKTextView类,并向该界面设计文件添加两个UISlider控件。为了让这两个UISlider控件控制文本的缩放和旋转角度,将控制缩放的UISlider的最小值、最大值分别设置为0.1、5;将控制旋转的UISlikder的最小值、最大值设分别为-90、90,程序为两个UISlider的Value Changed事件分别绑定scaleChanged:和rotateChanged: IBActino方法。

下面是该应用的视图控制器类的实现代码。

程序清单:codes/12/12.2/DrawTextTest/DrawTextTest/FKViewController.m

@implementation FKViewController  
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
    self.view.backgroundColor = [UIColor whiteColor];  
}  
- (IBAction)scaleChanged:(id)sender  
{  
    // 修改FKTextView的scaleRate属性  
    ((FKTextView*)self.view).scaleRate = ((UISlider*)sender).value;  
}  
- (IBAction)rotateChanged:(id)sender  
{  
    // 修改FKTextView的rotateAngle属性  
    ((FKTextView*)self.view).rotateAngle = ((UISlider*)sender).value;  
}  
@end 

从上面的控制器代码可以看出,当用户拖动界面的UISlider时,视图控制器中的监听器方法就会改变FKTextView控件的scaleRate、rotateAngle属性。当这些属性被修改时,FKTextView就会使用特定的缩放比、旋转角重绘文本。下面是FKTextView类的实现代码。

程序清单:codes/12/12.2/DrawTextTest/DrawTextTest/FKTextView.m

@implementation FKTextView  
- (void)setScaleRate:(CGFloat)scaleRate  
{  
    if(_scaleRate != scaleRate)  
    {  
        _scaleRate = scaleRate;   
        [self setNeedsDisplay];  // 通知该控件重绘自己  
    }  
}  
- (void)setRotateAngle:(CGFloat)rotateAngle  
{  
    if(_rotateAngle != rotateAngle)  
    {  
        _rotateAngle = rotateAngle;  
        [self setNeedsDisplay];  // 通知该控件重绘自己  
    }  
}  
// 重写该方法绘制该控件  
- (void)drawRect:(CGRect)rect  
{     
    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取该控件的绘图CGContextRef  
    CGContextSetCharacterSpacing (ctx, 4);  // 设置字符间距  
    CGContextSetRGBFillColor (ctx, 1, 0, 1, 1);  // 设置填充颜色  
    CGContextSetRGBStrokeColor (ctx, 0, 0, 1, 1);  // 设置线条颜色  
    CGContextSetTextDrawingMode (ctx, kCGTextFill);  // 设置使用填充模式绘制文字  
    // 绘制文字   
    [@"疯狂iOS讲义" drawAtPoint:CGPointMake(10 ,20)  
        withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:  
        [UIFont fontWithName:@"Arial Rounded MT Bold" size: 45],  
        NSFontAttributeName,  
        [UIColor magentaColor] , NSForegroundColorAttributeName , nil]];  
    // 设置使用描边模式绘制文字  
    CGContextSetTextDrawingMode (ctx, kCGTextStroke);  
    // 绘制文字  
    [@"疯狂Android讲义" drawAtPoint:CGPointMake(10 ,80)  
        withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:  
        [UIFont fontWithName:@"Heiti SC" size: 40],NSFontAttributeName,  
        [UIColor blueColor] , NSForegroundColorAttributeName , nil]];  
    // 设置使用填充、描边模式绘制文字  
    CGContextSetTextDrawingMode (ctx, kCGTextFillStroke);  
    // 绘制文字  
    [@"疯狂Ajax讲义" drawAtPoint:CGPointMake(10 ,130)  
        withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:  
        [UIFont fontWithName:@"Heiti SC" size: 50], NSFontAttributeName,  
        [UIColor magentaColor] , NSForegroundColorAttributeName , nil]];  
    // 定义一个垂直镜像的变换矩阵  
    CGAffineTransform yRevert = CGAffineTransformMake(1, 0, 0, -1, 0, 0);  
    // 设置绘制文本的字体和字体大小  
    CGContextSelectFont (ctx, "Courier New" , 40, kCGEncodingMacRoman);  
    // 为yRevert变换矩阵根据scaleRate添加缩放变换矩阵  
    CGAffineTransform scale = CGAffineTransformScale(yRevert,  
        self.scaleRate, self. scaleRate);  
    // 为scale变换矩阵根据rotateAngle添加旋转变换矩阵  
    CGAffineTransform rotate = CGAffineTransformRotate(scale,   
        M_PI * self.rotateAngle / 180);  
    CGContextSetTextMatrix(ctx, rotate);  // 对CGContextRef绘制文字时应用变换  
    CGContextShowTextAtPoint(ctx, 50, 300, "crazyit.org", 11);  // 绘制文本  
}  
@end 

上面的程序中,前三行粗体字代码直接调用了NSString的drawAtPoint:withAttributes:方法进行绘制–这是一种非常简单的绘制方式。

接下来需要对绘制的文本执行坐标变换,因此程序中先用CGAffineTransformMake()函数定义了一个变换矩阵:(1, 0, 0, -1, 0, 0),该变换矩阵将会对绘制的文本做垂直镜像。此处这么做的原因是因为Quartz 2D本身的坐标系统与UIView控件绘图的坐标系统并不相同,Quartz 2D为了照顾开发者的习惯,已经对坐标系统做过变换,但它在处理文本变换时少了一次垂直镜像变换,导致绘制出来的文本总是上下颠倒的。为了解决这个问题,笔者先对文本做了一次垂直镜像变换。

关于坐标变换的详细理论,后面会详细阐述,读者可参考相关内容。

然后利用CGAffineTransformScale()、CGAffineTransformRotate()增加了缩放、旋转变换,程序最后一行粗体字代码调用CGContextShowTextAtPoint()方法绘制了文本,该文本将会带有缩放和旋转效果。

上面的程序中,在绘制文本时,使用了大量iOS支持的字体,如果读者需要获取iOS支持的所有字体,可调用UIFont类的familyNames类方法,该方法返回一个数组,该数组包含iOS所支持的全部字体。

编译、运行该程序,用户即可通过拖动界面上的UISlider控件来改变文本的缩放、旋转,效果如图12.5所示。
这里写图片描述

设置阴影

前面已经介绍了CGContextRef为设置图形阴影提供了如下两个函数。

void CGContextSetShadow(CGContextRef context, CGSize offset, CGFloat blur):该函数设置阴影在X、Y方向上的偏移,并设置阴影的模糊程度。该函数的offset包含两个CGFloat值,第1个CGFloat值控制阴影在X方向的偏移,如果该值为正,则向右偏移,否则向左偏移;第2个CGFloat值控制阴影在Y方向的偏移,如果该值为正,则向下偏移,否则向上偏移。最后一个blur参数控制阴影的模糊程度,如果blur参数为1,表明阴影几乎不模糊,blur参数越大,阴影越模糊。

void CGContextSetShadowWithColor(CGContextRef context, CGSize offset, CGFloat blur, CGColorRef color):该函数与前一个函数的功能基本相似,只是该函数多了一个属性用于设置阴影颜色。

下面的程序示范了为所绘制的形状添加阴影。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKShadowView类。该程序的控制器类几乎无须修改,只要重写FKShadowView的drawRect:方法,在该方法中设置阴影,并绘制带阴影的形状和文本即可。下面是FKShadowView类的实现代码。

程序清单:codes/12/12.2/ShadowTest/ShadowTest/FKShadowView.m

@implementation FKShadowView  
- (void)drawRect:(CGRect)rect  
{     
    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图的CGContextRef  
    // 使用默认的阴影颜色,阴影向左上角投影,模糊度为5  
    CGContextSetShadow(ctx, CGSizeMake(8, -6), 5);  
    CGContextSetRGBFillColor (ctx, 1, 0, 1, 1);  // 设置填充颜色  
    CGContextSetRGBStrokeColor (ctx, 0, 0, 1, 1);  // 设置线条颜色  
    CGContextSetTextDrawingMode (ctx, kCGTextFill);  // 设置使用填充模式绘制文字  
    // 绘制文字  
    [@"疯狂iOS讲义" drawAtPoint:CGPointMake(10 ,20)  
        withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:  
        [UIFont fontWithName:@"Arial Rounded MT Bold" size: 45]  
        , NSFontAttributeName ,  
         [UIColor magentaColor] , NSForegroundColorAttributeName, nil]];  
    // 设置使用描边模式绘制文字  
    CGContextSetTextDrawingMode (ctx, kCGTextStroke);  
    // 绘制文字  
    [@"疯狂Android讲义" drawAtPoint:CGPointMake(10 ,80)  
        withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:  
        [UIFont fontWithName:@"Heiti SC" size: 40], NSFontAttributeName ,  
        [UIColor magentaColor] , NSForegroundColorAttributeName, nil]];  
    // 使用默认的阴影颜色,阴影向右下角投影,模糊度为20  
    CGContextSetShadowWithColor(ctx, CGSizeMake(10, 8), 10  
        , [[UIColor redColor] CGColor]);  
    CGContextFillRect(ctx, CGRectMake(20 , 150 , 180 , 80));  
    CGContextSetRGBStrokeColor (ctx, 1, 0, 1, 1);  // 设置线条颜色  
    CGContextStrokeRect(ctx, CGRectMake(30 , 260 , 180 , 80));  
} 

上面程序中的第一行粗体字代码使用默认的阴影颜色,通过传入CGSizeMake(8, -6)参数控制阴影向左上角投影;第二行粗体字代码使用红色作为阴影颜色,通过传入CGSizeMake(10, 8)参数控制阴影向右下角投影。编译、运行该程序,即可看到如图12.6所示的效果。

这里写图片描述

使用路径

正如前面提到的,Quartz 2D只提供了绘制矩形、椭圆的函数,并没有直接提供绘制圆角矩形、三角形等几何图形的函数。为了绘制更复杂的图形,必须启用路径。

表12.4列出了Quartz 2D为创建路径提供的函数。

表12.4 创建路径的相关函数
这里写图片描述
除此之外,Quartz 2D还提供了如下函数来获取当前CGContextRef所包含的路径信息。

bool CGContextIsPathEmpty(CGContextRef c):该函数用于判断指定CGContextRef包含的路径是否为空。

CGPoint CGContextGetPathCurrentPoint(CGContextRef c):该函数用于返回指定CGContextRef包含的路径的当前点。

CGRect CGContextGetPathBoundingBox(CGContextRef c):该函数用于返回指定CGContextRef中能完整包围所有路径的最小矩形。

bool CGContextPathContainsPoint(CGContextRef context, CGPoint point, CGPathDrawingMode mode):该函数判断指定CGContextRef包含的路径按指定绘制模式进行绘制时,是否需要绘制point点。

为了在Canvas中使用路径,可按如下步骤进行。

调用CGContextBeginPath()函数开始定义路径。

调用表12.4所示的各种函数添加子路径。

如果路径添加完成,调用CGContextClosePath()函数关闭路径。

调用CGContextDrawPath()、CGContextEOFillPath()、CGContextFillPath()或CGContextStrokePath()函数来填充路径或绘制路径边框即可。在这些方法中,第一个方法可以代替后面的几个方法,第一个方法指定使用特定的模式来绘制图形,它支持如下几种绘制方式。

kCGPathFill:指定填充路径。相当于CGContextFillPath()函数。

kCGPathEOFill:指定采用even-odd模式填充路径。相当于CGContextEOFillPath()函数。

kCGPathStroke:指定只绘制路径。相当于CGContextStrokePath()函数。

kCGPathFillStroke:指定既绘制路径,也填充路径。如果采用这种绘制方式,必须使用该函数。

kCGPathEOFillStroke:指定既绘制路径,也采用even-odd模式填充路径。如果采用这种绘制方式,必须使用该函数。

通过上面介绍的CGContextAddArc()函数,我们可以绘制扇形,下面的程序代码使用循环绘制了10个扇形,而且这10个扇形的透明度逐渐降低。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKArcView类。该程序的控制器类几乎无须修改,只要重写FKArcView的drawRect:方法,在该方法中使用路径绘制扇形即可。

下面是FKArcView类的实现代码。

程序清单:codes/12/12.2/AddArcTest/AddArcTest/FKArcView.m

@implementation FKArcView  

- (void)drawRect:(CGRect)rect  
{  
    CGContextRef ctx = UIGraphicsGetCurrentContext();  
    for(int i = 0 ; i < 10 ; i++)  
    {     
        CGContextBeginPath(ctx);  // 开始定义路径  
        // 添加一段圆弧,最后一个参数1代表逆时针,0代表顺时针  
        CGContextAddArc(ctx , i * 25 , i * 25, (i + 1) * 8 , M_PI * 1.5 , M_PI , 0);  
        CGContextClosePath(ctx);  // 关闭路径  
        CGContextSetRGBFillColor(ctx, 1, 0, 1, (10 - i) * 0.1);  // 设置填充颜色  
        CGContextFillPath(ctx);  // 填充当前路径  
    }  
}  
@end 

上面程序中的粗体字代码绘制了从M_PI * 1.5角度(12点方向)开始,到M_PI角度(9点方向)结束的扇形–在该绘图API中0度代表X轴方向(也就是3点方向)。需要指出的是,关于CGContextAddArc()函数的最后一个参数,官方文档介绍该参数为0代表逆时针,但笔者运行时看到的效果是0代表顺指针(可参见下面的运行效果)。

编译、运行该程序,可以看到如图12.7所示的效果。
这里写图片描述

从上面程序的运行结果来看,相信读者对CGContextAddArc (CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)函数的用法已经比较清楚,该方法的第2、3个参数指定圆弧的圆心,第4个参数用于设置圆弧的半径,第5、6个参数则用于设置圆弧的开始角度、结束角度,最后一个参数用于设置是否顺时针旋转。

可能有读者对CGContextAddArcToPoint(CGContextRef c, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius)函数感到疑惑,该方法也是添加一段圆弧路径。确定这段圆弧的方式是:假设从当前点到P1(x1、y1)绘制一条线条,再从 P1(x1、y1)到P2(x2、y2)绘制一条线条,CGContextAddArcToPoint()则绘制一条同时与上面两条线条相切且半径为radius的圆弧。该函数方法的示意图如图12.8所示。
这里写图片描述

借助CGContextAddArcToPoint()和CGContextAddLineToPoint()函数可以非常容易地实现添加圆角矩形路径的方法(所谓圆角矩形,就是在矩形的每个角都绘制一段90°的圆弧)。除此之外,还有CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)函数用于把绘制点移动到指定位置,借助这些方法还可以绘制任意的多角星。

下面的程序代码将会开发两个自定义函数,一个用于添加圆角矩形,一个用于添加多角星。程序代码如下。

程序清单:codes/12/12.2/PathTest/FKContext.h

#ifndef PathTest_FKContext_h  
#define PathTest_FKContext_h  
/*  
 该方法负责绘制圆角矩形。x1、y2:是圆角矩形左上角的座标;width、height:控制圆角矩行的宽、高;  
radius:控制圆角矩形的四个圆角的半径  
 */  
void CGContextAddRoundRect(CGContextRef c, CGFloat x1 , CGFloat y1  
    , CGFloat width , CGFloat height , CGFloat radius)  
{     
    CGContextMoveToPoint (c, x1 + radius , y1);  // 移动到左上角  
    CGContextAddLineToPoint(c , x1 + width - radius, y1);  // 添加一条连接到右上角的线段  
    // 添加一段圆弧  
    CGContextAddArcToPoint(c , x1 + width , y1, x1 + width, y1 + radius, radius);   // 添加一条连接到右下角的线段  
    CGContextAddLineToPoint(c , x1 + width, y1 + height - radius);  
    // 添加一段圆弧  
    CGContextAddArcToPoint(c , x1 + width, y1 + height  
        , x1 + width - radius , y1 + height , radius);    
    // 添加一条连接到左下角的线段  
    CGContextAddLineToPoint(c , x1 + radius, y1 + height);  
    // 添加一段圆弧  
    CGContextAddArcToPoint(c , x1, y1 + height , x1, y1 + height - radius , radius);    // 添加一条连接到左上角的线段  
    CGContextAddLineToPoint(c , x1 , y1 + radius);   
    // 添加一段圆弧  
    CGContextAddArcToPoint(c , x1 , y1 , x1 + radius , y1 , radius);   
}  
/*  
 该方法负责绘制多角星。n:该参数通常应设为奇数,控制绘制N角星。  
 dx、dy:控制N角星的中心。size:控制N角星的大小  
 */  
void CGContextAddStar(CGContextRef c , NSInteger n,   
    CGFloat dx , CGFloat dy , NSInteger size)  
{  
    CGFloat dig = 4 * M_PI / n ;      
    CGContextMoveToPoint(c , dx , dy + size);  // 移动到指定点  
    for(int i = 1 ; i <= n ; i++)  
    {  
        CGFloat x = sin(i * dig);  
        CGFloat y = cos(i * dig);  
        // 绘制从当前点连接到指定点的线条  
        CGContextAddLineToPoint(c , x * size + dx ,y * size + dy);  
    }  
}  
#endif 

上面两个函数都没有执行任何绘制操作,它们都只是向当前CGContextRef中添加路径,第一个函数用于添加一个圆角矩形路径,第二个函数用于添加一个多角星路径。

上面这个函数库与项目无关,该函数库中包含的两个函数完全可以在任何项目中复用。因此,如果读者以后的项目需要绘制圆角矩形、多角星,只要将该文件复制到项目中,并导入该函数库,即可调用这两个函数添加路径。

实例:绘制任意多角星

下面创建一个应用来绘制圆角矩形和多角星。首先创建一个Single View Application,该Application包含一个应用程序委托代理类、一个视图控制器和配套的Storyboard界面设计文件。将该界面设计文件中最大的View改为使用自定义的FKPathView类。该程序的控制器类几乎无须修改,只要重写FKPathView的drawRect:方法,在该方法中调用FKContext.h中的方法来添加圆角矩形、多角星路径,然后根据需要采用不同的方式绘制这些路径即可。

下面是FKPathView类的实现代码。

程序清单:codes/12/12.2/PathTest/FKPathView.m

#import "FKPathView.h"  
#import "FKContext.h"  
@implementation FKPathView  
- (void)drawRect:(CGRect)rect  
{     
    CGContextRef ctx = UIGraphicsGetCurrentContext();  // 获取绘图CGContextRef  
    CGContextBeginPath(ctx);  // 开始添加路径  
    CGContextAddStar(ctx, 5, 80, 150, 40);  // 添加一个五角星的路径  
    CGContextAddRoundRect(ctx, 10, 30, 150, 70, 14);  // 添加一个圆角矩形的路径  
    CGContextClosePath(ctx);  // 关闭路径  
    CGContextSetRGBStrokeColor(ctx, 1, 1, 0, 1);  // 设置线条颜色  
    CGContextSetLineWidth(ctx, 4);  // 设置线宽  
    CGContextStrokePath(ctx);  // 绘制路径  
    CGContextBeginPath(ctx);  // 开始添加路径  
    CGContextAddStar(ctx, 5, 240, 150, 40);  // 添加一个五角星的路径  
    CGContextAddRoundRect(ctx, 170, 30, 130, 70, 14);  // 添加一个圆角矩形的路径  
    CGContextClosePath(ctx);  // 关闭路径  
    CGContextSetRGBFillColor(ctx, 1, 0, 1, 1);  // 设置填充颜色  
    CGContextDrawPath(ctx, kCGPathFillStroke);  // 采用填充并绘制路径的方式来绘制路径  
    CGContextBeginPath(ctx);  // 开始添加路径  
    CGContextAddStar(ctx, 3, 60, 220, 40);  // 添加一个三角形的路径  
    CGContextClosePath(ctx);  // 关闭路径  
    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);  // 设置填充颜色  
    CGContextFillPath(ctx);  // 填充路径  
    CGContextBeginPath(ctx);  // 开始添加路径  
    CGContextAddStar(ctx, 7, 160, 220, 40);  // 添加一个7角星的路径  
    CGContextClosePath(ctx);  // 关闭路径  
    CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);  // 设置填充颜色  
    CGContextFillPath(ctx);  // 填充路径  
    CGContextBeginPath(ctx);  // 开始添加路径  
    CGContextAddStar(ctx, 9, 260, 220, 40);  // 添加一个9角星的路径    
    CGContextClosePath(ctx);  // 关闭路径  
    CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);  // 设置填充颜色  
    CGContextFillPath(ctx);  // 填充路径  
}  
@end 

上面程序的第一行粗体字代码导入了FKContext.h文件,接下来的粗体字代码多次调用了CGContextAddRoundRect()函数和CGContextAddStar()函数来添加圆角矩形和多角星。编译、运行该程序,即可看到如图12.9所示的效果。

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值