Android Animation運(yùn)行原理詳解

1. 前言

作為Android程序員兰粉,或者是想要去模仿一些酷炫的效果秀又,或者是為了實(shí)現(xiàn)視覺的變態(tài)需求,或者是壓抑不住內(nèi)心的創(chuàng)造欲想要炫技系吩,我們不可避免地需要做各種動(dòng)畫。Android中妒蔚,動(dòng)畫主要分為幀動(dòng)畫穿挨、插間動(dòng)畫以及屬性動(dòng)畫。幀動(dòng)畫最為簡(jiǎn)單肴盏,是用一系列的素材作為關(guān)鍵幀逐幀播放科盛,常用于制作加載動(dòng)畫,其工作量主要在設(shè)計(jì)部分菜皂;插間動(dòng)畫與屬性動(dòng)畫則更多地是需要開發(fā)通過控制各種動(dòng)畫參數(shù)來實(shí)現(xiàn)贞绵,只有系統(tǒng)地理解Android中動(dòng)畫運(yùn)行的原理,才能創(chuàng)作出更出色的動(dòng)畫恍飘,屬性動(dòng)畫在下一篇文章中分析榨崩,本文主要分享我在探索插間動(dòng)畫運(yùn)行原理過程中的一些收獲谴垫,包括:Matrix如何控制動(dòng)畫參數(shù);動(dòng)畫中各參數(shù)具體起什么作用母蛛;透明度動(dòng)畫翩剪、縮放動(dòng)畫、平移動(dòng)畫以及旋轉(zhuǎn)動(dòng)畫的運(yùn)行邏輯彩郊;動(dòng)畫在View的繪制過程中如何被應(yīng)用前弯。

2. Matrix介紹

在Android中,Matrix是一個(gè)3 x 3的矩陣:

Matrix 3 x 3 矩陣

Matrix可將一個(gè)點(diǎn)映射到另一個(gè)點(diǎn)焦辅,矩陣中包含了處理縮放博杖、透視以及平移的區(qū)域,從而可用于控制實(shí)現(xiàn)平移筷登、縮放剃根、旋轉(zhuǎn)等動(dòng)畫效果。強(qiáng)烈建議閱讀Android Matrix理論與應(yīng)用詳解以更深入地了解Matrix實(shí)現(xiàn)動(dòng)畫控制原理前方,這里僅摘錄其中的關(guān)鍵信息:

結(jié)論一:設(shè)對(duì)給定的圖像依次進(jìn)行了基本變化F1狈醉、F2、F3…..惠险、Fn苗傅,它們的變化矩陣分別為T1、T2班巩、T3…..渣慕、Tn,圖像復(fù)合變化的矩陣T可以表示為:T = TnTn-1…T1抱慌。

結(jié)論二:Preconcats matrix相當(dāng)于右乘矩陣逊桦,Postconcats matrix相當(dāng)于左乘矩陣。

Matrix還給我們提供了各種友好的接口來組合生成復(fù)雜的動(dòng)畫抑进,舉個(gè)例子:假如我們想要實(shí)現(xiàn)一個(gè)平移(a,b)之后旋轉(zhuǎn)(c,d)的動(dòng)畫强经,那用Matrix的實(shí)現(xiàn)代碼就是這樣的:

    Matrix matrix = new Matrix();
    matrix.setTranslate(a, b);
    matrix.postScale(c, d);

3. Animation運(yùn)行原理分析

(1)基本屬性介紹

使用過Animation的同學(xué)對(duì)下述基本屬性應(yīng)該非常熟悉,這里為了文章完整性寺渗,特地贅述一下:

  • mStartTime:動(dòng)畫實(shí)際開始時(shí)間
  • mStartOffset:動(dòng)畫延遲時(shí)間
  • mFillEnabled:mFillBefore及mFillAfter是否使能
  • mFillBefore:動(dòng)畫結(jié)束之后是否需要進(jìn)行應(yīng)用動(dòng)畫
  • mFillAfter:動(dòng)畫開始之前是否需要進(jìn)行應(yīng)用動(dòng)畫
  • mDuration:?jiǎn)未蝿?dòng)畫運(yùn)行時(shí)長(zhǎng)
  • mRepeatMode:動(dòng)畫重復(fù)模式(RESTART匿情、REVERSE)
  • mRepeatCount:動(dòng)畫重復(fù)次數(shù)(INFINITE,直接值)
  • mInterceptor:動(dòng)畫插間器
  • mListener:動(dòng)畫開始信殊、結(jié)束炬称、重復(fù)回調(diào)監(jiān)聽器

雖然大部分都知道上面這些屬性怎么用,但是可能還是有一些人對(duì)這些字段為什么有這樣的作用不甚明白鸡号,于是我們就來分析一下转砖。

(2)計(jì)算動(dòng)畫數(shù)據(jù)

Animation在其getTransformation函數(shù)被調(diào)用時(shí)會(huì)計(jì)算一幀動(dòng)畫數(shù)據(jù),而上面這些屬性基本都是在計(jì)算動(dòng)畫數(shù)據(jù)時(shí)發(fā)光發(fā)熱鲸伴,我們先看看getTransformation函數(shù)的運(yùn)行邏輯:

  1. startTimeSTART_ON_FIRST_FRAME(值為-1)時(shí)府蔗,將startTime設(shè)定為curTime
  2. 計(jì)算當(dāng)前動(dòng)畫進(jìn)度:
    normalizedTime = (curTime - (startTime + startOffset))/duration
  3. mFillEnabled==false:將normalisedTime夾逼至[0.0f, 1.0f]
  4. 判斷是否需要計(jì)算動(dòng)畫數(shù)據(jù):
    • normalisedTime在[0.0f, 1.0f],需計(jì)算動(dòng)畫數(shù)據(jù)
    • normalisedTime不在[0.0f, 1.0f]:
      • normalisedTime<0.0f, 僅當(dāng)mFillBefore==true時(shí)才計(jì)算動(dòng)畫數(shù)據(jù)
      • normalisedTime>1.0f, 僅當(dāng)mFillAfter==true時(shí)才計(jì)算動(dòng)畫數(shù)據(jù)
  5. 若需需要計(jì)算動(dòng)畫數(shù)據(jù):
    • 若當(dāng)前為第一幀動(dòng)畫汞窗,觸發(fā)mListener.onAnimationStart
    • mFillEnabled==false:將normalisedTime夾逼至[0.0f, 1.0f]
    • 根據(jù)插間器mInterpolator調(diào)整動(dòng)畫進(jìn)度:
      interpolatedTime = mInterpolator.getInterpolation(normalizedTime)
    • 若動(dòng)畫反轉(zhuǎn)標(biāo)志位mCycleFliptrue姓赤,則
      interpolatedTime = 1.0 - normalizedTime
    • 調(diào)用動(dòng)畫更新函數(shù)applyTransformation(interpolatedTime, transformation)計(jì)算出動(dòng)畫數(shù)據(jù)
  6. 若夾逼之前normalisedTime大于1.0f, 則判斷是否需繼續(xù)執(zhí)行動(dòng)畫:
    • 已執(zhí)行次數(shù)mRepeatCount等于需執(zhí)行次數(shù)mRepeated
      • 若未觸發(fā)mListener.onAnimationEnd,則觸發(fā)之
    • 已執(zhí)行次數(shù)mRepeatCount不等于需執(zhí)行次數(shù)mRepeated
      • 自增mRepeatCount
      • 重置mStartTime為-1
      • mRepeatModeREVERSE仲吏,則取反mCycleFlip
      • 觸發(fā)mListener.onAnimationRepeat

這一段是根據(jù)getTransformation源碼分析出來的不铆,建議有興趣的同學(xué)可以直接查看源碼。上面這段分析留了一個(gè)不小的懸念裹唆,那就是動(dòng)畫更新函數(shù)是什么鬼誓斥,這個(gè)函數(shù)在Animation這個(gè)抽象類中僅僅是個(gè)鉤子函數(shù),由其子類提供具體實(shí)現(xiàn)许帐,于是自然而然地引出了我們的下一個(gè)主題:主流動(dòng)畫介紹劳坑。

(3)主流動(dòng)畫分析

  • AlphaAnimation:透明度動(dòng)畫
    • 基本屬性
      • mFromAlpha:起始透明度
      • mToAlpha:終止透明度
    • applyTransformation函數(shù)實(shí)現(xiàn)
      • transformation.setAlpha(mFromAlpha + ((mToAlpha - mFromAlpha) * interpolatedTime))
  • ScaleAnimation:縮放動(dòng)畫
    • 基本屬性
      • mFromX:起始X值
      • mToX:終止X值
      • mFromY:起始Y值
      • mToY:終止Y值
      • mPivotX:縮放中心點(diǎn)X坐標(biāo)
      • mPivotY:縮放中心點(diǎn)Y坐標(biāo)
    • 屬性計(jì)算邏輯
      • mFromX、mToX成畦、mFromY距芬、mToY計(jì)算
        • Float類型scale直接值
        • Faction類型相對(duì)值
          • 相對(duì)于自身(%):百分比轉(zhuǎn)換為float直接值
          • 相對(duì)于父親(%p):根據(jù)父親size計(jì)算出size直接值,然后計(jì)算與本身size的百分比循帐,最后轉(zhuǎn)換為float直接值
        • Dimension類型size直接值:計(jì)算與本身size的百分比框仔,然后轉(zhuǎn)換為float直接值
      • mPivotX、mPivotY計(jì)算
        • ABSOLUTE類型直接值
        • RELATIVE_TO_SELF類型相對(duì)值:相對(duì)值乘以自身size得到直接值
        • RELATIVE_TO_PARENT類型相對(duì)值:相對(duì)值乘以父親size得到直接值
    • applyTransformation函數(shù)實(shí)現(xiàn)
      • sx = mFromX + ((mToX - mFromX) * interpolatedTime)
      • sy = mFromY + ((mToY - mFromY) * interpolatedTime)
      • 是否設(shè)定縮放中心點(diǎn):
        • 若mPivotX==0 且 mPivotY==0:transformation.getMatrix().setScale(sx, sy)
        • 否則:transformation.getMatrix().setScale(sx, sy, mPivotX, mPivotY)
  • TranslateAnimation:平移動(dòng)畫
    • 基本屬性
      • mFromXDelta
      • mToXDelta
      • mFromYDelta
      • mToYDelta
    • 屬性計(jì)算邏輯
      • 同ScaleAnimation中mPivotX拄养、mPivotY的計(jì)算邏輯
    • applyTransformation函數(shù)實(shí)現(xiàn)
      • dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime)
      • dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime)
      • transformation.getMatrix().setTranslate(dx, dy)
  • RotateAnimation:旋轉(zhuǎn)動(dòng)畫
    • 基本屬性
      • mFromDegrees
      • mToDegrees
      • mPivotX
      • mPivotY
    • 屬性計(jì)算邏輯
      • mFromDegrees离斩、mToDegrees均為角度(°)絕對(duì)值
      • mPivotX、mPivotY計(jì)算邏輯同ScaleAnimation
    • applyTransformation函數(shù)實(shí)現(xiàn)
      • 是否設(shè)定縮放中心點(diǎn):
        • 若mPivotX==0 且 mPivotY==0:transformation.getMatrix().setScale(sx, sy)
        • 否則:transformation.getMatrix().setScale(sx, sy, mPivotX, mPivotY)

透明度瘪匿、縮放跛梗、平移以及旋轉(zhuǎn)是最基本的動(dòng)畫,通過組合這些動(dòng)畫可以實(shí)現(xiàn)各種不一樣的酷炫的效果柿顶,但是怎么才能實(shí)現(xiàn)這些動(dòng)畫的組合茄袖,這就不得不提到AnimationSet了。

(4) AnimationSet分析

  • AnimationSet是動(dòng)畫集合嘁锯,用于組合運(yùn)行多個(gè)動(dòng)畫宪祥,僅支持playTogether模式。
  • AnimationSet繼承了Animation的字段家乘,但是字段的應(yīng)用有一些變化:
  • duration, repeatMode, fillBefore, fillAfter:這些屬性會(huì)傳遞應(yīng)用到所有的子Animation
  • repeatCount, fillEnabled:這些屬性在AnimationSet中不被應(yīng)用
  • startOffset, shareInterpolator:這些屬性僅用于AnimationSet蝗羊,不會(huì)傳遞至子Animation
  • 4.0以前在xml中設(shè)置duration, repeatMode, fillBefore, fillAfter, startOffset不會(huì)被應(yīng)用,但是4.0之后再xml中設(shè)定這些屬性跟運(yùn)行時(shí)設(shè)定效果一致
  • 一些值的計(jì)算邏輯:
    • duration:
      • 缺省時(shí)仁锯,取所有子Animation中最長(zhǎng)的duration耀找;
      • 已設(shè)定時(shí),返回mDuration
    • hasAlpha、willChangeTransformationMatrix野芒、willChangeBounds:當(dāng)有子Animation時(shí)蓄愁,所有子Animation的值取“或”
    • startTime:取所有子Animation中最小的startTime
    • 子Animation中startOffset處理:
      • 保存子Animation的原始startOffset
      • 設(shè)置子Animation的startOffset為原始startOffset與AnimationSet的startOffset之和
      • 保存的原始startOffset在AnimationSet.clear是用于恢復(fù)各子Animation的startOffset
  • applyTransformation函數(shù)實(shí)現(xiàn)
    • 順序調(diào)用子Animation的applyTransformation,然后利用Transformation.compose組合所有子Animation返回的Transformation作為該AnimationSet當(dāng)前幀的變換狀態(tài)
    • started及more值取所有子Animation對(duì)應(yīng)值的“或”
    • ended值取所有子Animation對(duì)應(yīng)值的“與”
    • 當(dāng)started第一次為true時(shí)狞悲,調(diào)用AnimationSet的mListener.onAnimationStart
    • 當(dāng)ended第一次為true(此時(shí)所有子Animation均結(jié)束)時(shí)撮抓,調(diào)用AnimationSet的mListener.onAnimationEnd

介紹完了主流動(dòng)畫以及組合動(dòng)畫,是不是Animation就介紹完了摇锋?其實(shí)不然丹拯,里面還漏掉了一個(gè)重要角色,那就是計(jì)算得到的動(dòng)畫數(shù)據(jù)是用什么存儲(chǔ)的荸恕。實(shí)際上乖酬,Animation的動(dòng)畫函數(shù)getTransformation目的在于生成當(dāng)前幀的一個(gè)Transformation,這個(gè)Transformation采用alpha以及Matrix存儲(chǔ)了一幀動(dòng)畫的數(shù)據(jù)融求,Transformation包含兩種模式:

  • alpha模式:用于支持透明度動(dòng)畫
  • matrix模式:用于支持縮放咬像、平移以及旋轉(zhuǎn)動(dòng)畫

同時(shí),Transformation還提供了許多兩個(gè)接口用于組合多個(gè)Transformation:

  • compose:前結(jié)合(alpha相乘双肤、矩陣右乘施掏、邊界疊加)
  • postCompose:后結(jié)合(alpha相乘、矩陣左乘茅糜、邊界疊加)

至此七芭,Animation本身算介紹完整了,還差一個(gè)可用于從XML中構(gòu)建動(dòng)畫以及插間器的AnimationUtils蔑赘,這里就不做具體分析了狸驳,有興趣的同學(xué)可以自行研究。但是缩赛,到現(xiàn)在為止耙箍,我們還沒講明白是:getTransformation這個(gè)函數(shù)究竟是在哪里調(diào)用的?計(jì)算得到的動(dòng)畫數(shù)據(jù)又是怎么被應(yīng)用的酥馍?慌不要慌辩昆,待我娓娓道來,當(dāng)這些問題揭秘之后旨袒,我們就知道為什么Animation這個(gè)包要放在android.view下面以及Animation完成之后為什么View本身的屬性不會(huì)被改變汁针,于是也就知道插間動(dòng)畫(Animation)跟屬性動(dòng)畫(Animator)本質(zhì)上的區(qū)別在哪了。

4. Animation的調(diào)用

要了解Animation的調(diào)用源頭砚尽,要從Animation的基本使用View.startAnimation開始尋根溯源:

    public void startAnimation(Animation animation) {
        animation.setStartTime(Animation.START_ON_FIRST_FRAME);
        setAnimation(animation);
        invalidateParentCaches();
        invalidate(true);
    }

通過invalidate(true)函數(shù)會(huì)觸發(fā)View的重新繪制施无,由于View的繪制流程并不是本文的重點(diǎn),因此這里僅說明從View.draw是怎么走到對(duì)Animation的處理函數(shù)的:

View.draw(Canvas)
—> ViewGroup.dispatchDraw(Canvas)
—> ViewGroup.drawChild(Canvas, View, long)
—> View.draw(Canvas, ViewGroup, long)
—> View.applyLegacyAnimation(ViewGroup, long, Animation, boolean)

View.applyLegacyAnimation就是Animation大顯神通的舞臺(tái)必孤,其核心代碼主要分三個(gè)部分:

  1. 初始化Animation(僅初始化一次)

    • 調(diào)用Animation.initialize(width, height, parentWidth, parentHeight)猾骡,通過View及ParentView的Size來解析Animation中的相關(guān)數(shù)據(jù);
    • 調(diào)用Animation.initializeInvalidateRegion(left, top, right, bottom)來設(shè)定動(dòng)畫的初始區(qū)域,并在fillBefore為true時(shí)計(jì)算Animation動(dòng)畫進(jìn)度為0.0f的數(shù)據(jù)
  2. 調(diào)用getTransformation根據(jù)當(dāng)前繪制事件生成Animation中對(duì)應(yīng)幀的動(dòng)畫數(shù)據(jù)

  3. 根據(jù)動(dòng)畫數(shù)據(jù)設(shè)定重繪制區(qū)域

    • 若僅為Alpha動(dòng)畫兴想,此時(shí)動(dòng)畫區(qū)域?yàn)閂iew的當(dāng)前區(qū)域幢哨,且不會(huì)產(chǎn)生變化
    • 若包含非Alpha動(dòng)畫,此時(shí)動(dòng)畫區(qū)域需要調(diào)用Animation.getInvalidateRegion進(jìn)行計(jì)算襟企,該函數(shù)會(huì)根據(jù)上述生成動(dòng)畫數(shù)據(jù)Thransformation中的Matrix進(jìn)行計(jì)算嘱么,并與之前的動(dòng)畫區(qū)域執(zhí)行unio操作狮含,從而獲取動(dòng)畫的完整區(qū)域
    • 調(diào)用ViewGroup.invalidate(int l, int t, int r, int b)設(shè)定繪制區(qū)域

當(dāng)View.applyLegacyAnimation調(diào)用完成之后顽悼,View此次繪制的動(dòng)畫數(shù)據(jù)就構(gòu)建完成,之后便回到View.draw(Canvas, ViewGroup, long)應(yīng)用動(dòng)畫數(shù)據(jù)對(duì)視圖進(jìn)行繪制刷新几迄,其核心代碼如下:

    if (transformToApply != null) {
        if (concatMatrix) {
            if (drawingWithRenderNode) {
                    // 應(yīng)用動(dòng)畫數(shù)據(jù)
                renderNode.setAnimationMatrix(transformToApply.getMatrix());
            } else {
                canvas.translate(-transX, -transY);
                // 應(yīng)用動(dòng)畫數(shù)據(jù)
                canvas.concat(transformToApply.getMatrix());
                canvas.translate(transX, transY);
            }
            parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
        }

        float transformAlpha = transformToApply.getAlpha();
        if (transformAlpha < 1) {
            // 應(yīng)用動(dòng)畫數(shù)據(jù)
            alpha *= transformAlpha;
            parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
        }
    }

重點(diǎn)來了蔚龙,大家看到Animation產(chǎn)生的動(dòng)畫數(shù)據(jù)實(shí)際并不是應(yīng)用在View本身的,而是應(yīng)用在RenderNode或者Canvas上的映胁,這就是為什么Animation不會(huì)改變View的屬性的根本所在木羹。另一方面,我們知道Animation僅在View被繪制的時(shí)候才能發(fā)揮自己的價(jià)值解孙,這也是為什么插間動(dòng)畫被放在Android.view包內(nèi)坑填,因?yàn)樗鶹iew是真心相愛的。
文章到這弛姜,其實(shí)差不多可以結(jié)束了脐瑰,但是創(chuàng)作動(dòng)畫過程中總是會(huì)被用到的一個(gè)神器還沒出現(xiàn),這讓我有些不舍廷臼,盡管有太多人講解這一神器苍在,但是我還是毅然決然地決定抄一遍書,一來表示我對(duì)這一神器的愛荠商,另一方面也是希望讓文章更完整寂恬。

5. 插間器(Interpolator)

如果沒有插間器,Animation應(yīng)該按照時(shí)間來線性計(jì)算每一個(gè)時(shí)間點(diǎn)的動(dòng)畫幀數(shù)據(jù)莱没;當(dāng)時(shí)當(dāng)加入插件器之后初肉,我們計(jì)算動(dòng)畫幀數(shù)據(jù)時(shí)就可以更加的富有創(chuàng)造力,我可以隨心所欲地計(jì)算任一時(shí)間點(diǎn)的動(dòng)畫幀數(shù)據(jù)饰躲,可以新加速在減速牙咏,也可以先減速在加速,總之一句話属铁,我的地盤我做主眠寿。按照劇情的發(fā)展,接下來我應(yīng)該介紹常用插間器了焦蘑,但是作為一個(gè)有態(tài)度的程序員盯拱,我是不會(huì)按常理出牌的,想要了解常用插間器的實(shí)現(xiàn)原理,建議閱讀Android Animations Tutorial 5: More on Interpolators狡逢。

6. 后記

其實(shí)很早之前就看過Animation的源碼宁舰,但是當(dāng)時(shí)因?yàn)閼胁]有寫文章做筆記,這次因?yàn)轫?xiàng)目需要優(yōu)化動(dòng)畫奢浑,于是又重新擼了一遍蛮艰,在此撰文為記,以備后用雀彼。當(dāng)然壤蚜,也希望這篇分享能給大家一些收獲,非常感謝你的閱讀徊哑,如果有浪費(fèi)到你的時(shí)間袜刷,也就浪費(fèi)了,權(quán)當(dāng)看了一章湊字?jǐn)?shù)的小說莺丑,233333~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末著蟹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子梢莽,更是在濱河造成了極大的恐慌萧豆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昏名,死亡現(xiàn)場(chǎng)離奇詭異涮雷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)葡粒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門份殿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嗽交,你說我怎么就攤上這事卿嘲。” “怎么了夫壁?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵拾枣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我盒让,道長(zhǎng)梅肤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任邑茄,我火速辦了婚禮姨蝴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肺缕。我一直安慰自己左医,他們只是感情好授帕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浮梢,像睡著了一般跛十。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秕硝,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天芥映,我揣著相機(jī)與錄音,去河邊找鬼远豺。 笑死奈偏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的憋飞。 我是一名探鬼主播霎苗,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼榛做!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起内狸,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后贸桶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盒件,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年昂灵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了避凝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眨补,死狀恐怖管削,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撑螺,我是刑警寧澤含思,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站甘晤,受9級(jí)特大地震影響含潘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜线婚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一遏弱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧塞弊,春花似錦漱逸、人聲如沸缀踪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)驴娃。三九已至,卻和暖如春循集,著一層夾襖步出監(jiān)牢的瞬間唇敞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工咒彤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疆柔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓镶柱,卻偏偏與公主長(zhǎng)得像旷档,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子歇拆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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