ios学习开源代码系列(二)ifengNewsOrderDemo

在code4app上面找到了ifengNewsOrderDemo这个开源代码,实现的是类似网易新闻的订阅功能,挺有趣的,于是找来研读一下,发现这工程还是有点复杂,能够慢慢研究的地方挺多

首先来看看应用的运行效果

当点击右上角的按钮的时候,就会自上而下淡入一个订阅界面


此时我们可以拖动界面上的按钮,或者点击右下角的按钮返回上一界面


下面看看工程组件之间的关系





首先看看RootViewController做了些什么,从viewDidLoad入手

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    
    
    OrderButton * orderButton = [OrderButton orderButtonWithViewController:self titleArr:[NSArray arrayWithObjects:KChannelList, nil] urlStringArr:[NSArray arrayWithObjects:KChannelUrlStringList, nil]];
    [self.view addSubview:orderButton];
    [orderButton addTarget:self action:@selector(orderViewOut:) forControlEvents:UIControlEventTouchUpInside];

}
代码里面就是在view上创建了一个按钮(右上角那个),然后绑定了一个点击事件,我们看看那个点事件

- (void)orderViewOut:(id)sender{
    
    OrderButton * orderButton = (OrderButton *)sender;
    if([[orderButton.vc.view subviews] count]>1){
        //        [[[orderButton.vc.view subviews]objectAtIndex:1] removeFromSuperview];
        NSLog(@"%@",[orderButton.vc.view subviews]);
    }
    OrderViewController * orderVC = [[[OrderViewController alloc] init] autorelease];
    orderVC.titleArr = orderButton.titleArr;
    orderVC.urlStringArr = orderButton.urlStringArr;
    UIView * orderView = [orderVC view];
    [orderView setFrame:CGRectMake(0, - orderButton.vc.view.bounds.size.height, orderButton.vc.view.bounds.size.width, orderButton.vc.view.bounds.size.height)];
    [orderView setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]];
    [orderVC.backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:orderView];
    [self addChildViewController:orderVC];
    [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        [orderView setFrame:CGRectMake(0, 0, orderButton.vc.view.bounds.size.width, orderButton.vc.view.bounds.size.height)];
        
    } completion:^(BOOL finished){
        
    }];
    
}
方法首先新建了OrderViewController,然后把他加入到RootViewController的ChildViewController里面,最后通过动画效果把view展示出来,view开始是放置在屏幕外面的,然后才通过Animation再把他显示出来


还有在“我的订阅”视图右下方的返回按键也是类似的原理

- (void)backAction{
    OrderViewController * orderVC = [self.childViewControllers objectAtIndex:0];
    [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        [orderVC.view setFrame:CGRectMake(0, - self.view.bounds.size.height, self.view.bounds.size.width, self.view.bounds.size.height)];
        
    } completion:^(BOOL finished){
        NSString * string = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString * filePath = [string stringByAppendingString:@"/modelArray0.swh"];
        NSString * filePath1 = [string stringByAppendingString:@"/modelArray1.swh"];
        NSMutableArray * modelArr = [NSMutableArray array];
        
        
        for (TouchView * touchView in orderVC->_viewArr1) {
            [modelArr addObject:touchView.touchViewModel];
        }
        NSData * data = [NSKeyedArchiver archivedDataWithRootObject:modelArr];
        [data writeToFile:filePath atomically:YES];
        [modelArr removeAllObjects];
        for (TouchView * touchView in orderVC->_viewArr2) {
            [modelArr addObject:touchView.touchViewModel];
        }
        data = [NSKeyedArchiver archivedDataWithRootObject:modelArr];
        [data writeToFile:filePath1 atomically:YES];
        [[[self.childViewControllers  objectAtIndex:0] view] removeFromSuperview];
        [orderVC removeFromParentViewController];
        
    }];
    
}
通过Animation把view推到可见区域的上方,然后把OrderViewController从ParentViewController中删除掉

所以OrderViewController是在每次显示的时候被创建,又在每次消失时删除


在看OrderViewController前我们给界面划分一下逻辑关系,如下图


下面我们来看看OrderViewController的viewDidLoad方法

第一步首先是把“订阅”和“跟多频道”的数据序列化到Library目录下

[super viewDidLoad];
	// Do any additional setup after loading the view.
    
    NSString * string = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSLog(@"%@", string);
    NSString * filePath = [string stringByAppendingString:@"/modelArray0.swh"];
    NSString * filePath1 = [string stringByAppendingString:@"/modelArray1.swh"];
    
    
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        NSArray * channelListArr = self.titleArr;
        NSArray * channelUrlStringListArr = self.urlStringArr;
        NSMutableArray * mutArr = [NSMutableArray array];
        for (int i = 0; i < [channelListArr count]; i++) {
            NSString * title = [channelListArr objectAtIndex:i];
            NSString * urlString = [channelUrlStringListArr objectAtIndex:i];
            TouchViewModel * touchViewModel = [[TouchViewModel alloc] initWithTitle:title urlString:urlString];
            [mutArr addObject:touchViewModel];
            [touchViewModel release];
            if (i == KDefaultCountOfUpsideList - 1) {
                NSData * data = [NSKeyedArchiver archivedDataWithRootObject:mutArr];
                [data writeToFile:filePath atomically:YES];
                [mutArr removeAllObjects];
            }
            else if(i == [channelListArr count] - 1){
                NSData * data = [NSKeyedArchiver archivedDataWithRootObject:mutArr];
                [data writeToFile:filePath1 atomically:YES];
            }
            
        }
    }

然后定义了两个model数组和两个view数组

    _modelArr1 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    NSArray * modelArr2 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath1];
    
    //区域1的views
    _viewArr1 = [[NSMutableArray alloc] init];
    //区域2的views
    _viewArr2 = [[NSMutableArray alloc] init];

接下来是绘制“我的订阅”和“更多频道”的label

    _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(110, 25, 100, 40)];
    _titleLabel.text = @"我的订阅";
    [_titleLabel setTextAlignment:NSTextAlignmentCenter];
    [_titleLabel setTextColor:[UIColor colorWithRed:187/255.0 green:1/255.0 blue:1/255.0 alpha:1.0]];
    [self.view addSubview:_titleLabel];
    
    
    
    _titleLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(110, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, 100, 20)];
    _titleLabel2.text = @"更多频道";
    [_titleLabel2 setFont:[UIFont systemFontOfSize:10]];
    [_titleLabel2 setTextAlignment:NSTextAlignmentCenter];
    [_titleLabel2 setTextColor:[UIColor grayColor]];
    [self.view addSubview:_titleLabel2];

最后是分别循环绘制区域1和区域2的views

    for (int i = 0; i < _modelArr1.count; i++) {
        TouchView * touchView = [[TouchView alloc] initWithFrame:CGRectMake(KTableStartPointX + KButtonWidth * (i%5), KTableStartPointY + KButtonHeight * (i/5), KButtonWidth, KButtonHeight)];
        [touchView setBackgroundColor:[UIColor colorWithRed:210/255.0 green:210/255.0 blue:210/255.0 alpha:1.0]];
        
        [_viewArr1 addObject:touchView];
        [touchView release];
        touchView->_array = _viewArr1;
        if (i == 0) {
            [touchView.label setTextColor:[UIColor colorWithRed:187/255.0 green:1/255.0 blue:1/255.0 alpha:1.0]];
        }
        else{
            
            [touchView.label setTextColor:[UIColor colorWithRed:99/255.0 green:99/255.0 blue:99/255.0 alpha:1.0]];
        }
        touchView.label.text = [[_modelArr1 objectAtIndex:i] title];
        [touchView.label setTextAlignment:NSTextAlignmentCenter];
        [touchView setMoreChannelsLabel:_titleLabel2];
        touchView->_viewArr11 = _viewArr1;
        touchView->_viewArr22 = _viewArr2;
        [touchView setTouchViewModel:[_modelArr1 objectAtIndex:i]];
        
        [self.view addSubview:touchView];
    }
    
    
    for (int i = 0; i < modelArr2.count; i++) {
        TouchView * touchView = [[TouchView alloc] initWithFrame:CGRectMake(KTableStartPointX + KButtonWidth * (i%5), KTableStartPointY + [self array2StartY] * KButtonHeight + KButtonHeight * (i/5), KButtonWidth, KButtonHeight)];
        
        [touchView setBackgroundColor:[UIColor colorWithRed:210/255.0 green:210/255.0 blue:210/255.0 alpha:1.0]];
        
        [_viewArr2 addObject:touchView];
        touchView->_array = _viewArr2;
        
        touchView.label.text = [[modelArr2 objectAtIndex:i] title];
        [touchView.label setTextColor:[UIColor colorWithRed:99/255.0 green:99/255.0 blue:99/255.0 alpha:1.0]];
        [touchView.label setTextAlignment:NSTextAlignmentCenter];
        [touchView setMoreChannelsLabel:_titleLabel2];
        touchView->_viewArr11 = _viewArr1;
        touchView->_viewArr22 = _viewArr2;
        [touchView setTouchViewModel:[modelArr2 objectAtIndex:i]];
        
        [self.view addSubview:touchView];
        
        [touchView release];
        
    }

最后的重点是TouchView,TouchView里面做了许多工作,主要包括调整分组和完成动画效果

首先我们看看头文件

#import <UIKit/UIKit.h>
@class TouchViewModel;
@interface TouchView : UIImageView
{
    CGPoint _point;
    CGPoint _point2;
    //代表是否在移动中 0否 1是
    NSInteger _sign;
    @public
    
    //所在的view的集合(“已订阅”的view集合或者“跟多频道”的view集合)
    NSMutableArray * _array;
    //“已订阅”的view集合
    NSMutableArray * _viewArr11;
    //“跟多频道”的view集合
    NSMutableArray * _viewArr22;
}
@property (nonatomic,retain) UILabel * label;
@property (nonatomic,retain) UILabel * moreChannelsLabel;
//每个TouchView对应一个Model
@property (nonatomic,retain) TouchViewModel * touchViewModel;
@end

TouchView用了_point和_point2来标识一些位置信息,用_sign表示TouchView是出于移动状态还是静止状态,利用_array来标识自己属于哪个区域,另外也用_viewArr11和_viewArr22来引用两个区域的view的集合


接下来是看看它的实现,首先是一些初始化操作

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.multipleTouchEnabled = YES;
        self.userInteractionEnabled = YES;
        UILabel *l = [[UILabel alloc] initWithFrame:CGRectZero];
        self.label = l;
        [l release];
        _sign = 0;
        
    }
    return self;
}

- (void)layoutSubviews{
    //往TouchView内部添加label
    [self.label setFrame:CGRectMake(1, 1, KButtonWidth - 2, KButtonHeight - 2)];
    [self.label setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]];
    
    [self addSubview:self.label];
    
}

初始化的时候它会往自己添加一个label,用来显示文字标题,如“头条”、“热点”等等


重点是touchesBegan、touchesMoved和touchesEnded方法,相关解析我都写了注释

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    
    UITouch * touch = [touches anyObject];
    //相对于内部的位移
    _point = [touch locationInView:self];
    //相对于父视图的位移
    _point2 = [touch locationInView:self.superview];
    //把当前点击的view放到最顶层,当移动的时候,就不会被其他按钮阻挡了
    [self.superview exchangeSubviewAtIndex:[self.superview.subviews indexOfObject:self] withSubviewAtIndex:[[self.superview subviews] count] - 1];
}

如果用户只是点击了一下touchview,那么就不会调用touchesMoved方法,直接就调用touchesEnded方法

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self.superview];
    int a = point.x - _point.x;
    int b = point.y - _point.y;
    
    
    if (![self.label.text isEqualToString:@"头条"]) {
        [self.label setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]];
        [self setImage:nil];
        
        //这里_sign是用来判断touchesEnded之前touchView是出于什么状态的,如果用户只是点击了一下,并没有拖动,那么_sign就为0,并且touchView应该添加到相反区域上,如果之前用户是拖动的,那么_sign就为1,这时候不能简单地把touchView添加到相反区域上,因为用户有可能没有拖动到相反区域上,而是在原来的区域里面释放
        if (_sign == 0) {
            if (_array == _viewArr11) {
                [_viewArr11 removeObject:self];
                [_viewArr22 insertObject:self atIndex:_viewArr22.count];
                _array = _viewArr22;
                [self animationAction];
            }
            else if ( _array == _viewArr22){
                [_viewArr22 removeObject:self];
                [_viewArr11 insertObject:self atIndex:_viewArr11.count];
                _array = _viewArr11;
                [self animationAction];
            }
        }
        
        //此时_sign=1,那么就
        else if (([self buttonInArrayArea1:_viewArr11 Point:point] || [self buttonInArrayArea2:_viewArr22 Point:point])&&!(point.x - _point.x > KTableStartPointX && point.x - _point.x < KTableStartPointX + KButtonWidth && point.y - _point.y > KTableStartPointY && point.y - _point.y < KTableStartPointY + KButtonHeight)){
            if (point.x < KTableStartPointX || point.y < KTableStartPointY) {
                [self setFrame:CGRectMake(_point2.x - _point.x, _point2.y - _point.y, self.frame.size.width, self.frame.size.height)];
            }
            else{
                [self setFrame:CGRectMake(KTableStartPointX + (a + KButtonWidth/2 - KTableStartPointX)/KButtonWidth*KButtonWidth, KTableStartPointY + (b + KButtonHeight/2 - KTableStartPointY)/KButtonHeight*KButtonHeight, self.frame.size.width, self.frame.size.height)];
            }
            
        }
        else{
            
            [self animationAction];
            
        }
        _sign = 0;
    }
    [self.label setBackgroundColor:[UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]];
    [self setImage:nil];
}

移动过程中的绘制逻辑和数据交换都很大部分发生在touchedMoved里面

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    
    _sign = 1;
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self.superview];
    if (![self.label.text isEqualToString:@"头条"]) {
        [self.label setBackgroundColor:[UIColor clearColor]];
        [self setImage:[UIImage imageNamed:@"order_drag_move_bg.png"]];
        [self setFrame:CGRectMake( point.x - _point.x, point.y - _point.y, self.frame.size.width, self.frame.size.height)];


        //中心点
        CGFloat newX = point.x - _point.x + KButtonWidth/2;
        CGFloat newY = point.y - _point.y + KButtonHeight/2;
        
        //如果点击的是“头条”框,则不做处理
        if (!CGRectContainsPoint([[_viewArr11 objectAtIndex:0] frame], CGPointMake(newX, newY)) ) {
            //如果自己是属于区域二的view
            if ( _array == _viewArr22) {
                //当前处于区域一
                if ([self buttonInArrayArea1:_viewArr11 Point:point]) {
                    //计算出当前位置属于viewArr11数组中的哪个索引
                    int index = ((int)newX - KTableStartPointX)/KButtonWidth + (5 * (((int)newY - KTableStartPointY)/KButtonHeight));
                    //把自己从原来所属的区域中删除
                    [ _array removeObject:self];
                    //把自己插入到区域一中去
                    [_viewArr11 insertObject:self atIndex:index];
                    //把自己所属标识改为区域一
                    _array = _viewArr11;
                    [self animationAction1a];
                    [self animationAction2];
                }
                
                //如果超过了“更多频道”以上水平线以上,则自动认为是加入到区域一
                else if (newY < KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea1:_viewArr11 Point:point]){
                    
                    [ _array removeObject:self];
                    [_viewArr11 insertObject:self atIndex:_viewArr11.count];
                    _array = _viewArr11;
                    [self animationAction2];
                    
                }
                //如果仍然是在区域二里面移动,则只是调整一下显示的位置
                else if([self buttonInArrayArea2:_viewArr22 Point:point]){
                    unsigned long index = ((unsigned long )(newX) - KTableStartPointX)/KButtonWidth + (5 * (((int)(newY) - [self array2StartY] * KButtonHeight - KTableStartPointY)/KButtonHeight));
                    [ _array removeObject:self];
                    [_viewArr22 insertObject:self atIndex:index];
                    [self animationAction2a];
                    
                }
                else if(newY > KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea2:_viewArr22 Point:point]){
                    [ _array removeObject:self];
                    [_viewArr22 insertObject:self atIndex:_viewArr22.count];
                    [self animationAction2a];
                    
                }
            }
            //如果自己是属于区域一的view
            else if ( _array == _viewArr11) {
                if ([self buttonInArrayArea1:_viewArr11 Point:point]) {
                    int index = ((int)newX - KTableStartPointX)/KButtonWidth + (5 * (((int)(newY) - KTableStartPointY)/KButtonHeight));
                    [ _array removeObject:self];
                    [_viewArr11 insertObject:self atIndex:index];
                    _array = _viewArr11;
                    
                    [self animationAction1a];
                    [self animationAction2];
                }
                else if (newY < KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea1:_viewArr11 Point:point]){
                    [ _array removeObject:self];
                    [_viewArr11 insertObject:self atIndex: _array.count];
                    [self animationAction1a];
                    [self animationAction2];
                }
                else if([self buttonInArrayArea2:_viewArr22 Point:point]){
                    unsigned long index = ((unsigned long)(newX) - KTableStartPointX)/KButtonWidth + (5 * (((int)(newY) - [self array2StartY] * KButtonHeight - KTableStartPointY)/KButtonHeight));
                    [ _array removeObject:self];
                    [_viewArr22 insertObject:self atIndex:index];
                    _array = _viewArr22;
                    [self animationAction2a];
                }
                else if(newY > KTableStartPointY + [self array2StartY] * KButtonHeight &&![self buttonInArrayArea2:_viewArr22 Point:point]){
                    [ _array removeObject:self];
                    [_viewArr22 insertObject:self atIndex:_viewArr22.count];
                    _array = _viewArr22;
                    [self animationAction2a];
                    
                }
            }
        }
    }
}

下面给出其中调用到的Animation函数的注解

- (void)animationAction{
    //先循环绘制“我的订阅”的内容
    for (int i = 0; i < _viewArr11.count; i++) {
        
        [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
            
            [[_viewArr11 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)];
        } completion:^(BOOL finished){
            
        }];
    }
    //再循环绘制“更多频道”的内容
    for (int i = 0; i < _viewArr22.count; i++) {
        [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
            
            [[_viewArr22 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + [self array2StartY] * KButtonHeight + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)];
            
        } completion:^(BOOL finished){
            
        }];
    }
    
    //调整“更多频道”label的位置
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        
        [self.moreChannelsLabel setFrame:CGRectMake(self.moreChannelsLabel.frame.origin.x, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, self.moreChannelsLabel.frame.size.width, self.moreChannelsLabel.frame.size.height)];
        
    } completion:^(BOOL finished){
        
    }];
    
}

//在区域一中给除自己之外的view重绘
- (void)animationAction1a{
    for (int i = 0; i < _viewArr11.count; i++) {
        if ([_viewArr11 objectAtIndex:i] != self) {
            [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
                
                [[_viewArr11 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)];
            } completion:^(BOOL finished){
                
            }];
        }
    }
    
}

//区域2以及区域2的label进行重绘
- (void)animationAction2{
    for (int i = 0; i < _viewArr22.count; i++) {
        
        [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
            
            [[_viewArr22 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + [self array2StartY] * KButtonHeight + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)];
            
        } completion:^(BOOL finished){
            
        }];
    }
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        
        [self.moreChannelsLabel setFrame:CGRectMake(self.moreChannelsLabel.frame.origin.x, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, self.moreChannelsLabel.frame.size.width, self.moreChannelsLabel.frame.size.height)];
        
    } completion:^(BOOL finished){
        
    }];
}

//在区域二中给除自己之外的view重绘
- (void)animationAction2a{
    for (int i = 0; i < _viewArr22.count; i++) {
        if ([_viewArr22 objectAtIndex:i] != self) {
            [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
                [[_viewArr22 objectAtIndex:i] setFrame:CGRectMake(KTableStartPointX + (i%5) * KButtonWidth, KTableStartPointY + [self array2StartY] * KButtonHeight + (i/5)* KButtonHeight, KButtonWidth, KButtonHeight)];
            } completion:^(BOOL finished){
                
            }];
        }
        
    }
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        
        [self.moreChannelsLabel setFrame:CGRectMake(self.moreChannelsLabel.frame.origin.x, KTableStartPointY + KButtonHeight * ([self array2StartY] - 1) + 22, self.moreChannelsLabel.frame.size.width, self.moreChannelsLabel.frame.size.height)];
        
    } completion:^(BOOL finished){
        
    }];
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值