ios表情鍵盤的實現(xiàn)

效果展示

效果展示.gif

實現(xiàn)的功能

  • 支持將表情轉(zhuǎn)換成字符串, 同時也可以將帶有表情的字符串轉(zhuǎn)換成表情圖片
  • 可自定義表情包, 可自定義每頁表情的行數(shù)和列數(shù), 自定義表情包需要兩步
    1: 添加表情包到EmojiPackage.bundle目錄下
    2: 按照demo中的格式修改EmojiPackageList.plist文件
  • 支持長按預(yù)覽, 大表情支持gif, 刪除表情
  • YBEmojiTextView實現(xiàn)了拷貝粘貼剪切功能, 所以如果需要支持該功能, 輸入框需要繼承自該類
  • 支持修改部分外觀, 具體請查看YBEmojiConfig.h文件
  • 適配iPhone X

思路

數(shù)據(jù)的處理

<1> 表情數(shù)據(jù)使用plist文件存儲, 根目錄是一個數(shù)組, 每一個對象是一個表情包字典, 每一個表情包中有表情包的屬性, 包括

  • cover_pic: 表情包封面圖片, 要放在對應(yīng)的表情目錄下
  • folderName: 表情圖片對應(yīng)的文件夾名字, 封面圖片就放在該目錄下
  • isLargeEmoji: 是否是大表情
  • title: 標(biāo)題, 暫時沒用到, 可用于以后擴(kuò)展
  • emojis: 表情數(shù)組, 每一個對象是一個字典, 里邊有兩個字段
    desc: 表情對應(yīng)的字符串, 用來在長按或者大表情下方顯示, 文字提取自[/]中間的字符串, 例如: ??對應(yīng)的字符串就是[/哈哈]格式一定要對
    image: 表情圖片的名字, 用來找到對應(yīng)的表情圖片

<2> 每一個表情包圖片都在一個文件夾中, 文件夾存在EmojiPackage.bundle目錄下, EmojiPackageList.plist文件也在改目錄下
<3> 根據(jù)plist文件創(chuàng)建對應(yīng)的數(shù)據(jù)模型
<4> 對于數(shù)據(jù)我們這里使用單例模式, 同時提供一個表情字符和表情圖片相互轉(zhuǎn)換的接口, 用于外部文字和字符串進(jìn)行互轉(zhuǎn)

真正的鍵盤

其實系統(tǒng)提供好接口給我們自定義鍵盤, 如下

// Presented when object becomes first responder.  If set to nil, reverts to following responder chain.  If
// set while first responder, will not take effect until reloadInputViews is called.
@property (nullable, readwrite, strong) UIView *inputView;             
@property (nullable, readwrite, strong) UIView *inputAccessoryView;

UITextView和UITextField都有這兩個屬性, 所以我們只需要搭建好UI, 修改inputView屬性, 然后調(diào)用reloadInputViews就可以了, 這樣體驗就會和系統(tǒng)的一樣鍵盤一樣了, 如果需要按鍵音的話需要我們自定義的inputView類遵循UIInputViewAudioFeedback協(xié)議, 同時實現(xiàn) enableInputClicksWhenVisible方法并返回YES, 這樣就可以在點擊表情的時候調(diào)用[[UIDevice currentDevice] playInputClick]方法發(fā)出按鍵音了, Demo中沒有實現(xiàn), 需要的話可以自己添加

接下來就是搭建UI了

分析: 通常的表情鍵盤都分為三部分, 上方為可以左右滑動的表情視圖YBEmojiContentView, 中間為頁碼指示器UIPageControl, 最下方為表情包按鈕YBEmojiTabbar, 用來切換表情包, 接下來創(chuàng)建一個繼承自 UIView 的 YBEmojiInputView 作為inputView, 然后將上邊三部分分別添加到Y(jié)BEmojiInputView上, 然后實現(xiàn)具體的每一部分

YBEmojiInputView主要用來調(diào)節(jié)三個模塊的關(guān)系以及提供一些代理給外部使用, 外部只需要實現(xiàn)UITextView的代理方法, 對文字做對應(yīng)的處理就可以了, 代碼基本上是固定的, 這樣輸入框部分就不受限制了, 可以隨意定制自己的UI, 滿足不同用戶對UI的需求

@protocol YBEmojiInputViewDelegate<NSObject>

// 點擊表情
- (void)inputView:(YBEmojiInputView *)inputView clickedEmojiWith:(YBEmojiItemModel *)emoji;

// 點擊大表情
- (void)inputView:(YBEmojiInputView *)inputView clickedBigEmojiWith:(YBEmojiItemModel *)emoji;

// 點擊刪除
- (void)inputView:(YBEmojiInputView *)inputView clickedDeleteWith:(UIButton *)button;

// 點擊發(fā)送
- (void)inputView:(YBEmojiInputView *)inputView clickedSendWith:(UIButton *)button;

@end

指示器和底部欄比較簡單, 我們這里主要來說YBEmojiContentView

1. YBEmojiContentView

既然可以左右滑動, 那就不用考慮了, 先建一個UIScrollView, 至于contentSize是計算出來的, 具體怎么算用腳指頭想都知道

然后考慮到內(nèi)存, 我們不能一下創(chuàng)建那么按鈕上去, 所以我這邊的做法是創(chuàng)建一個YBEmojiPageView作為一頁, 然后創(chuàng)建三頁, 然后在scrollView滑動的時候來交換三個YBEmojiPageView的位置以及frame同時更新表情圖片即可

為了避免重復(fù)更新圖片消耗性能, 所以這邊創(chuàng)建一個pageFlag來記錄當(dāng)前頁碼, 當(dāng)前頁已經(jīng)更新了就不在更新了

核心代碼
// 更新三個pageView位置
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (_delegate && [_delegate respondsToSelector:@selector(contentView:didScrollViewToIndex:)]) {
        NSInteger pageIndex = roundf(self.scrollView.contentOffset.x / self.bounds.size.width);
        [_delegate contentView:self didScrollViewToIndex:pageIndex];
    }
    // 當(dāng)表情也小于等于兩頁的時候就不需要更新了
    if (self.totalPage <= 2) { return; }
    [self updatePagesView];
}
// 更新pageView位置以及內(nèi)容向右滑動后, 將最右邊的pageView放在最左邊, 重新賦值, 向左滑動也一樣>
- (void)updatePagesView {
    CGFloat pageOffset = self.scrollView.contentOffset.x / self.bounds.size.width;
   // 頁碼四舍五入取整
    NSInteger page = roundf(pageOffset);
    if (page != self.pageFlag) {
        YBEmojiPageView *aView = nil;
        if (pageOffset > page) { // 向右滑動
            // 更新表情
            [self.rightPageView configEmojisButtonWith:self.groupModel pageIndex:page - 1];
            // 交換位置
            aView = self.rightPageView;
            self.rightPageView = self.centerPageView;
            self.centerPageView = self.leftPageView;
            self.leftPageView = aView;
        }else { // 向左滑動
            // 更新表情 
            [self.leftPageView configEmojisButtonWith:self.groupModel pageIndex:page + 1];
            // 交換位置
            aView = self.leftPageView;
            self.leftPageView =  self.centerPageView;
            self.centerPageView = self.rightPageView;
            self.rightPageView = aView;
        }
        // 更新pageViews的frame
        [self layoutPageViewsWith:page];
    }
    self.pageFlag = page;
}
// 根據(jù)頁碼更新pageView的frame
- (void)layoutPageViewsWith:(NSInteger)page {
    self.leftPageView.frame = CGRectMake((page - 1) * self.bounds.size.width, 0, self.bounds.size.width, self.bounds.size.height);
    self.centerPageView.frame = CGRectMake(page * self.bounds.size.width, 0, self.bounds.size.width, self.bounds.size.height);
    self.rightPageView.frame = CGRectMake((page + 1) * self.bounds.size.width, 0, self.bounds.size.width, self.bounds.size.height);
}
YBEmojiPageView作用是用來顯示表情, 主要功能如下:
  1. 計算一頁顯示最多表情數(shù)量, 然后將表情控件循環(huán)創(chuàng)建出來
- (instancetype)initWithConfig:(YBEmojiConfig *)config {
    if (self = [super init]) {
        self.config = config;
        self.backgroundColor = config.pageViewBackgroundColor;
        self.emojiItems = [NSMutableArray array];
        // 初始化, 循環(huán)創(chuàng)建表情按鈕(每頁的按鈕數(shù)量 = 行數(shù) x 列數(shù), 最后一個為刪除按鈕, 所以表情按鈕數(shù)量要 -1<大表情就不需要-1了>)
        NSInteger btnCount = MAX(config.smallEmojiLineCount*config.smallEmojiColumnCount-1, config.largeEmojiLineCount*config.largeEmojiColumnCount);
        for (NSUInteger i = 0; i < btnCount; i++) {
            YBEmojiItemView *emojiItem = [[YBEmojiItemView alloc] init];
            [emojiItem addTarget:self action:@selector(clickedEmojiItemView:)];
            [_emojiItems addObject:emojiItem];
            [self addSubview:emojiItem];
        }
        // 刪除按鈕
        self.deleteBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [self.deleteBtn setImage:self.config.pageViewDeleteButtonImage forState:UIControlStateNormal];
        [self.deleteBtn addTarget:self action:@selector(clickedDeleteButtonAction:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:self.deleteBtn];
        
        UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressPageView:)];
        longPress.minimumPressDuration = 0.25;
        [self addGestureRecognizer:longPress];
    }
    return self;
}
  1. 提供給外界的接口用來更新該頁的表情
- (void)configEmojisButtonWith:(YBEmojiGroupModel *)groupModel pageIndex:(NSInteger)pageIndex {
    self.isLargeEmoji = groupModel.isLargeEmoji;
    // 重置表情按鈕圖片
    NSArray<YBEmojiItemModel *> *emojis = [self emojiItemWith:groupModel atPageIndex:pageIndex];
    self.hidden = emojis.count == 0;
    for (int i = 0; i < self.emojiItems.count; i ++) {
        YBEmojiItemView *emojiItemView = self.emojiItems[i];
        // 設(shè)置表情圖片 當(dāng)表情數(shù)量不滿一整頁的時候, 其余按鈕圖片置空
        YBEmojiItemModel *emoji = i < emojis.count ? emojis[i] : nil;
        emojiItemView.isShowTitle = groupModel.isLargeEmoji;
        [emojiItemView setEmoji:emoji];
    }
    [self setNeedsLayout];
}
// 獲取表情包對應(yīng)頁碼的模型數(shù)組
- (NSArray<YBEmojiItemModel *> *)emojiItemWith:(YBEmojiGroupModel *)groupModel atPageIndex:(NSInteger )pageIndex {
    if (!groupModel || !groupModel.emojis.count) {
        return nil;
    }
    NSInteger columnCount = self.isLargeEmoji ? self.config.largeEmojiColumnCount : self.config.smallEmojiColumnCount;
    NSInteger lineCount = self.isLargeEmoji ? self.config.largeEmojiLineCount : self.config.smallEmojiLineCount;
    NSInteger emojiCOuntOfPage = self.isLargeEmoji ? columnCount * lineCount : columnCount * lineCount - 1;
    NSUInteger totalPage = (groupModel.emojis.count / emojiCOuntOfPage) + 1;
    if (pageIndex >= totalPage || pageIndex < 0) {
        return nil;
    }
    BOOL isLastPage = (pageIndex == totalPage - 1 ? YES : NO);
    // 截取的初始位置
    NSUInteger beginIndex = pageIndex * emojiCOuntOfPage;
    // 截取長度
    NSUInteger length = isLastPage ? (groupModel.emojis.count - pageIndex * emojiCOuntOfPage) : emojiCOuntOfPage;
    NSArray *emojis = [groupModel.emojis subarrayWithRange:NSMakeRange(beginIndex, length)];
    return emojis;
}
  1. 根據(jù)是否為大表情更新表情控件frame
- (void)layoutSubviews {
    [super layoutSubviews];
    
    NSInteger columnCount = self.isLargeEmoji ? self.config.largeEmojiColumnCount : self.config.smallEmojiColumnCount;
    NSInteger lineCount = self.isLargeEmoji ? self.config.largeEmojiLineCount : self.config.smallEmojiLineCount;
    
    // 計算表情按鈕寬度
    CGFloat width = (self.bounds.size.width - self.config.pageViewEdgeInsets.left - self.config.pageViewEdgeInsets.right - ((columnCount - 1) * self.config.pageViewMinColumnSpace)) / (CGFloat)columnCount;
    // 計算表情按鈕高度
    CGFloat heigh = (self.bounds.size.height - self.config.pageViewEdgeInsets.top - self.config.pageViewEdgeInsets.bottom - ((lineCount - 1) * self.config.pageViewMinLineSpace)) / (CGFloat)lineCount;
    // 表情按鈕為正方形, 所以取一個最小值作為寬高, 那么久需要重新計算行間距列間距
    CGFloat minSize = MIN(width, heigh);
    // 計算行間距
    CGFloat lineSpace = (self.bounds.size.height - self.config.pageViewEdgeInsets.top - self.config.pageViewEdgeInsets.bottom - minSize * lineCount) / (CGFloat)(lineCount + 1);
    // 計算列間距
    CGFloat columnSpace = (self.bounds.size.width - self.config.pageViewEdgeInsets.left - self.config.pageViewEdgeInsets.right - minSize * columnCount) / (CGFloat)(columnCount + 1);
    // 遍歷設(shè)置表情按鈕的frame
    for (int i = 0; i < self.emojiItems.count; i ++) {
        NSInteger line = i / columnCount;   // 當(dāng)前行數(shù)
        NSInteger column = i % columnCount; // 當(dāng)前列數(shù)
        // 表情按鈕的最小 x 和最小 y
        CGFloat minX = self.config.pageViewEdgeInsets.left + column * minSize + ((column + 1) * columnSpace);
        CGFloat minY = self.config.pageViewEdgeInsets.top + (line * minSize) + ((line + 1) * lineSpace);
        CGRect frame = CGRectMake(minX, minY, minSize, minSize);
        self.emojiItems[i].frame = frame;
    }
    // 刪除按鈕
    self.deleteBtn.frame = CGRectMake(self.bounds.size.width - self.config.pageViewEdgeInsets.right - minSize, self.bounds.size.height - self.config.pageViewEdgeInsets.bottom - minSize - lineSpace, minSize, minSize);
    self.deleteBtn.hidden = self.isLargeEmoji;
}
  1. 長按手勢, 顯示預(yù)覽表情
- (void)longPressPageView:(UILongPressGestureRecognizer *)longPress {
    
    YBEmojiItemView *emojiItemView = nil;
    CGPoint point = [longPress locationInView:self];
    // 遍歷當(dāng)前頁所有按鈕, 找到手指所在的按鈕
    for (YBEmojiItemView *emojiItem in self.emojiItems) {
        // 大表情長按時候有背景顏色, 小表情則沒有
        if (CGRectContainsPoint(emojiItem.frame, point)) {
            emojiItemView = emojiItem;
            if (self.isLargeEmoji) {
                // 大表情長按預(yù)覽的背景顏色
                emojiItem.backgroundColor = self.config.largeEmojiHighlightBackgroundColor;
            }else {
                break;
            }
        }else {
            emojiItem.backgroundColor = UIColor.clearColor;
        }
    }
    
    if (longPress.state == UIGestureRecognizerStateFailed ||
        longPress.state == UIGestureRecognizerStateCancelled ||
        longPress.state == UIGestureRecognizerStateEnded ||
        emojiItemView.emoji == nil) {
        // hide preview
        self.emojiPreview.hidden = YES;
        // 清除在大表情的時候長按的背景顏色
        if (self.isLargeEmoji) {
            emojiItemView.backgroundColor = UIColor.clearColor;
        }
    }else {
        // show preview
        self.emojiPreview.hidden = NO;
        UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
        // 先計算出相對于window的位置, 然后計算預(yù)覽視圖的frame
        CGRect rectOfWindow = [emojiItemView convertRect:emojiItemView.bounds toView:window];
        // 預(yù)覽視圖的寬度
        CGFloat preview_w = self.isLargeEmoji ? self.config.largeEmojiPreviewSize.width : self.config.emojiPreviewSize.width;
        // 預(yù)覽視圖的高度
        CGFloat preview_h = self.isLargeEmoji ? self.config.largeEmojiPreviewSize.height : self.config.emojiPreviewSize.height;
        // 預(yù)覽視圖的x
        CGFloat preview_x = CGRectGetMaxX(rectOfWindow) - preview_w + (preview_w - rectOfWindow.size.width) / 2.0;
        // 預(yù)覽視圖的y
        CGFloat preview_y = self.isLargeEmoji ? CGRectGetMinY(rectOfWindow) - preview_h : CGRectGetMaxY(rectOfWindow) - preview_h;
        // 計算大表情三角指示器的偏移量
        CGFloat angleOffset_x = 0;
        if (self.config.largeEmojiPreviewBorderMargin != 0 && self.isLargeEmoji) {
            if (preview_x < self.config.largeEmojiPreviewBorderMargin) {
                angleOffset_x = preview_x - self.config.largeEmojiPreviewBorderMargin;
                preview_x = self.config.largeEmojiPreviewBorderMargin;
            }
            if (preview_x + preview_w > UIScreen.mainScreen.bounds.size.width - self.config.largeEmojiPreviewBorderMargin) {
                angleOffset_x = self.config.largeEmojiPreviewBorderMargin + preview_x + preview_w - UIScreen.mainScreen.bounds.size.width;
                preview_x = UIScreen.mainScreen.bounds.size.width - self.config.largeEmojiPreviewBorderMargin - preview_w;
            }
        }
        CGRect frame = CGRectMake(preview_x, preview_y, preview_w, preview_h);
        // 將當(dāng)前手指所在位置的表情模型給預(yù)覽視圖進(jìn)行顯示
        [self.emojiPreview setEmojiItemModel:emojiItemView.emoji isLargeEmoji:self.isLargeEmoji];
        self.emojiPreview.frame = frame;
        // 用來調(diào)整大表情三角指示器的居中
        [self.emojiPreview setAngleOffset:angleOffset_x];
    }
}

  1. 提供代理給外界, 實現(xiàn)表情點擊以及刪除方法
@protocol YBEmojiPageViewDelegate<NSObject>

// 點擊表情
- (void)pageView:(YBEmojiPageView *)pageView clickedEmojiWith:(YBEmojiItemModel *)emoji;

// 點擊大表情
- (void)pageView:(YBEmojiPageView *)pageView clickedBigEmojiWith:(YBEmojiItemModel *)emoji;

// 點擊刪除
- (void)pageView:(YBEmojiPageView *)pageView clickedDeleteWith:(UIButton *)button;

@end

至于YBEmojiPreviewView是一個預(yù)覽視圖, 比較簡單, 只需要提供接口用來更改顯示的圖片, frame在外界根據(jù)當(dāng)前手指所在的按鈕來進(jìn)行計算, 在繪制一個大表情的背景線框就可以了, 具體請查看demo, 顯示gif的話, 代碼我拷貝的YLGifImageYLGifImageView

2. UIPageControl 這個就不多說了, 不會的面壁去吧

3. YBEmojiTabbar作用是顯示表情包封面圖片, 以及提供點擊方法

底部無非就是若干個表情包按鈕和一個發(fā)送按鈕, UI以及代碼都比較簡單, 最后提供一個代理給外部使用就可以了

拷貝 粘貼 剪切

如果輸入框中顯示有表情圖片, 那么拷貝,剪切的時候, 我們需要將其創(chuàng)轉(zhuǎn)成字符串, 如果粘貼的時候文字中有表情字符串則需要轉(zhuǎn)換成表情圖片

所以Demo中提供了一個繼承自UITextView的YBEmojiTextView, 已經(jīng)實現(xiàn)拷貝粘貼剪切的功能, 使用者只需要繼承自該類即可, 具體的代碼實現(xiàn):

// 注: 以下代碼拷貝自 PPStickerKeyboard 也給我提供了一些思路, 還有其他一些相關(guān)代碼, 在此表示感謝
// 他的簡書地址http://www.itdecent.cn/p/9359b562a76f
- (void)cut:(id)sender {
    NSString *string = [YBEmojiDataManager.manager plainStringWith:self.attributedText range:self.selectedRange];
    if (string.length) {
        [UIPasteboard generalPasteboard].string = string;
        NSRange selectedRange = self.selectedRange;
        NSMutableAttributedString *attributeContent = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
        [attributeContent replaceCharactersInRange:self.selectedRange withString:@""];
        self.attributedText = attributeContent;
        self.selectedRange = NSMakeRange(selectedRange.location, 0);
        if (self.delegate && [self.delegate respondsToSelector:@selector(textViewDidChange:)]) {
            [self.delegate textViewDidChange:self];
        }
    }
}

- (void)copy:(id)sender {
    NSString *string = [YBEmojiDataManager.manager plainStringWith:self.attributedText range:self.selectedRange];
    if (string.length) {
        [UIPasteboard generalPasteboard].string = string;
    }
}

- (void)paste:(id)sender {
    NSString *string = UIPasteboard.generalPasteboard.string;
    if (string.length) {
        NSMutableAttributedString *attributedPasteString = [[NSMutableAttributedString alloc] initWithString:string];
        attributedPasteString = [YBEmojiDataManager.manager replaceEmojiWithAttributedString:attributedPasteString attributes:self.attributes];
        NSRange selectedRange = self.selectedRange;
        NSMutableAttributedString *attributeContent = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
        [attributeContent replaceCharactersInRange:self.selectedRange withAttributedString:attributedPasteString];
        self.attributedText = attributeContent;
        self.selectedRange = NSMakeRange(selectedRange.location + attributedPasteString.length, 0);
        if (self.delegate && [self.delegate respondsToSelector:@selector(textViewDidChange:)]) {
            [self.delegate textViewDidChange:self];
        }
    }
}

以上就是實現(xiàn)表情鍵盤的一些思路以及要點, 具體請查看Demo, 代碼已上傳至Github

解決在ios14上圖片顯示不出來的bug

// 在YBEmojiGifImageView中對displayLayer方法做如下修改
- (void)displayLayer:(CALayer *)layer
{
    if (!self.animatedImage || [self.animatedImage.images count] == 0) {
        if (@available(iOS 14.0, *)) {
            [super displayLayer:layer];
        }
        return;
    }
    //NSLog(@"display index: %luu", (unsigned long)self.currentFrameIndex);
    if(self.currentFrame && ![self.currentFrame isKindOfClass:[NSNull class]]) {
        layer.contents = (__bridge id)([self.currentFrame CGImage]);
    }
}

注: 暫不支持YYText

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 2017.02.22 可以練習(xí),每當(dāng)這個時候,腦袋就犯困,我這腦袋真是神奇呀,一說讓你做事情,你就犯困,你可不要太...
    Carden閱讀 1,491評論 0 1
  • 報錯描述:我有一個ajax請求 請求的action 然后報錯信息為: 讓我郁悶了...我查了一整天的資料都沒找到解...
    dodoliu閱讀 1,187評論 0 0
  • 不念過去 不畏將來。這是多么難得的狀態(tài)啊! 臨在,就是有覺察力的安住在當(dāng)下。 劉老師講課內(nèi)容 臨在 臨在就是有覺察...
    隨心所欲哲閱讀 454評論 0 0
  • 兒時的小姐妹 文/秦萱 我兒時的姐妹很土 單薄的身子裹著媽媽織就的粗布衣衫 在家里喂豬、做飯 把年幼的弟妹綁在背上...
    珠語閱讀 765評論 0 2
  • 小孩小孩你別哭 冬月臘月就殺豬 紅蘿卜, 呡呡甜, 看到看到要過年…… 進(jìn)入冬、臘月間,天氣就一天天冷起來了。 田...
    瀘中陳健閱讀 867評論 0 1

友情鏈接更多精彩內(nèi)容