iOS-MJRefresh框架

李明杰的MJRefresh應(yīng)該也算是iOS中使用最廣泛的一個框架了,而且MJ的框架也用了好多中文注釋成福,這點讓我感覺到很親切碾局,網(wǎng)上也有好多分析的文章砌溺,但是別人的畢竟沒自己的印象深刻锡搜,現(xiàn)在分析一下MJRefresh每一句代碼是干什么的贾虽?為什么要這么寫挟伙?

想要寫一個好的框架需要注意兩點:1. 易用性強 2. 可定制性強 3. 設(shè)計合理
易用性強才會有人愿意使用豁陆,可定制性強才會有更多的場景可以使用研侣,設(shè)計合理以后修改的時候才不會太麻煩矢劲,別人也很容易理解蒲拉。

一. 如何實現(xiàn)下拉刷新

  1. 利用contentOffset
    首先如何實現(xiàn)下拉刷新潭苞,很顯然下拉的時候scrollView 的contentOffset會改變埋合,我們可以監(jiān)聽這個值的變化來給scrollView添加一個mj_header并實現(xiàn)相應(yīng)的動畫效果。
  2. 如何添加mj_header
    現(xiàn)在目標是給scrollView添加一個MJRefreshHeader萄传,只要給scrollView添加MJRefreshHeader甚颂,其他tableView和collectionView就都有了,但是系統(tǒng)的scrollView沒有這個屬性秀菱,這時候我們可以通過給scrollView的分類添加關(guān)聯(lián)對象的方式振诬,來實現(xiàn)給scrollView添加一個屬性,具體可參考UIScrollView+MJRefresh代碼衍菱。
  3. mj_header添加在contentInset.top的位置赶么。
  4. 為什么要有MJRefreshComponent
    接下來我們就想如何去寫一個MJRefreshHeader,顯然脊串,我們不但有下拉刷新還有上拉刷新辫呻,這時候我們就需要一個baseView,這個baseView就是MJRefreshComponent琼锋,我們的上拉和下拉刷新控件都繼承于這個MJRefreshComponent放闺,MJRefreshComponent繼承于UIView是最基礎(chǔ)的基類,所以關(guān)于上拉下拉所有唯一共用的東西我們都可以寫在這里面缕坎。

接下來的事情就很簡單了怖侦,我們可以層層繼承,在合適的類添加合適的控件實現(xiàn)合適的方法谜叹。

二. MJRefreshComponent

1. 定義的東西和成員變量

/** 刷新控件的狀態(tài) */
typedef NS_ENUM(NSInteger, MJRefreshState) {
    /** 普通閑置狀態(tài) */
    MJRefreshStateIdle = 1,
    /** 松開就可以進行刷新的狀態(tài) */
    MJRefreshStatePulling,
    /** 正在刷新中的狀態(tài) */
    MJRefreshStateRefreshing,
    /** 即將刷新的狀態(tài) */
    MJRefreshStateWillRefresh,
    /** 所有數(shù)據(jù)加載完畢匾寝,沒有更多的數(shù)據(jù)了 */
    MJRefreshStateNoMoreData
};

/** 進入刷新狀態(tài)的回調(diào) */
typedef void (^MJRefreshComponentRefreshingBlock)(void);
/** 開始刷新后的回調(diào)(進入刷新狀態(tài)后的回調(diào)) */
typedef void (^MJRefreshComponentbeginRefreshingCompletionBlock)(void);
/** 結(jié)束刷新后的回調(diào) */
typedef void (^MJRefreshComponentEndRefreshingCompletionBlock)(void);

/** 刷新控件的基類 */
@interface MJRefreshComponent : UIView
{
    /** 記錄scrollView剛開始的inset */
    UIEdgeInsets _scrollViewOriginalInset;
    /** 父控件 */
    __weak UIScrollView *_scrollView;
}
  1. 首先定義了刷新狀態(tài)MJRefreshState和三個刷新狀態(tài)的block,這個很容易理解荷腊,每個刷新控件一定有這些東西艳悔。
  2. 另外還定義了兩個成員變量
    ① _scrollViewOriginalInset這個值記錄scrollView剛開始的inset,這個值會在scrollViewContentOffsetDidChange方法里面使用到女仰,用來設(shè)置mj_header的位置猜年。
    ② _scrollView就是父控件香府,弱引用。

2. 刷新回調(diào)

#pragma mark - 刷新回調(diào)
/** 正在刷新的回調(diào) */
@property (copy, nonatomic) MJRefreshComponentRefreshingBlock refreshingBlock;
/** 設(shè)置回調(diào)對象和回調(diào)方法 */
- (void)setRefreshingTarget:(id)target refreshingAction:(SEL)action;

/** 回調(diào)對象 */
@property (weak, nonatomic) id refreshingTarget;
/** 回調(diào)方法 */
@property (assign, nonatomic) SEL refreshingAction;
/** 觸發(fā)回調(diào)(交給子類去調(diào)用) */
- (void)executeRefreshingCallback;

這里定義了刷新回調(diào)码倦,以及回調(diào)方法和回調(diào)對象企孩,主要介紹executeRefreshingCallback方法:

#pragma mark - 內(nèi)部方法
//執(zhí)行刷新回調(diào),不同子類都會調(diào)用,所以抽取到父類
- (void)executeRefreshingCallback
{
    MJRefreshDispatchAsyncOnMainQueue({
        if (self.refreshingBlock) {
            self.refreshingBlock();
        }
        if ([self.refreshingTarget respondsToSelector:self.refreshingAction]) {
            //消息發(fā)送機制:
            //((void (*)(void *, SEL, UIView *))objc_msgSend)((__bridge void *)(self.refreshingTarget), self.refreshingAction, self);
            MJRefreshMsgSend(MJRefreshMsgTarget(self.refreshingTarget), self.refreshingAction, self);
        }
        if (self.beginRefreshingCompletionBlock) {
            self.beginRefreshingCompletionBlock();
        }
    })
}

這個方法執(zhí)行刷新回調(diào),不同子類都會調(diào)用袁稽,所以抽取到父類里面勿璃。

3. 刷新狀態(tài)控制

#pragma mark - 刷新狀態(tài)控制
/** 進入刷新狀態(tài) */
- (void)beginRefreshing;
- (void)beginRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
/** 開始刷新后的回調(diào)(進入刷新狀態(tài)后的回調(diào)) */
@property (copy, nonatomic) MJRefreshComponentbeginRefreshingCompletionBlock beginRefreshingCompletionBlock;
/** 結(jié)束刷新的回調(diào) */
@property (copy, nonatomic) MJRefreshComponentEndRefreshingCompletionBlock endRefreshingCompletionBlock;
/** 結(jié)束刷新狀態(tài) */
- (void)endRefreshing;
- (void)endRefreshingWithCompletionBlock:(void (^)(void))completionBlock;
/** 是否正在刷新 */
@property (assign, nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
//- (BOOL)isRefreshing;
/** 刷新狀態(tài) 一般交給子類內(nèi)部實現(xiàn) */
@property (assign, nonatomic) MJRefreshState state;

這里定義了開始結(jié)束刷新的方法以及開始結(jié)束刷新的block,定義了刷新狀態(tài)以及是否正在刷新的BOOL值來控制刷新狀態(tài)推汽。

① setState:

一般交給子類內(nèi)部實現(xiàn)补疑,不同狀態(tài)做不同的事情。

- (void)setState:(MJRefreshState)state
{
    _state = state;
    
    // 加入主隊列的目的是等setState:方法調(diào)用完畢歹撒、設(shè)置完文字后再去布局子控件
    //因為文字的變化會引起左側(cè)箭頭位置的變化莲组,這時候需要刷新來重制位置。
    MJRefreshDispatchAsyncOnMainQueue([self setNeedsLayout];)
}

② beginRefreshing:

他其實主要就是把state標記為MJRefreshStateRefreshing暖夭。但是它還做了另外一層判斷:window的有無锹杈。MJ 也做了備注,說明了為什么要有這個判斷迈着,主要是因為預(yù)防用戶過早的調(diào)用了beginRefresh方法竭望,然而這時候自身還并沒有顯示出來,所以巧妙的先將state標記為了MJRefreshStateWillRefresh裕菠。

- (void)beginRefreshing
{
    [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{
        self.alpha = 1.0;
    }];
    self.pullingPercent = 1.0;
    // 只要正在刷新咬清,就完全顯示
    if (self.window) {
        self.state = MJRefreshStateRefreshing;
    } else {
        // 預(yù)防正在刷新中時,調(diào)用本方法使得header inset回置失敗
        if (self.state != MJRefreshStateRefreshing) {
            self.state = MJRefreshStateWillRefresh;
            // 刷新(預(yù)防從另一個控制器回到這個控制器的情況奴潘,回來要重新刷新一下)
            [self setNeedsDisplay];
        }
    }
}

4. 交給子類實現(xiàn)

#pragma mark - 交給子類們?nèi)崿F(xiàn)
/** 初始化 */
- (void)prepare NS_REQUIRES_SUPER;
/** 擺放子控件frame */
- (void)placeSubviews NS_REQUIRES_SUPER;
/** 當scrollView的contentOffset發(fā)生改變的時候調(diào)用 */
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
/** 當scrollView的contentSize發(fā)生改變的時候調(diào)用 */
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;
/** 當scrollView的拖拽狀態(tài)發(fā)生改變的時候調(diào)用 */
- (void)scrollViewPanStateDidChange:(NSDictionary *)change NS_REQUIRES_SUPER;

這些方法主要是交給子類來實現(xiàn)旧烧,這里只實現(xiàn)了prepare方法:

- (void)prepare
{
    // 基本屬性
    //保證在橫豎屏切換的時候能夠保證自身相對于父視圖的左右邊距保持不變,這個方法是每個子類都必須的画髓,所以放在了基類MJRefreshComponent中掘剪。
    self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    self.backgroundColor = [UIColor clearColor];
}

方法的調(diào)用順序是:

alloc(prepare) -> setState -> willMoveToSuperView -> layoutSubViews(placeSubviews) -> drawRect

5. 拖拽百分比和透明度

#pragma mark - 其他
/** 拉拽的百分比(交給子類重寫) */
@property (assign, nonatomic) CGFloat pullingPercent;
/** 根據(jù)拖拽比例自動切換透明度 */
@property (assign, nonatomic, getter=isAutoChangeAlpha) BOOL autoChangeAlpha MJRefreshDeprecated("請使用automaticallyChangeAlpha屬性");
/** 根據(jù)拖拽比例自動切換透明度 */
@property (assign, nonatomic, getter=isAutomaticallyChangeAlpha) BOOL automaticallyChangeAlpha;
@end

這里有拖拽百分比和自動切換透明度,pullingPercent一般交給子類來實現(xiàn)雀扶,根據(jù)拖拽的比例來實現(xiàn)個性定制杖小,默認是根據(jù)拖拽百分比自動切換透明度,如下:

- (void)setPullingPercent:(CGFloat)pullingPercent
{
    _pullingPercent = pullingPercent;
    
    if (self.isRefreshing) return;
    
    if (self.isAutomaticallyChangeAlpha) {
        self.alpha = pullingPercent;
    }
}

6. UILabel分類

@interface UILabel(MJRefresh)

+ (instancetype)mj_label;
- (CGFloat)mj_textWith;

@end

實現(xiàn)了兩個方法:

@implementation UILabel(MJRefresh)
+ (instancetype)mj_label
{
    UILabel *label = [[self alloc] init];
    label.font = MJRefreshLabelFont;
    label.textColor = MJRefreshLabelTextColor;
    label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    label.textAlignment = NSTextAlignmentCenter;
    label.backgroundColor = [UIColor clearColor];
    return label;
}

- (CGFloat)mj_textWith {
    CGFloat stringWidth = 0;
    CGSize size = CGSizeMake(MAXFLOAT, MAXFLOAT);
    if (self.text.length > 0) {
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
        stringWidth =[self.text
                      boundingRectWithSize:size
                      options:NSStringDrawingUsesLineFragmentOrigin
                      attributes:@{NSFontAttributeName:self.font}
                      context:nil].size.width;
#else
        
        stringWidth = [self.text sizeWithFont:self.font
                            constrainedToSize:size
                                lineBreakMode:NSLineBreakByCharWrapping].width;
#endif
    }
    return stringWidth;
}
@end

創(chuàng)建一個label并實現(xiàn)計算label寬度的方法愚墓。

7. willMoveToSuperview

上面說了,MJRefreshComponent.m文件方法調(diào)用順序是:
alloc(prepare) -> setState -> willMoveToSuperView -> layoutSubViews(placeSubviews) -> drawRect

- (void)willMoveToSuperview:(UIView *)newSuperview
{
    [super willMoveToSuperview:newSuperview];
    
    // 如果不是UIScrollView昂勉,不做任何事情
    if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) return;
    
    // 舊的父控件移除監(jiān)聽
    [self removeObservers];
    
    if (newSuperview) { // 新的父控件
        //mj_x mj_w 的設(shè)置出現(xiàn)在了MJRefreshComponent的willMoveToSuperView方法中浪册,因為這兩個值始終是不會去變的。雖然可能會橫豎屏切換岗照,但是autoresizingMask的設(shè)置就解決了這個問題村象,MJRefresh的水平方向的布局始終是定下來了笆环。
        // 設(shè)置寬度
        self.mj_w = newSuperview.mj_w;
        // 設(shè)置位置
        self.mj_x = -_scrollView.mj_insetL;
        
        // 記錄UIScrollView
        _scrollView = (UIScrollView *)newSuperview;
        // 設(shè)置永遠支持垂直彈簧效果
        _scrollView.alwaysBounceVertical = YES;
        // 記錄UIScrollView最開始的contentInset
        _scrollViewOriginalInset = _scrollView.mj_inset;
        
        // 添加監(jiān)聽
        [self addObservers];
    }
}

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    //到底這個MJRefreshStateWillRefresh標記會有什么樣的影響?drawRect是在最后才回去調(diào)用的厚者,此時視圖已經(jīng)被添加到父視圖了躁劣。通過這種方法,延緩了MJRefresh的刷新時間库菲,從而保證了父視圖的存在账忘。
    if (self.state == MJRefreshStateWillRefresh) {
        // 預(yù)防view還沒顯示出來就調(diào)用了beginRefreshing
        self.state = MJRefreshStateRefreshing;
    }
}

這個方法在UIView的整個生命周期中是會調(diào)用兩次,一次是子視圖即將添加到父視圖上的時候熙宇,還有一次是子視圖即將從父視圖移除的時候(他們的區(qū)別就是添加的時候newSuperview是有值的鳖擒,移除的時候newSuperview沒有值)。

可能有的小伙伴會對這個地方產(chǎn)生疑惑烫止,為什么要把這些初始化操作放在這個里面蒋荚?不能直接放在初始化方法中嗎?
其實只要想一下MJRerfesh的服務(wù)對象就知道了馆蠕,這里是判斷父視圖是不是scrollView以及其子類的最佳位置期升,放在初始化方法中沒法判斷父視圖,放在layoutSubViews中則太晚了互躬,而且會調(diào)用多次吓妆。

8. KVO監(jiān)聽

#pragma mark - KVO監(jiān)聽
- (void)addObservers
{
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentOffset options:options context:nil];
    [self.scrollView addObserver:self forKeyPath:MJRefreshKeyPathContentSize options:options context:nil];
    self.pan = self.scrollView.panGestureRecognizer;
    [self.pan addObserver:self forKeyPath:MJRefreshKeyPathPanState options:options context:nil];
}

- (void)removeObservers
{
    [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentOffset];
    [self.superview removeObserver:self forKeyPath:MJRefreshKeyPathContentSize];
    [self.pan removeObserver:self forKeyPath:MJRefreshKeyPathPanState];
    self.pan = nil;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    // 遇到這些情況就直接返回
    if (!self.userInteractionEnabled) return;
    
    // 這個就算看不見也需要處理
    if ([keyPath isEqualToString:MJRefreshKeyPathContentSize]) {
        [self scrollViewContentSizeDidChange:change];
    }
    
    // 看不見
    if (self.hidden) return;
    if ([keyPath isEqualToString:MJRefreshKeyPathContentOffset]) {
        [self scrollViewContentOffsetDidChange:change];
    } else if ([keyPath isEqualToString:MJRefreshKeyPathPanState]) {
        [self scrollViewPanStateDidChange:change];
    }
}

- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{}
- (void)scrollViewContentSizeDidChange:(NSDictionary *)change{}
- (void)scrollViewPanStateDidChange:(NSDictionary *)change{}

這里監(jiān)聽了contentOffset和contentSize以及scrollView的pan手勢的狀態(tài),監(jiān)聽到改變會調(diào)用相應(yīng)的方法吨铸,只不過MJRefreshComponent里面單純只是實現(xiàn)了這三個方法行拢,相應(yīng)的邏輯處理都在子類。

三. 下拉刷新

關(guān)于MJRefresh的繼承關(guān)系诞吱,可以看MJ老師自己畫的圖:
結(jié)構(gòu)圖.png

MJRefreshComponent是不能直接做下拉刷新的舟奠,它的子類才可以。

1. MJRefreshHeader

直接繼承于MJRefreshComponent
下拉刷新控件房维,負責(zé)監(jiān)控用戶下拉的狀態(tài)沼瘫,這個控件沒添加子控件,直接使用是空白咙俩。

2. MJRefreshStateHeader

繼承于MJRefreshHeader
這個控件添加了兩個label耿戚,一個顯示刷新時間,一個顯示刷新狀態(tài)阿趁,效果圖如下:

MJRefreshStateHeader.png

① MJRefreshNormalHeader

繼承于MJRefreshStateHeader膜蛔,這個控件在兩個label的基礎(chǔ)上又添加了箭頭和菊花,效果圖如下:

MJRefreshNormalHeader.png

② MJRefreshGifHeader

也是繼承于MJRefreshStateHeader脖阵,這個控件在兩個label的基礎(chǔ)上又添加了Gif圖片皂股,使用的時候需要子類化這個控件重寫prepare方法。

- (void)prepare
{
    [super prepare];
    
    // 設(shè)置普通狀態(tài)的動畫圖片
    NSMutableArray *idleImages = [NSMutableArray array];
    for (NSUInteger i = 1; i<=60; i++) {
        UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"dropdown_anim__000%zd", I]];
        [idleImages addObject:image];
    }
     [self setImages:idleImages forState:MJRefreshStateIdle];
    
    // 設(shè)置即將刷新狀態(tài)的動畫圖片(一松開就會刷新的狀態(tài))
    NSMutableArray *refreshingImages = [NSMutableArray array];
    for (NSUInteger i = 1; i<=3; i++) {
        UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"dropdown_loading_0%zd", I]];
        [refreshingImages addObject:image];
    }
    [self setImages:refreshingImages forState:MJRefreshStatePulling];
    
    // 設(shè)置正在刷新狀態(tài)的動畫圖片
    [self setImages:refreshingImages forState:MJRefreshStateRefreshing];
}

效果圖如下:
MJRefreshGifHeader.png

可以看出命黔,如果我們想自定義下拉刷新呜呐,可以根據(jù)需求自定義控件繼承于MJRefreshStateHeader就斤、MJRefreshNormalHeader、MJRefreshGifHeader蘑辑。

四. 上拉刷新

關(guān)于MJRefresh的繼承關(guān)系洋机,可以看MJ老師自己畫的圖:
結(jié)構(gòu)圖.png

MJRefreshComponent是不能直接做下拉刷新的,它的子類才可以洋魂。

1. MJRefreshFooter

繼承于MJRefreshComponent
上拉刷新控件的根控件绷旗,實現(xiàn)了創(chuàng)建上拉控件的方法以及抽取了上拉控件必須的方法。

2. MJRefreshAutoFooter

繼承于MJRefreshFooter
會自動刷新的上拉刷新控件忧设,不需要手動釋放才刷新刁标,不會回彈到底部,沒添加子控件直接使用是空白址晕。

MJRefreshAutoStateFooter

繼承于MJRefreshAutoFooter
添加了一個label的上拉刷新膀懈,示意圖如下:

MJRefreshAutoStateFooter.png

① MJRefreshAutoNormalFooter

繼承于MJRefreshAutoStateFooter
在一個label的基礎(chǔ)上又添加了菊花的上拉刷新,示意圖如下:

MJRefreshAutoNormalFooter.png

② MJRefreshAutoGifFooter

繼承于MJRefreshAutoStateFooter
在一個label的基礎(chǔ)上又添加imageV動圖的上拉刷新谨垃,示意圖如下:

MJRefreshAutoGifFooter.png

3. MJRefreshBackFooter

繼承于MJRefreshFooter
上拉需要手動釋放才會刷新的上拉刷新控件启搂,會回彈到底部,沒添加子控件直接使用是空白刘陶。

MJRefreshBackStateFooter

繼承于MJRefreshBackFooter
添加了一個label的上拉刷新胳赌,示意圖如下:

MJRefreshBackStateFooter.png

① MJRefreshBackNormalFooter

繼承于MJRefreshBackStateFooter
在一個label的基礎(chǔ)上又添加箭頭和菊花的上拉刷新,示意圖如下:

MJRefreshBackNormalFooter.png

② MJRefreshBackGifFooter

也是繼承于MJRefreshBackStateFooter
在一個label的基礎(chǔ)上又添加了imageV動圖的上拉刷新匙隔,示意圖如下:

MJRefreshBackGifFooter.png

如果我們想自定義上拉刷新疑苫,可以根據(jù)需求自定義上拉控件繼承于StateFooter、NormalFooter纷责、GifFooter捍掺。

github地址:MJRefresh

待完整...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市再膳,隨后出現(xiàn)的幾起案子挺勿,更是在濱河造成了極大的恐慌,老刑警劉巖喂柒,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件不瓶,死亡現(xiàn)場離奇詭異,居然都是意外死亡灾杰,警方通過查閱死者的電腦和手機蚊丐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吭露,“玉大人吠撮,你說我怎么就攤上這事〗哺停” “怎么了泥兰?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長题禀。 經(jīng)常有香客問我鞋诗,道長,這世上最難降的妖魔是什么迈嘹? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任削彬,我火速辦了婚禮,結(jié)果婚禮上秀仲,老公的妹妹穿的比我還像新娘融痛。我一直安慰自己,他們只是感情好神僵,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布雁刷。 她就那樣靜靜地躺著,像睡著了一般保礼。 火紅的嫁衣襯著肌膚如雪沛励。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天炮障,我揣著相機與錄音目派,去河邊找鬼。 笑死胁赢,一個胖子當著我的面吹牛企蹭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播智末,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼谅摄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吹害?” 一聲冷哼從身側(cè)響起螟凭,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎它呀,沒想到半個月后螺男,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡纵穿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年下隧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谓媒。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡淆院,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出句惯,到底是詐尸還是另有隱情土辩,我是刑警寧澤支救,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站拷淘,受9級特大地震影響各墨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜启涯,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一贬堵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧结洼,春花似錦黎做、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挽铁,卻和暖如春伟桅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叽掘。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工楣铁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人更扁。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓盖腕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親浓镜。 傳聞我的和親對象是個殘疾皇子溃列,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348