iOS Objective-C 自定义饼状图

前些天项目中使用了饼状图,现在开发完做下记录。

//

//  PieChartView.h

#import <UIKit/UIKit.h>

@protocol PieChartViewDelegate

@required

-(NSInteger)getChartRows;

-(NSString*)getChartName:(NSInteger)row;

-(CGFloat)getChartRate:(NSInteger)row;

-(void)didSelectChartView:(NSInteger)row;

@end

@interface PieChartView : UIView

@property(nonatomic,weak)id<PieChartViewDelegate> delegate;

@end

//

//  PieChartView.m

//  NbanClient

//


#import "PieChartView.h"


static NSArray *chartColors;

@interface PieChartView()<CAAnimationDelegate>

{

    NSInteger _currentIndex;

    BOOL _isShowAnimate;//判断自身是否处于外部

    UIView *_circleView;//中心圆形

}

@property(nonatomic,copy)NSArray *chartColors;

@property(nonatomic,strong)NSMutableArray *chartRanColors;

@property(nonatomic,strong)NSMutableArray *chartPaths;

@property(nonatomic,strong)NSMutableArray *chartLinePaths;

@property(nonatomic,strong)NSMutableArray<UILabel*> *chartLabels;

@property(nonatomic,strong)NSMutableArray<NSNumber*> *labelWidths;

@property(nonatomic,strong)CAShapeLayer*  animateBackLayer;


@end


@implementation PieChartView


- (instancetype)init

{

    self = [super init];

    if (self) {

        self.backgroundColor = UIColorFromRGB(0xf2f2f2);

        

        UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(taphandle:)];

        [self addGestureRecognizer:gesture];

        _chartPaths = [[NSMutableArray alloc]init];

        _chartLinePaths = [[NSMutableArray alloc]init];

        _chartRanColors = [[NSMutableArray alloc]init];

        _chartLabels = [[NSMutableArray alloc]init];

        _labelWidths = [[NSMutableArray alloc]init];

    }

    return self;

}


- (void)drawRect:(CGRect)rect {

    [_chartLinePaths removeAllObjects];

    [_chartPaths removeAllObjects];

    [_chartRanColors removeAllObjects];

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetStrokeColorWithColor(context, [UIColorFromRGB(0xffffff) CGColor]);

    CGContextSetFillColorWithColor(context, [UIColorFromRGB(0xffffff) CGColor]);

    CGContextAddRect(context, CGRectMake(AdapterW(16), 0, screen_width-AdapterW(32), rect.size.height));

    CGContextDrawPath(context, kCGPathFillStroke);

    if (_delegate) {

        NSInteger count = [_delegate getChartRows];

        NSMutableArray *currentChartColors = [[NSMutableArray alloc]initWithArray:self.chartColors];

        CGFloat allRate = 0;

        for (int i = 0; i<count; i++) {

            if ([currentChartColors count] == 0) {

                currentChartColors = [[NSMutableArray alloc]initWithArray:self.chartColors];

            }

            UIBezierPath *path = [UIBezierPath bezierPath];

            NSInteger ran =  random()%[currentChartColors count];

            [_chartRanColors addObject:currentChartColors[ran]];

            CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] CGColor]);

            CGContextSetFillColorWithColor(context, [currentChartColors[ran] CGColor]);

            CGFloat rate = [_delegate getChartRate:i];

            [path moveToPoint:self.center];

            [path addArcWithCenter:self.center radius:75 startAngle:M_PI*2*allRate endAngle:M_PI*2*(rate+allRate) clockwise:YES];

           // CGContextMoveToPoint(context, rect.size.width/2, rect.size.height/2);

            //CGContextAddArc(context, rect.size.width/2, rect.size.height/2, 75, M_PI*2*allRate, M_PI*2*(rate+allRate), NO);

            [path closePath];

            CGContextAddPath(context, [path CGPath]);

            CGContextDrawPath(context, kCGPathFillStroke);

            [_chartPaths addObject:path];

            UIBezierPath *linePath = [UIBezierPath bezierPath];

            CGContextSetStrokeColorWithColor(context, [currentChartColors[ran] CGColor]);

            CGPoint midPoint = [self getArcMidPoint:rate/2+allRate];

            CGPoint endPoint = [self getArcEndMidPoint:rate/2+allRate district:[self isDistrctLine:rate/2+allRate] endRadius:20];

            [linePath moveToPoint:CGPointMake(midPoint.x, midPoint.y)];

            //CGContextMoveToPoint(context, midPoint.x, midPoint.y);

            if([self isDistrctLine:rate/2+allRate]){

                [linePath addLineToPoint:endPoint];

               // CGContextAddLineToPoint(context, endPoint.x, endPoint.y);

            }

            else {

                CGPoint controlPoint = [self getArcControlMidPoint:rate/2+allRate];

                [linePath addQuadCurveToPoint:endPoint controlPoint:controlPoint];

                //CGContextAddQuadCurveToPoint(context, controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);

            }

            CGContextAddPath(context, [linePath CGPath]);

            CGContextStrokePath(context);

            [_chartLinePaths addObject:linePath];

    

            CGFloat rateWidth = [[APPManager defaultManager] getTextWidth:[NSString stringWithFormat:@"%.2f%%",[_delegate getChartRate:i]*100] setHeight:20 setFont:[UIFont systemFontOfSize:AdapterF(9)]];

            NSString *name = [NSString stringWithFormat:@"%@\n%.2f%%",[_delegate getChartName:i],[_delegate getChartRate:i]*100];

            CGFloat labelWidth = [[APPManager defaultManager] getTextWidth:name setHeight:20 setFont:[UIFont systemFontOfSize:AdapterF(9)]];

            if (labelWidth<rateWidth) {

                labelWidth = rateWidth;

            }

            if (midPoint.x>endPoint.x) {

                if (endPoint.x-8<labelWidth) {

                    labelWidth = endPoint.x-8;

                }

            }

            else {

                if (self.frame.size.width-endPoint.x-8<labelWidth) {

                    labelWidth = self.frame.size.width-endPoint.x-8;

                }

            }

            [_labelWidths addObject:[NSNumber numberWithFloat:labelWidth]];

            UILabel*label = [[UILabel alloc]init];

            //label.backgroundColor = UIColorFromRGB(0xff0000);

            label.textColor = UIColorFromRGB(0x999999);

            label.font = [UIFont systemFontOfSize:AdapterF(8)];

            label.text = name;

            label.textAlignment = NSTextAlignmentCenter;

            label.numberOfLines = 0;

            [self addSubview:label];

            [label mas_makeConstraints:^(MASConstraintMaker *make) {

                if (midPoint.x>endPoint.x) {

                    make.right.mas_equalTo(-(self.frame.size.width-endPoint.x)-8);

                    

                    //make.left.mas_equalTo(0);

                }

                else {

                    make.left.mas_equalTo(endPoint.x+8);

                   // make.right.mas_equalTo(0);

                }

                make.top.mas_equalTo(endPoint.y-10);

                make.height.mas_equalTo(25);

                make.width.mas_equalTo(labelWidth);

            }];

            [_chartLabels addObject:label];

            allRate = allRate + rate;

            [currentChartColors removeObjectAtIndex:ran];

        }

        UIView *circleView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 13, 13)];

        circleView.layer.cornerRadius = 6.5;

        circleView.backgroundColor = [UIColor whiteColor];

        circleView.center = self.center;

        [self addSubview:circleView];

        [circleView bringSubviewToFront:circleView];

//        CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] CGColor]);

//        CGContextSetFillColorWithColor(context, [[UIColor whiteColor] CGColor]);

//        CGContextAddArc(context, rect.size.width/2, rect.size.height/2, 6.5, 0, M_PI*2, NO);

//        CGContextDrawPath(context, kCGPathFillStroke);

        

    }

    

    

}

- (NSArray*)chartColors{

    if (!_chartColors) {

         _chartColors = [[NSArray alloc]initWithObjects:UIColorFromRGB(0xFFEF6B),UIColorFromRGB(0xFF814A),UIColorFromRGB(0x56CA77),UIColorFromRGB(0xFFA6F9),UIColorFromRGB(0x45A3FC),UIColorFromRGB(0xA6A9FF),UIColorFromRGB(0x47CBCA),UIColorFromRGB(0xFFC491),UIColorFromRGB(0xF0657D),UIColorFromRGB(0xCC8CF5),UIColorFromRGB(0xB7E89E),UIColorFromRGB(0xFF7F74), nil];

    }

    return _chartColors;

}


/**

 获取扇形上中点坐标


 @param arc 占有率

 @return 坐标 CGPoint

 */

- (CGPoint)getArcMidPoint:(float)arc

{

    CGFloat angle = M_PI*2.0*arc;//将进度转换成弧度

    float radius = 75;//半径

    int index = (angle)/M_PI_2;//用户区分在第几象限内

    float needAngle = angle - index*M_PI_2;//用于计算正弦/余弦的角度

    float x = 0,y = 0;

    switch (index) {

        case 3:

          //  NSLog(@"第一象限");

            x = self.frame.size.width/2 + sinf(needAngle)*radius;

            y = self.frame.size.height/2 - cosf(needAngle)*radius;

            break;

        case 0:

           // NSLog(@"第二象限");

            x = self.frame.size.width/2  + cosf(needAngle)*radius;

            y = self.frame.size.height/2 + sinf(needAngle)*radius;

            break;

        case 1:

          //  NSLog(@"第三象限");

            x = self.frame.size.width/2 - sinf(needAngle)*radius;

            y = self.frame.size.height/2  + cosf(needAngle)*radius;

            break;

        case 2:

          //  NSLog(@"第四象限");

            x = self.frame.size.width/2 - cosf(needAngle)*radius;

            y = self.frame.size.height/2 - sinf(needAngle)*radius;

            break;

            

        default:

            break;

    }

    return  CGPointMake(x, y);

}

/**

 获取扇形上直线坐标

 

 @param arc 占有率

 @return 坐标 CGPoint

 */

- (CGPoint)getArcEndMidPoint:(float)arc district:(BOOL)isDistrict endRadius:(CGFloat)endRadius

{

    CGFloat angle = M_PI*2.0*arc;//将进度转换成弧度

    float radius = 75+endRadius;//半径

    int index = (angle)/M_PI_2;//用户区分在第几象限内

    float needAngle = angle - index*M_PI_2;//用于计算正弦/余弦的角度

    float x = 0,y = 0;

    switch (index) {

        case 3:

           // NSLog(@"第一象限");

            if (isDistrict) {

                x = self.frame.size.width/2 + sinf(needAngle)*radius;

                y = self.frame.size.height/2 - cosf(needAngle)*radius;

            }

            else {

                x = self.frame.size.width/2 + sinf(needAngle)*radius+5;

                y = self.frame.size.height/2 - cosf(needAngle)*radius+5;

            }

           

            break;

        case 0:

           // NSLog(@"第二象限");

            if (isDistrict) {

                x = self.frame.size.width/2 + cosf(needAngle)*radius;

                y = self.frame.size.height/2 + sinf(needAngle)*radius;

            }

            else {

                x = self.frame.size.width/2 + cosf(needAngle)*radius+10;

                y = self.frame.size.height/2 + sinf(needAngle)*radius-10;

            }

            

            break;

        case 1:

           // NSLog(@"第三象限");

            if (isDistrict) {

                x = self.frame.size.width/2 - sinf(needAngle)*radius;

                y = self.frame.size.height/2 + cosf(needAngle)*radius;

            }

            else {

                x = self.frame.size.width/2 - sinf(needAngle)*radius-10;

                y = self.frame.size.height/2 + cosf(needAngle)*radius-10;

            }

            break;

        case 2:

           // NSLog(@"第四象限");

            if (isDistrict) {

                x = self.frame.size.width/2 - cosf(needAngle)*radius;

                y = self.frame.size.height/2 - sinf(needAngle)*radius;

            }

            else {

                x = self.frame.size.width/2 - cosf(needAngle)*radius-10;

                y = self.frame.size.height/2 - sinf(needAngle)*radius+10;

            }

            break;

            

        default:

            break;

    }

    return  CGPointMake(x, y);

}


/**

 获取二次贝塞尔曲线控制点


 @param arc 占有率

 @return CGPoint

 */

- (CGPoint)getArcControlMidPoint:(float)arc

{

    CGFloat angle = M_PI*2.0*arc;//将进度转换成弧度

    float radius = 75+10;//半径

    int index = (angle)/M_PI_2;//用户区分在第几象限内

    float needAngle = angle - index*M_PI_2;//用于计算正弦/余弦的角度

    float x = 0,y = 0;

    switch (index) {

        case 3:

           // NSLog(@"第一象限");

            

            x = self.frame.size.width/2 + sinf(needAngle)*radius;

            y = self.frame.size.height/2 - cosf(needAngle)*radius;

            

            break;

        case 0:

           // NSLog(@"第二象限");

            

            x = self.frame.size.width/2 + cosf(needAngle)*radius;

            y = self.frame.size.height/2 + sinf(needAngle)*radius;

           

            break;

        case 1:

           // NSLog(@"第三象限");

           

            x = self.frame.size.width/2 - sinf(needAngle)*radius;

            y = self.frame.size.height/2 + cosf(needAngle)*radius;

            

            break;

        case 2:

           // NSLog(@"第四象限");

        

            x = self.frame.size.width/2 - cosf(needAngle)*radius;

            y = self.frame.size.height/2 - sinf(needAngle)*radius;

           

            break;

            

        default:

            break;

    }

    return  CGPointMake(x, y);

}


/**

 判断线是否做弯曲处理


 @param rate 占有率

 @return BOOL

 */

- (BOOL)isDistrctLine:(float)rate{

    CGFloat angle = M_PI*2.0*rate;//将进度转换成弧度

    if ((angle>0&&angle<=M_PI_4)||(angle>M_PI_4+M_PI_2&&angle<=M_PI+M_PI_4)||(angle>M_PI_4+M_PI_2+M_PI&&angle<=M_PI*2)) {

        return YES;

    }

    return NO;

}

- (NSInteger)getCurrentIndex:(CGPoint)point{

    for (int i=0; i<[_chartPaths count]; i++) {

        if ([_chartPaths[i] containsPoint:point]) {

            return i;

        }

    }

    return -1;

}

- (float)getMidRate:(NSInteger)index{

    float currentRate = 0 ;

    for (int i = 0 ; i<index+1; i++) {

        CGFloat rate = [_delegate getChartRate:i];

        if (i==index) {

           currentRate = rate/2 + currentRate;

            return currentRate ;

        }

        currentRate = rate + currentRate;

    }

    return currentRate;

}

- (float)getRate:(NSUInteger)index{

    float currentRate = 0 ;

    for (int i = 0 ; i<index+1; i++) {

        CGFloat rate = [_delegate getChartRate:i];

        if (i==index) {

            currentRate = rate + currentRate;

            return currentRate ;

        }

        currentRate = rate + currentRate;

    }

    return currentRate;

}

- (void)taphandle:(UITapGestureRecognizer *)sender{

    CGPoint touchPoint = [sender locationInView:self];

    NSInteger oldIndex = _currentIndex;

    _currentIndex = [self getCurrentIndex:touchPoint];

    if (_currentIndex>=0) {

        if (self.animateBackLayer) {

            [self.animateBackLayer removeFromSuperlayer];

            

        }

        if (oldIndex>=0) {

            _chartLabels[oldIndex].textColor = UIColorFromRGB(0x999999);

            _chartLabels[oldIndex].font = [UIFont systemFontOfSize:AdapterF(8)];

            CGPoint midPoint = [self getArcMidPoint:[self getMidRate:oldIndex]];

            CGPoint endlinePoint = [self getArcEndMidPoint:[self getMidRate:oldIndex] district:[self isDistrctLine:[self getMidRate:oldIndex]] endRadius:20];

            

            [_chartLabels[oldIndex] mas_remakeConstraints:^(MASConstraintMaker *make) {

                if (midPoint.x>endlinePoint.x) {

                    make.right.mas_equalTo(-(self.frame.size.width-endlinePoint.x)-8);

                    //make.left.mas_equalTo(0);

                }

                else {

                    make.left.mas_equalTo(endlinePoint.x+8);

                    // make.right.mas_equalTo(0);

                }

                make.top.mas_equalTo(endlinePoint.y-8);

                make.height.mas_equalTo(25);

                make.width.mas_equalTo(_labelWidths[oldIndex]);


            }];

        }

        

        if (!(oldIndex ==_currentIndex&&_isShowAnimate)) {

            _chartLabels[_currentIndex].textColor = _chartRanColors[_currentIndex];

            _chartLabels[_currentIndex].font = [UIFont boldSystemFontOfSize:AdapterF(9)];

            [self bringSubviewToFront:_chartLabels[_currentIndex]];

        }

        

        UIBezierPath *shadowPath = [UIBezierPath bezierPath];

        [shadowPath moveToPoint:self.center];

        //如果目标layer小于8度则shadow用本身

        if((M_PI*2*([self getRate:_currentIndex]-[_delegate getChartRate:_currentIndex])+(M_PI*4/180))<M_PI*2*[self getRate:_currentIndex]-(M_PI*4/180)){

        [shadowPath addArcWithCenter:self.center radius:79 startAngle:(M_PI*2*([self getRate:_currentIndex]-[_delegate getChartRate:_currentIndex])+(M_PI*2/180)) endAngle:M_PI*2*[self getRate:_currentIndex]-(M_PI*2/180) clockwise:YES];//和layer缩小4度

        }

        else {

            [shadowPath addArcWithCenter:self.center radius:79 startAngle:(M_PI*2*([self getRate:_currentIndex]-[_delegate getChartRate:_currentIndex])) endAngle:M_PI*2*[self getRate:_currentIndex] clockwise:YES];

        }

        _animateBackLayer = [[CAShapeLayer alloc]init];

        _animateBackLayer.frame = self.bounds;

        _animateBackLayer.fillColor = [[UIColor whiteColor] CGColor];

        _animateBackLayer.path = [_chartPaths[_currentIndex] CGPath];

        CAShapeLayer* _animateBackLineLayer = [[CAShapeLayer alloc]init];

        _animateBackLineLayer.lineWidth = 2;

        _animateBackLineLayer.frame = self.bounds;

        _animateBackLineLayer.fillColor = [[UIColor whiteColor] CGColor];

        _animateBackLineLayer.strokeColor = [[UIColor whiteColor] CGColor];

        _animateBackLineLayer.path = [_chartLinePaths[_currentIndex] CGPath];

        CAShapeLayer *animateLayer = [[CAShapeLayer alloc]init];

        animateLayer.frame = self.bounds;

        animateLayer.path = [_chartPaths[_currentIndex] CGPath];

        animateLayer.fillColor = [_chartRanColors[_currentIndex] CGColor];

        CAShapeLayer *animateShadowLayer = [[CAShapeLayer alloc]init];

        animateShadowLayer.frame = self.bounds;

        animateShadowLayer.path = [shadowPath CGPath];

        animateShadowLayer.fillColor = [_chartRanColors[_currentIndex] CGColor];

        animateShadowLayer.opacity = 0.3;

        CAShapeLayer *animateLineLayer = [[CAShapeLayer alloc]init];

        animateLineLayer.fillColor = [[UIColor whiteColor] CGColor];

        animateLineLayer.frame = self.bounds;

        animateLineLayer.path = [_chartLinePaths[_currentIndex] CGPath];

        animateLineLayer.strokeColor = [_chartRanColors[_currentIndex] CGColor];

        [_animateBackLayer addSublayer:_animateBackLineLayer];

        [_animateBackLayer addSublayer:animateShadowLayer];

        [_animateBackLayer addSublayer:animateLayer];

        [_animateBackLayer addSublayer:animateLineLayer];

        [self.layer insertSublayer:self.animateBackLayer atIndex:0];

        CGPoint endPoint = [self getArcEndMidPoint:[self getMidRate:_currentIndex] district:YES endRadius:7];

        CGPoint startPoint = [self getArcMidPoint:[self getMidRate:_currentIndex]];

        CABasicAnimation *ani = [[CABasicAnimation alloc]init];

        ani.fillMode = kCAFillModeForwards;

        ani.keyPath = @"position";

        if (oldIndex == _currentIndex && _isShowAnimate) {

            ani.toValue = [NSValue valueWithCGPoint:self.center];

           

        }else {

            

            ani.fromValue = [NSValue valueWithCGPoint:self.center];

            ani.toValue = [NSValue valueWithCGPoint:CGPointMake(self.center.x+endPoint.x-startPoint.x, self.center.y+endPoint.y-startPoint.y)];

        }

        ani.delegate = self;

        ani.duration = 0.1;

        ani.removedOnCompletion = NO;

        [animateLayer addAnimation:ani forKey:nil];

        endPoint = [self getArcEndMidPoint:[self getMidRate:_currentIndex] district:YES endRadius:11];

        startPoint = [self getArcMidPoint:[self getMidRate:_currentIndex]];

        CABasicAnimation *lineAni = [[CABasicAnimation alloc]init];

        lineAni.fillMode = kCAFillModeForwards;

        lineAni.keyPath = @"position";

        if (oldIndex == _currentIndex && _isShowAnimate) {

            lineAni.toValue = [NSValue valueWithCGPoint:self.center];

           

        }else {

           

            lineAni.fromValue = [NSValue valueWithCGPoint:self.center];

            lineAni.toValue = [NSValue valueWithCGPoint:CGPointMake(self.center.x+endPoint.x-startPoint.x, self.center.y+endPoint.y-startPoint.y)];

        }

        lineAni.delegate = self;

        lineAni.duration = 0.1;

        lineAni.removedOnCompletion = NO;

        [animateLineLayer addAnimation:lineAni forKey:nil];

        [animateShadowLayer addAnimation:ani forKey:nil];

        

        

        CGPoint midPoint = [self getArcMidPoint:[self getMidRate:_currentIndex]];

        CGPoint endlinePoint = (oldIndex == _currentIndex && _isShowAnimate)?[self getArcEndMidPoint:[self getMidRate:_currentIndex] district:[self isDistrctLine:[self getMidRate:_currentIndex]] endRadius:20]:[self getArcEndMidPoint:[self getMidRate:_currentIndex] district:[self isDistrctLine:[self getMidRate:_currentIndex]] endRadius:30];

        CGFloat rateWidth = [[APPManager defaultManager] getTextWidth:[NSString stringWithFormat:@"%.2f%%",[_delegate getChartRate:_currentIndex]*100] setHeight:30 setFont:[UIFont systemFontOfSize:AdapterF(10)]];

        NSString *name = [NSString stringWithFormat:@"%@\n%.2f%%",[_delegate getChartName:_currentIndex],[_delegate getChartRate:_currentIndex]*100];

        CGFloat labelWidth = [[APPManager defaultManager] getTextWidth:name setHeight:30 setFont:[UIFont systemFontOfSize:AdapterF(10)]];

        if (labelWidth<rateWidth) {

            labelWidth = rateWidth;

        }

        if (midPoint.x>endlinePoint.x) {

            if (endPoint.x-23<labelWidth) {

                labelWidth = endlinePoint.x-23;

            }

        }

        else {

            if (self.frame.size.width-endlinePoint.x-23<labelWidth) {

                labelWidth = self.frame.size.width-endPoint.x-23;

            }

        }

        [_chartLabels[_currentIndex] mas_remakeConstraints:^(MASConstraintMaker *make) {

            if (midPoint.x>endlinePoint.x) {

                make.right.mas_equalTo(-(self.frame.size.width-endlinePoint.x)-8);

                //make.left.mas_equalTo(0);

            }

            else {

                make.left.mas_equalTo(endlinePoint.x+8);

                // make.right.mas_equalTo(0);

            }

            make.top.mas_equalTo(endlinePoint.y-15);

            make.height.mas_equalTo(30);

            make.width.mas_equalTo(labelWidth);

        }];

        [UIView animateWithDuration:0.1 animations:^{

            [self layoutIfNeeded];

        }];

        if (oldIndex == _currentIndex && _isShowAnimate) {

            _isShowAnimate = false;

            _currentIndex = -1;

        }else {

            _isShowAnimate = true;

        }

    }

    else {

        _currentIndex = oldIndex;

    }

    

}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {

    if (flag&&!_isShowAnimate) {

        [self.animateBackLayer removeFromSuperlayer];

        self.animateBackLayer = nil;

    }

}

@end



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值