漢諾塔自動解題動畫中的iOS開發(fā)技巧

前段時間做了一道題香府,要求實現(xiàn)漢諾塔游戲的自動解題動畫:

image.png

漢諾塔游戲應(yīng)該都了解規(guī)則:

1致开、將盤子全部移動到塔C
2甲雅、每次只能移動一個圓盤解孙;
3、大盤不能疊在小盤上面抛人。

要求由用戶輸入盤子的數(shù)量弛姜,繪制盤子和塔,點擊開始后自動解題妖枚,并以動畫移動盤子的形式演示廷臼。

覺得還挺有意思的,而且在做的過程中也踩了一些坑,用了一些技巧和優(yōu)化荠商,因此記錄下來寂恬。

效果:

20170518105404247.gif

漢諾塔解法

這道題中漢諾塔的解法本身并不是難點。

1莱没、如果只有一個盤子初肉,那就直接從A移動到C;
2饰躲、如果有兩個盤子牙咏,那就要先把小盤子移動到B,然后大盤子移動到C嘹裂,再把小盤子移動到C妄壶;
3、如果有三個盤子寄狼,那就要先把上面兩個盤子移動到B(借助C的輔助)丁寄,然后把底下的大盤子移動到C,然后把B上的兩個盤子借助A移動到C泊愧;
……
4伊磺、如果有n個盤子,那就要先把上面n-1個盤子移動到B(借助C的輔助)拼卵,然后把底下的大盤子移動到C奢浑,然后把B上的n-1盤子借助A移動到C。

綜上所述腋腮,除了一個盤子的情況直接移動雀彼,其余都需要借助其他盤子的幫助,復(fù)雜情況雖然不一樣即寡,但是過程是遞歸不斷重復(fù)的徊哑。

遞歸代碼如下:

// 確定提交
- (void)submit {
    if ([self.numberField.text isEqualToString:@""]) {
        NSLog(@"未輸入內(nèi)容");
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"您還未輸入層數(shù)!" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        }];
        [alertController addAction:okAction];
        [self presentViewController:alertController animated:YES completion:nil];
    } else {
        self.diskNumber = [self.numberField.text integerValue];
        self.moveCount = 0;
        [self hanoiWithDisk:self.diskNumber towers:@"A" :@"B" :@"C"];
        NSLog(@">>移動了%ld次", self.moveCount);
    }
}

// 移動算法
- (void)hanoiWithDisk:(NSInteger)diskNumber towers:(NSString *)towerA :(NSString *)towerB :(NSString *)towerC {
    if (diskNumber == 1) {// 只有一個盤子則直接從A塔移動到C塔
        [self move:1 from:towerA to:towerC];
    } else {
        [self hanoiWithDisk:diskNumber-1 towers:towerA :towerC :towerB];// 遞歸把A塔上編號1~diskNumber-1的盤子移動到B塔,C塔輔助
        [self move:diskNumber from:towerA to:towerC];// 把A塔上編號為diskNumber的盤子移動到C塔
        [self hanoiWithDisk:diskNumber-1 towers:towerB :towerA :towerC];// 遞歸把B塔上編號1~diskNumber-1的盤子移動到C塔聪富,A塔輔助
    }
}

// 移動過程
- (void)move:(NSInteger)diskIndex from:(NSString *)fromTower to:(NSString *)toTower {
    NSLog(@"第%ld次移動:把%ld號盤從%@移動到%@", ++self.moveCount, diskIndex, fromTower, toTower);
}

三層盤子時:

image.png

四層盤子時:

image.png

可見算法是正確的莺丑,接下來就是實現(xiàn)繪制和動畫的問題。

繪制塔和盤子

解決了算法的問題墩蔓,下一步我們要繪制圖形了梢莽。

這里為了方便我決定全部用UIView來做,比如塔就是一橫一豎兩個UIView奸披,每個盤子都是一個UIView昏名。

為了方便給盤子編號,創(chuàng)建一個繼承自UIView的盤子類阵面,加上編號屬性:

#pragma mark - Disk Model
// 自定義的盤子模型轻局,在UIView基礎(chǔ)上加上編號屬性
@interface OXDiskModel : UIView
@property NSInteger index;
@end

@implementation OXDiskModel

@end

因為這個代碼很短洪鸭,沒必要新開一個文件,直接在繪制圖形的ViewController.m文件中加上這個代碼就可以實現(xiàn)了仑扑。

對于塔览爵,一開始我直接在界面上繪制三個塔的6條線,很簡單镇饮,但是在涉及到動畫的時候蜓竹,需要頻繁用到每個塔的位置以及塔上已有的盤子數(shù)量才能確定盤子移動到的位置,這就很麻煩储藐,而且不穩(wěn)定梅肤,代碼很復(fù)雜。

后來我改成把塔也抽象出來成一個塔類邑茄,在其類中繪制兩條線,并且加上塔名稱以及塔上盤子數(shù)量的屬性俊啼,這樣就可以直接調(diào)用了肺缕,在遞歸算法中,我們可以直接傳遞三個塔對象授帕,可以很方便地計算同木,減少了大量的代碼,代碼結(jié)構(gòu)也更加清晰跛十。

塔的繪制代碼和屬性就不寫出來了彤路,有單獨的類文件,可以直接在工程中看芥映,這里直說思想洲尊,對于一些適合抽離出來的對象,我們應(yīng)該盡可能抽象成對應(yīng)的類奈偏,將它的操作坞嘀、行為、屬性等放在類中寫惊来,可以極大地簡化代碼丽涩、使代碼結(jié)構(gòu)更清晰。

這樣裁蚁,我們就可以根據(jù)屏幕大小算出每個塔合適的大小矢渊,然后去創(chuàng)建三個塔對象,添加到界面上就好了枉证。

// 三座塔
- (void)initThreeTower {
    // 添加三座塔
    NSInteger height = (SCREENHEIGHT - 150)/3 - 30;
    for (int i = 0; i < 3; i++) {
        OXTowerView *tower = [[OXTowerView alloc] initWithFrame:CGRectMake((SCREENWIDTH-250)/2, 130 + (height+30)*i, 250, height+5)];
        tower.diskNumber = 0;
        [self.view addSubview:tower];
        [self.towerArray addObject:tower];
        
        // 塔號
        UILabel *towerLabel = [[UILabel alloc] initWithFrame:CGRectMake(12, tower.frame.origin.y + height + 5, SCREENWIDTH-24, 15)];
        switch (i) {
            case 0:
                towerLabel.text = @"A";
                tower.towerId = @"A";
                tower.diskNumber = self.diskNumber;// 一開始盤子都在塔A上
                break;
                
            case 1:
                towerLabel.text = @"B";
                tower.towerId = @"B";
                break;
                
            case 2:
                towerLabel.text = @"C";
                tower.towerId = @"C";
                break;
                
            default:
                break;
        }
        towerLabel.textColor = [UIColor darkGrayColor];
        towerLabel.textAlignment = NSTextAlignmentCenter;
        towerLabel.font = [UIFont systemFontOfSize:14];
        [self.view addSubview:towerLabel];
    }
}

然后根據(jù)輸入的盤子層數(shù)矮男,動態(tài)算出每個盤子合適的高度以及每個盤子的寬度(從大到小)刽严,放在第一個塔上:

// 初始放置盤子
- (void)initWithDiskPut {
    NSInteger towerHeight = (SCREENHEIGHT - 150)/3 - 40;
    NSInteger diskHeight = towerHeight / self.diskNumber;// 盤子高度
    
    // 依次放置盤子
    for (int i = 0; i < self.diskNumber; i++) {
        NSInteger diskWeight = 230 - 30*i;// 盤子寬度
        
        // 自定義的盤子模型類
        OXDiskModel *disk = [[OXDiskModel alloc] initWithFrame:CGRectMake((SCREENWIDTH-diskWeight)/2, 140 + diskHeight*(self.diskNumber-i-1), diskWeight, diskHeight)];
        disk.backgroundColor = [UIColor yellowColor];
        disk.layer.borderColor = [[UIColor darkGrayColor] CGColor];
        disk.layer.borderWidth = 1;
        disk.index = self.diskNumber - i;
        [self.view addSubview:disk];
        [self.diskArray addObject:disk];
    }
}

動畫解題

在繪制過程中我們充分利用了面向?qū)ο缶幊痰乃枷搿昂灵,F(xiàn)在來到最后一個問題避凝,把算法和動畫結(jié)合起來。

算法還是那個算法眨补,在之前的算法中管削,我們傳遞的參數(shù)只是簡單的字符串來代替三個塔,盤子也只是用盤子編號來代替撑螺,這里我們就要用我們的塔對象和盤子對象來作為真正的參數(shù)傳遞了含思。

對于塔,我們直接傳遞塔對象甘晤;對于盤子含潘,我們傳參還是傳盤子編號,但是我們用一個數(shù)組記錄所有盤子线婚,然后循環(huán)找到當前要移動的對應(yīng)編號的盤子遏弱。

盤子的移動動畫我們使用簡單的UIView動畫就可以實現(xiàn)了,關(guān)于UIView基礎(chǔ)動畫可以看這篇文章:傳送門:iOS基礎(chǔ)動畫教程塞弊。

在動畫block中漱逸,我們?nèi)ジ淖儽P子的center,也就是中心點的Y坐標游沿,來達到移動的目的饰抒,如何計算出要移動到哪呢?從參數(shù)中我們可以知道要移動到哪個塔诀黍,根據(jù)塔的屬性可以知道塔上現(xiàn)在有多少個盤子袋坑,那么就可以根據(jù)塔的坐標、塔上盤子的數(shù)量眯勾、每個盤子的高度來計算出這個盤子要移動到哪個坐標了枣宫。

UIView動畫有一個completion block,用來在動畫完成后執(zhí)行一些操作吃环,上面我們要用到塔上的盤子數(shù)量镶柱,那在移動完后我們一定也要更新每座塔的數(shù)量,移走的塔數(shù)量減一模叙,移到的塔數(shù)量加一歇拆。

這里就可以體現(xiàn)把塔作為對象的好處了, 試想一下不這么做范咨,我們?nèi)绻烂孔淖鴺艘约懊孔系谋P子數(shù)量故觅,一定要用數(shù)組去記錄,而且傳參時我們只能像最開始一樣傳遞塔名字符串渠啊,那還得根據(jù)這個字符串來判斷改變數(shù)組中的第幾個元素的塔數(shù)量输吏,獲取哪個塔坐標,這都增加了很多代碼量替蛉。但是有了塔對象贯溅,我們可以直接作為參數(shù)傳遞拄氯,也可以直接獲取盤子數(shù)量去修改,太方便了它浅。

// 開始
- (void)start {
    self.moveCount = 0;
    [self hanoiWithDisk:self.diskNumber towers:@"A" :@"B" :@"C"];
    NSLog(@">>移動了%ld次", self.moveCount);
}

// 移動算法
- (void)hanoiWithDisk:(NSInteger)diskNumber towers:(OXTowerView *)towerA :(OXTowerView *)towerB :(OXTowerView *)towerC {
    if (diskNumber == 1) {// 只有一個盤子則直接從A塔移動到C塔
        [self move:1 from:towerA to:towerC];
    } else {
        [self hanoiWithDisk:diskNumber-1 towers:towerA :towerC :towerB];// 遞歸把A塔上編號1~diskNumber-1的盤子移動到B塔译柏,C塔輔助
        [self move:diskNumber from:towerA to:towerC];// 把A塔上編號為diskNumber的盤子移動到C塔
        [self hanoiWithDisk:diskNumber-1 towers:towerB :towerA :towerC];// 遞歸把B塔上編號1~diskNumber-1的盤子移動到C塔,A塔輔助
    }
}

// 移動過程
- (void)move:(NSInteger)diskIndex from:(OXTowerView *)fromTower to:(OXTowerView *)toTower {
    NSLog(@"第%ld次移動:把%ld號盤從%@移動到%@", ++self.moveCount, diskIndex, fromTower, toTower);
    for (OXDiskModel *disk in self.diskArray) {
        if (disk.index == diskIndex) {
            [UIView animateWithDuration:1.0 animations:^{
                // 計算改變盤子位置
            } completion:^(BOOL finished) {
                if (finished) {// 動畫完成
                    // 更新塔上的盤子數(shù)量
                    fromTower.diskNumber--;
                    toTower.diskNumber++;
                }
            }];
        }
    }
}

這里有一個有意思的點可以看一下移動算法中后面三個塔參數(shù)前面是沒有文字的姐霍,只有一個冒號鄙麦,OC支持定義方法時參數(shù)前不需要一定要有文字,只不過為了方便理解都會加一個參數(shù)說明镊折。

到此胯府,是不是問題都解決了?不是的恨胚,如果你直接這么寫骂因,運行后會發(fā)現(xiàn)所有動畫都一起移動到塔C,根本沒有過程赃泡!這是為什么侣签?

因為算法運行得很快,而動畫需要時間急迂,這就導致還沒開始動畫,所有的算法都計算完了蹦肴,最后只會把所有盤子一起移動到塔C僚碎,因為那就是算法最后算出來的目標位置。

這時我想到的第一個方法是用dispatch_semaphore_t來做為信號量阴幌,控制算法等待動畫完畢后再進行勺阐,用法說明可以看這篇文章:傳送門:iOS之利用GCD信號量控制并發(fā)網(wǎng)絡(luò)請求,比如像下面這樣:

// 移動過程
- (void)move:(NSInteger)diskIndex from:(OXTowerView *)fromTower to:(OXTowerView *)toTower {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);// 初始化信號量為0
    NSLog(@"第%ld次移動:把%ld號盤從%@移動到%@", ++self.moveCount, diskIndex, fromTower, toTower);
    for (OXDiskModel *disk in self.diskArray) {
        if (disk.index == diskIndex) {
            [UIView animateWithDuration:1.0 animations:^{
                // 計算改變盤子位置
            } completion:^(BOOL finished) {
                if (finished) {// 動畫完成
                    // 更新塔上的盤子數(shù)量
                    fromTower.diskNumber--;
                    toTower.diskNumber++;
                    dispatch_semaphore_signal(sema);// 增加信號量矛双,結(jié)束等待
                }
            }];
            break;
        }
    }
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);// 信號量若沒增加渊抽,則一直等待,直到動畫完成
}

運行后會發(fā)現(xiàn)動畫干脆都不動了议忽,為什么懒闷?因為動畫在主線程,信號量等待也在主線程栈幸,那就造成了“信號量等待信號才能繼續(xù)往下進行<-->動畫在主線程中被信號量卡主等待愤估,無法進行,但是進行完了才能給出信號量”的循環(huán)等待速址。

這怎么解決玩焰?其實看上面的解釋就能夠想到辦法了,把算法放到分線程去跑芍锚,動畫放在主線程昔园!這樣信號量等待是讓分線程等待蔓榄,不會影響主線程,這樣就不會阻塞默刚,同時可以實現(xiàn)算法等待動畫完畢后再進行的效果甥郑,完美:

// 開始移動
- (void)beginMove {
    self.moveCount = 0;
    
    WeakSelf
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 到分線程去處理算法
        StrongSelf
        if (strongSelf) {
            [strongSelf hanoiWithDisk:strongSelf.diskNumber towers:[strongSelf.towerArray objectAtIndex:0] :[strongSelf.towerArray objectAtIndex:1] :[strongSelf.towerArray objectAtIndex:2]];
        }
    });
    
//    NSLog(@">>移動了%ld次", self.moveCount);
    
    
}

// 移動算法
- (void)hanoiWithDisk:(NSInteger)diskNumber towers:(OXTowerView *)towerA :(OXTowerView *)towerB :(OXTowerView *)towerC {
    if (diskNumber == 1) {// 只有一個盤子則直接從A塔移動到C塔
        [self move:1 from:towerA to:towerC];
    } else {
        [self hanoiWithDisk:diskNumber-1 towers:towerA :towerC :towerB];// 遞歸把A塔上編號1~diskNumber-1的盤子移動到B塔,C塔輔助
        
        [self move:diskNumber from:towerA to:towerC];// 把A塔上編號為diskNumber的盤子移動到C塔
        
        [self hanoiWithDisk:diskNumber-1 towers:towerB :towerA :towerC];// 遞歸把B塔上編號1~diskNumber-1的盤子移動到C塔羡棵,A塔輔助
        
    }
}

// 移動過程
- (void)move:(NSInteger)diskIndex from:(OXTowerView *)fromTower to:(OXTowerView *)toTower {
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);// 初始化信號量為0
    
    NSLog(@"第%ld次移動:把%ld號盤從塔%@移動到塔%@", ++self.moveCount, diskIndex, fromTower.towerId, toTower.towerId);
    
    for (OXDiskModel *disk in self.diskArray) {
        if (disk.index == diskIndex) {
            
            WeakSelf
            dispatch_async(dispatch_get_main_queue(), ^{// 切回主線程進行移動動畫
                [UIView animateWithDuration:1.0 animations:^{
                    StrongSelf
                    if (strongSelf) {
                        // 改變盤子的位置
                        CGPoint diskCenter = disk.center;
                        NSInteger towerY = 10 + toTower.frame.origin.y;
                        NSInteger towerHeight = toTower.frame.size.height-15;
                        NSInteger diskHeight = towerHeight / strongSelf.diskNumber;// 每個盤子高度
                        NSInteger hasDiskHieght = diskHeight * toTower.diskNumber;// 已放置了的盤子高度
                        diskCenter.y = towerY + (towerHeight - hasDiskHieght) - diskHeight/2;
                        disk.center = diskCenter;
                    }
                    
                } completion:^(BOOL finished) {
                    if (finished) {// 動畫完成
                        StrongSelf
                        if (strongSelf) {
                            // 改變fromTower的盤子數(shù)量
                            fromTower.diskNumber--;
                            
                            // 改變toTower的盤子數(shù)量
                            toTower.diskNumber++;
                            
                            dispatch_semaphore_signal(sema);// 增加信號量壹若,結(jié)束等待
                        }
                    }
                }];
            });
            
            
            break;
        }
    }
    
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);// 信號量若沒增加,則一直等待皂冰,直到動畫完成
}

這時候再運行就可以完美實現(xiàn)效果了:

20170518105404247.gif

結(jié)

為了解決阻塞的問題店展,還嘗試過延遲執(zhí)行、動畫隊列等方法秃流,但都不如這個方法簡單有效赂蕴。

在做這個的過程中,用到了很多小技巧舶胀,也多次優(yōu)化了代碼概说,對于我自己來說代碼越來越賞心悅目,實在是一次很好的學習訓練的經(jīng)驗嚣伐。

而且看著自己做的漢諾塔游戲自動動畫解題很有意思不是嘛糖赔!


示例工程:https://github.com/Cloudox/OXHanoiDemo


查看作者首頁

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市轩端,隨后出現(xiàn)的幾起案子放典,更是在濱河造成了極大的恐慌,老刑警劉巖基茵,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奋构,死亡現(xiàn)場離奇詭異,居然都是意外死亡拱层,警方通過查閱死者的電腦和手機弥臼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來根灯,“玉大人径缅,你說我怎么就攤上這事±臃危” “怎么了芥驳?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長茬高。 經(jīng)常有香客問我兆旬,道長,這世上最難降的妖魔是什么怎栽? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任丽猬,我火速辦了婚禮宿饱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脚祟。我一直安慰自己谬以,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布由桌。 她就那樣靜靜地躺著为黎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪行您。 梳的紋絲不亂的頭發(fā)上铭乾,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音娃循,去河邊找鬼炕檩。 笑死,一個胖子當著我的面吹牛捌斧,可吹牛的內(nèi)容都是我干的笛质。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼捞蚂,長吁一口氣:“原來是場噩夢啊……” “哼妇押!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起姓迅,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤敲霍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后队贱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡潭袱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年柱嫌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屯换。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡编丘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出彤悔,到底是詐尸還是另有隱情嘉抓,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布晕窑,位于F島的核電站抑片,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏杨赤。R本人自食惡果不足惜敞斋,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一截汪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧植捎,春花似錦衙解、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至济锄,卻和暖如春暑椰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拟淮。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工干茉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人很泊。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓角虫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親委造。 傳聞我的和親對象是個殘疾皇子戳鹅,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復(fù)雜昏兆,今天將帶大家一窺ios動畫全貌枫虏。在這里你可以看...
    每天刷兩次牙閱讀 8,471評論 6 30
  • 兩個月以前我20歲了。在我?guī)讱q的時候我經(jīng)常會想我18歲會是什么樣爬虱,兩年前也就是18歲的我經(jīng)常想20歲自己會是什么樣...
    喝啥喲閱讀 145評論 0 0
  • 【中國·典】姓氏圖騰 晚霞報 剛認識一個人的時候 我們會習慣問“您貴姓” 其實姓氏一開始是作為家族的象征 還有專門...
    xcy無名閱讀 2,920評論 0 2
  • 今天是參加寫作訓練營的第12天跑筝。從一開始到現(xiàn)在死讹,已經(jīng)寫了10篇文章,而這篇是第十一篇曲梗。從未想過可以每天堅持寫500...
    云林閱讀 333評論 2 0
  • 1.學習了安卓表單配置 安卓表單配置工具是為了方便移動端的表單配置赞警,配置完成表單后下載,將其放到文書的CisSer...
    小FFF閱讀 147評論 0 0