android ui 學(xué)習(xí)系列 -自定義Behavior (1) - 相關(guān)原理

寫(xiě)在開(kāi)頭

不吐不快啊,關(guān)于自定義Behavior 這個(gè)東東真是讓我撓頭啊,N 多回調(diào)方法,看了很多資料,這 N 多回調(diào)方法差不多清楚了,但是又出來(lái)一個(gè) view 依賴綁定,概念有依賴和依賴的 view,到底誰(shuí)提供滾動(dòng)事件,誰(shuí)來(lái)消費(fèi)滾動(dòng)事件,這個(gè)仔細(xì)看了那是很多資料才算是明白。那么問(wèn)題又來(lái)了,2個(gè) view 要建立依賴關(guān)系,才能實(shí)現(xiàn)滾動(dòng)聯(lián)動(dòng),那么為啥我寫(xiě)一個(gè)自定義 Behavior 消費(fèi)滾動(dòng)事件了,我這個(gè)自定義 Behavior 沒(méi)有和任何 view 實(shí)現(xiàn)依賴綁定啊......

這里面一堆的相關(guān)概念和原理,真是讓人撓頭啊,真是費(fèi)了不少時(shí)間找資料,才總算是搞明白了,在這里說(shuō)一句 : 真 NM 費(fèi)勁 !!!

在這里非常感謝這篇文章:

把 5.0 的 nestedScrolling 嵌套滾動(dòng)和Behavior解釋的很清楚。下面的內(nèi)容我也是把文章里面 大段的理論簡(jiǎn)單描述一下,便于理解,還是推薦大家去詳細(xì)看這篇文章,想要搞懂 5.0 的 nestedScrolling 嵌套滾動(dòng),非這篇博文不行。


NestedScrollingParent # NestedScrollingChild

在以前,我們要實(shí)現(xiàn)控件滾動(dòng)間的聯(lián)動(dòng),只能去監(jiān)聽(tīng)滾動(dòng)控件,在這個(gè)滾動(dòng)控件上注冊(cè)監(jiān)聽(tīng)器,或者是寫(xiě)一個(gè)自定義 view,在滾動(dòng)事件里去操作其他的 view,實(shí)現(xiàn)狀態(tài)變換。這樣呢滾動(dòng)效果代碼就和具體的業(yè)務(wù)代碼放在一起了,無(wú)法分離,自定義 view 的方式也是很不靈活,所以呢隨著 5.0的推出,在 MD 中 google 推出了一套新的滾動(dòng)監(jiān)聽(tīng)套件,就是標(biāo)題中的 NestedScrollingParent / NestedScrollingChild

對(duì)于控件滾動(dòng)間的聯(lián)動(dòng)效果來(lái)說(shuō),分2種角色:一種是產(chǎn)生發(fā)送滾動(dòng)事件,另一種是消費(fèi)滾動(dòng)事件,因此 google 對(duì)這2種角色分別抽象除了接口:

  • NestedScrollingChild :產(chǎn)生發(fā)送滾動(dòng)事件
  • NestedScrollingParent :消費(fèi)滾動(dòng)事件

其中分別有 NestedScrollingParentHelper / NestedScrollingChildrenHelper輔助類來(lái)幫助處理的大部分邏輯


NestedScrollView.png

借著這張圖我們可以看到這2個(gè)接口全部信息和所有回調(diào)方法。

child 的滾動(dòng)邏輯如下:

滾動(dòng)的簡(jiǎn)單邏輯順序:

  • 如果要準(zhǔn)備開(kāi)始滑動(dòng)了,需要告訴 Parent,Child 要準(zhǔn)備進(jìn)入滑動(dòng)狀態(tài)了,調(diào)用
    startNestedScroll()。
  • Child 在滑動(dòng)之前,先問(wèn)一下你的 Parent 是否需要滑動(dòng),也就是調(diào)用
    dispatchNestedPreScroll()。如果父類消耗了部分滑動(dòng)事件,Child 需要重新計(jì)算一下父類消耗后剩下給 Child 的滑動(dòng)距離余量。然后,Child 自己進(jìn)行余下的滑動(dòng)。
  • 最后,如果滑動(dòng)距離還有剩余,Child 就再問(wèn)一下,Parent 是否需要在繼續(xù)滑動(dòng)你剩下的距離,也就是調(diào)用 dispatchNestedScroll()。

從事件分發(fā)的角度來(lái)看:

  • case MotionEvent.ACTION_DOWN:
    • child 先回調(diào) startNestedScroll 方法,里面?zhèn)魅氲氖菨L動(dòng)方向,startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
    • startNestedScroll 方法又會(huì)去詢問(wèn) Parent 的 onStartNestedScroll / onNestedScrollAccepted 方法,只要 Parent 愿意優(yōu)先處理這次的滑動(dòng)事件,在結(jié)束的時(shí)候 Parent 還會(huì)收到 onStopNestedScroll 回調(diào)
  • case MotionEvent.ACTION_MOVE:
    • 在消費(fèi)滾動(dòng)事件之前,會(huì)提供一個(gè)讓 Parent 實(shí)現(xiàn)聯(lián)合滾動(dòng)的機(jī)會(huì),因此在 child 滾動(dòng)之前, Parent 可以消費(fèi)一部分或者全部的滑動(dòng)事件,注意若是 parent 先消費(fèi)了部分滾動(dòng)數(shù)值, child 是無(wú)效再去消費(fèi)這部分滾動(dòng)數(shù)值的
      • child 調(diào)用 dispatchNestedPreScroll 方法
      • dispatchNestedPreScroll 方法會(huì)調(diào)用 parent 的 onNestedPreScroll
    • 在 child 消費(fèi)完滾動(dòng)數(shù)值后,會(huì)再去詢問(wèn) parent 還要不要滾動(dòng)
      • child 調(diào)用 dispatchNestedScroll 方法
      • dispatchNestedScroll 方法會(huì)調(diào)用 parent 的 onNestedScroll 方法
  • case MotionEvent.ACTION_CANCEL | MotionEvent.ACTION_UP:
    • 在滾動(dòng)結(jié)束后,會(huì)分別調(diào)用 child 和 parent 的 stopNestedScroll方法
      • stopNestedScroll();

parent 的滾邏輯如下:

  • 滑動(dòng)動(dòng)作是 Child主動(dòng)發(fā)起的,Parent 接收滑動(dòng)回調(diào)并作出響應(yīng)。從上面的 Child 分析可知,滑動(dòng)開(kāi)始的調(diào)用 startNestedScroll(),Parent收到 onStartNestedScroll() 回調(diào),決定是否需要配合 Child 一起進(jìn)行處理滑動(dòng),如果需要配合,還會(huì)回調(diào) onNestedScrollAccepted()

  • 每次滑動(dòng)前,Child 先詢問(wèn) Parent 是否需要滑動(dòng),即 dispatchNestedPreScroll() ,這就回調(diào)到 Parent 的 onNestedPreScroll(),Parent 可以在這個(gè)回調(diào)中消費(fèi)掉 Child 的 Scroll 事件,也就是優(yōu)先于 Child 滑動(dòng)

  • Child 滑動(dòng)以后,會(huì)調(diào)用 dispatchNestedScroll() ,回調(diào)到 Parent 的 onNestedScroll() ,這里就是 Child 滑動(dòng)后,剩下的給 Parent 處理,也就是后于 Child 滑動(dòng)

  • 最后,滑動(dòng)結(jié)束 Child 調(diào)用 stopNestedScroll,回調(diào) Parent 的 onStopNestedScroll() 表示本次處理結(jié)束

其實(shí)不寫(xiě) parent 的邏輯思路 ,單看 child 的也能知道的。

總之我寫(xiě)的比較簡(jiǎn)單,這樣便于理解,想看詳細(xì)的去看上面貼出的地址,那篇文章寫(xiě)的很詳細(xì)。這就是 Google 新的嵌套滾動(dòng)的核心,在具體滾動(dòng)控件消費(fèi)滾動(dòng)數(shù)值的前后都去問(wèn)問(wèn)有誰(shuí)需要消費(fèi)滾動(dòng)數(shù)值,這樣就實(shí)現(xiàn)了聯(lián)動(dòng)的效果。你可以選擇我們先消費(fèi)滾動(dòng)事件人,然后具體的滾動(dòng)控件再滾動(dòng)剩下的值。或者選擇跟隨滾動(dòng)控件滾動(dòng)之后再消費(fèi)滾動(dòng)值。

Behavior 扮演的角色

上面說(shuō)了 google 的 nestedScrolling 嵌套滾動(dòng)的實(shí)現(xiàn)思路,那么 Behavior 具體在這期中是個(gè)什么位置呢,為啥我們要使用 Behavior 呢

從 MD 的控件使用思路上來(lái)看,NestedScrollView / RecyclerView 實(shí)現(xiàn)了 NestedScrollingChild 接口,發(fā)送滾動(dòng)事件。CoordinatorLayout 一定要作跟布局使用,CoordinatorLayout 實(shí)現(xiàn)了 NestedScrollingParent 接口,他遍歷所有的直接子 view,找到期中實(shí)現(xiàn)了 NestedScrollingChild 接口的可滾動(dòng) view, 然后把自己 set 進(jìn)去,實(shí)現(xiàn)和可滾動(dòng)控件的綁定,進(jìn)而可以作為跟容器分發(fā)滾動(dòng)事件,至于如何分發(fā)滾動(dòng)事件,這里就用到 Behavior 了。CoordinatorLayout 本身并不直接實(shí)現(xiàn) NestedScrollingChild 接口,而是把相關(guān)方法再次抽象成一個(gè) Behavior 接口拋出去,交給需要的直接子 view 去實(shí)現(xiàn),自己作為 Behavior 接口的代理,NestedScrollingParent 的每個(gè)方法 CoordinatorLayout 都會(huì)遍歷所有直接子 view 獲取其中的 Behavior ,執(zhí)行對(duì)應(yīng)的方法,從而實(shí)現(xiàn)在跟節(jié)點(diǎn)上分發(fā)滾動(dòng)事件。

我們來(lái)看一個(gè) CoordinatorLayout 具體的方法:

   // 參數(shù)child:當(dāng)前實(shí)現(xiàn)`NestedScrollingParent`的ViewParent包含觸發(fā)嵌套滾動(dòng)的直接子view對(duì)象
   // 參數(shù)target:觸發(fā)嵌套滾動(dòng)的view  (在這里如果不涉及多層嵌套的話,child和target)是相同的
   // 參數(shù)nestedScrollAxes:就是嵌套滾動(dòng)的滾動(dòng)方向了.垂直或水平方法
   //返回參數(shù)代表當(dāng)前ViewParent是否可以觸發(fā)嵌套滾動(dòng)操作
   //CoordiantorLayout的實(shí)現(xiàn)上是交由子View的Behavior來(lái)決定,并回調(diào)了各個(gè)acceptNestedScroll方法,告訴它們處理結(jié)果
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        boolean handled = false;

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View view = getChildAt(i);
            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
            final Behavior viewBehavior = lp.getBehavior();
            if (viewBehavior != null) {
                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target,nestedScrollAxes);
                handled |= accepted;
                lp.acceptNestedScroll(accepted);
            } else {
                lp.acceptNestedScroll(false);
            }
        }
        return handled;
    }

可以很明顯的看到 CoordiantorLayout 就是遍歷了所有的字節(jié)子 view,獲取到子View的Behavior 來(lái)回調(diào)了相關(guān)方法。

繼續(xù)看圖:


NestedScroll2.png

Behavior 方法大全

從上面我們知道了 Behavior 實(shí)現(xiàn)的都是 CoordinatorLayout 拋出來(lái)的 NestedScrollingParent 接口的具體實(shí)現(xiàn),當(dāng)然肯定發(fā)還有其他的一些方法,這里我們先來(lái)看一看,最好結(jié)合上面我們講的 parent 的邏輯順序來(lái)看,你會(huì)發(fā)現(xiàn)簡(jiǎn)單好理解多了

Behavior 提供了幾個(gè)重要的方法:

  • layoutDependsOn
  • onDependentViewChanged
  • onStartNestedScroll
  • onNestedPreScroll
  • onNestedScroll
  • onStopNestedScroll
  • onNestedScrollAccepted
  • onNestedPreFling
  • onStartNestedScroll
  • onLayoutChild
/**
     * 表示是否給應(yīng)用了Behavior 的View 指定一個(gè)依賴的布局,通常,當(dāng)依賴的View 布局發(fā)生變化時(shí)
     * 不管被被依賴View 的順序怎樣,被依賴的View也會(huì)重新布局
     * @param parent
     * @param child 綁定behavior 的View
     * @param dependency   依賴的view
     * @return 如果child 是依賴的指定的View 返回true,否則返回false
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return super.layoutDependsOn(parent, child, dependency);
    }

    /**
     * 當(dāng)被依賴的View 狀態(tài)(如:位置、大小)發(fā)生變化時(shí),這個(gè)方法被調(diào)用
     * @param parent
     * @param child
     * @param dependency
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        return super.onDependentViewChanged(parent, child, dependency);
    }

    /**
     *  當(dāng)coordinatorLayout 的子View試圖開(kāi)始嵌套滑動(dòng)的時(shí)候被調(diào)用。當(dāng)返回值為true的時(shí)候表明
     *  coordinatorLayout 充當(dāng)nested scroll parent 處理這次滑動(dòng),需要注意的是只有當(dāng)返回值為true
     *  的時(shí)候,Behavior 才能收到后面的一些nested scroll 事件回調(diào)(如:onNestedPreScroll、onNestedScroll等)
     *  這個(gè)方法有個(gè)重要的參數(shù)nestedScrollAxes,表明處理的滑動(dòng)的方向。
     *
     * @param coordinatorLayout 和Behavior 綁定的View的父CoordinatorLayout
     * @param child  和Behavior 綁定的View
     * @param directTargetChild
     * @param target
     * @param nestedScrollAxes 嵌套滑動(dòng) 應(yīng)用的滑動(dòng)方向,看 {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
     * @return
     */
    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    /**
     * 嵌套滾動(dòng)發(fā)生之前被調(diào)用
     * 在nested scroll child 消費(fèi)掉自己的滾動(dòng)距離之前,嵌套滾動(dòng)每次被nested scroll child
     * 更新都會(huì)調(diào)用onNestedPreScroll。注意有個(gè)重要的參數(shù)consumed,可以修改這個(gè)數(shù)組表示你消費(fèi)
     * 了多少距離。假設(shè)用戶滑動(dòng)了100px,child 做了90px 的位移,你需要把 consumed[1]的值改成90,
     * 這樣coordinatorLayout就能知道只處理剩下的10px的滾動(dòng)。
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param dx  用戶水平方向的滾動(dòng)距離
     * @param dy  用戶豎直方向的滾動(dòng)距離
     * @param consumed
     */
    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
    }

    /**
     * 進(jìn)行嵌套滾動(dòng)時(shí)被調(diào)用
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param dxConsumed target 已經(jīng)消費(fèi)的x方向的距離
     * @param dyConsumed target 已經(jīng)消費(fèi)的y方向的距離
     * @param dxUnconsumed x 方向剩下的滾動(dòng)距離
     * @param dyUnconsumed y 方向剩下的滾動(dòng)距離
     */
    @Override
    public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
    }

    /**
     *  嵌套滾動(dòng)結(jié)束時(shí)被調(diào)用,這是一個(gè)清除滾動(dòng)狀態(tài)等的好時(shí)機(jī)。
     * @param coordinatorLayout
     * @param child
     * @param target
     */
    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
        super.onStopNestedScroll(coordinatorLayout, child, target);
    }

    /**
     * onStartNestedScroll返回true才會(huì)觸發(fā)這個(gè)方法,接受滾動(dòng)處理后回調(diào),可以在這個(gè)
     * 方法里做一些準(zhǔn)備工作,如一些狀態(tài)的重置等。
     * @param coordinatorLayout
     * @param child
     * @param directTargetChild
     * @param target
     * @param nestedScrollAxes
     */
    @Override
    public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    /**
     * 用戶松開(kāi)手指并且會(huì)發(fā)生慣性動(dòng)作之前調(diào)用,參數(shù)提供了速度信息,可以根據(jù)這些速度信息
     * 決定最終狀態(tài),比如滾動(dòng)Header,是讓Header處于展開(kāi)狀態(tài)還是折疊狀態(tài)。返回true 表
     * 示消費(fèi)了fling.
     *
     * @param coordinatorLayout
     * @param child
     * @param target
     * @param velocityX x 方向的速度
     * @param velocityY y 方向的速度
     * @return
     */
    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }

    //可以重寫(xiě)這個(gè)方法對(duì)子View 進(jìn)行重新布局
    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
        return super.onLayoutChild(parent, child, layoutDirection);
    }

或者看這個(gè)理解,這個(gè)好理解:

  • NestedScrollingParent : 下文簡(jiǎn)稱 NSP

  • NestedScrollingChild : 下文簡(jiǎn)稱 NSC

  • onStartNestedScroll
    用戶按下手指時(shí)觸發(fā),詢問(wèn) NSP 是否要處理這次滑動(dòng)操作,如果返回 true 則表示“我要處理這次滑動(dòng)”,如果返回 false 則表示“我不 care 你的滑動(dòng),你想咋滑就咋滑”,后面的一系列回調(diào)函數(shù)就不會(huì)被調(diào)用了。它有一個(gè)關(guān)鍵的參數(shù),就是滑動(dòng)方向,表明了用戶是垂直滑動(dòng)還是水平滑動(dòng),本例子只需考慮垂直滑動(dòng),因此判斷滑動(dòng)方向?yàn)榇怪睍r(shí)就處理這次滑動(dòng),否則就不 care

  • onNestedScrollAccepted
    當(dāng) NSP 接受要處理本次滑動(dòng)后,這個(gè)回調(diào)被調(diào)用,我們可以做一些準(zhǔn)備工作,比如讓之前的滑動(dòng)動(dòng)畫(huà)結(jié)束。

  • onNestedPreScroll
    當(dāng) NSC 即將被滑動(dòng)時(shí)調(diào)用,在這里你可以做一些處理。值得注意的是,這個(gè)方法有一個(gè)參數(shù) int[] consumed,你可以修改這個(gè)數(shù)組來(lái)表示你到底處理掉了多少像素。假設(shè)用戶滑動(dòng)了 100px,你做了 90px 的位移,那么就需要把 consumed[1] 改成 90(下標(biāo) 0、1 分別對(duì)應(yīng) x、y 軸),這樣 NSC 就能知道,然后繼續(xù)處理剩下的 10px。

  • onNestedScroll
    上一個(gè)方法結(jié)束后,NSC 處理剩下的距離。比如上面還剩 10px,這里 NSC 滾動(dòng) 2px 后發(fā)現(xiàn)已經(jīng)到頭了,于是 NSC 結(jié)束其滾動(dòng),調(diào)用該方法,并將 NSC 處理剩下的像素?cái)?shù)作為參數(shù)(dxUnconsumed、dyUnconsumed)傳過(guò)來(lái),這里傳過(guò)來(lái)的就是 8px。參數(shù)中還會(huì)有 NSC 處理過(guò)的像素?cái)?shù)(dxConsumed、dyConsumed)。這個(gè)方法主要處理一些越界后的滾動(dòng)

  • onNestedPreFling
    用戶松開(kāi)手指并且會(huì)發(fā)生慣性滾動(dòng)之前調(diào)用。參數(shù)提供了速度信息,我們這里可以根據(jù)速度,決定最終的狀態(tài)是展開(kāi)還是折疊,并且啟動(dòng)滑動(dòng)動(dòng)畫(huà)。通過(guò)返回值我們可以通知 NSC 是否自己還要進(jìn)行滑動(dòng)滾動(dòng),一般情況如果面板處于中間態(tài),我們就不讓 NSC 接著滾了,因?yàn)槲覀冞€要用動(dòng)畫(huà)把面板完全展開(kāi)或者完全折疊。

  • onStopNestedScroll

一切滾動(dòng)停止后調(diào)用,如果不會(huì)發(fā)生慣性滾動(dòng),fling 相關(guān)方法不會(huì)調(diào)用,直接執(zhí)行到這里。這里我們做一些清理工作,當(dāng)然有時(shí)也要處理中間態(tài)問(wèn)題。

自定義 Behavior 可以分2種實(shí)現(xiàn)思路:

  • 某個(gè) view 監(jiān)聽(tīng)另一個(gè) view 的狀態(tài)變化,例如大小、位置、顯示狀態(tài)等
    這情況需要重寫(xiě) layoutDependsOn 和 onDependentViewChanged 方法
  • 某個(gè) view 監(jiān)聽(tīng) CoordinatorLayout 內(nèi)的 NestedScrollingChild 的接口實(shí)現(xiàn)類的滑動(dòng)狀態(tài)
    這情況需要重寫(xiě) onStartNestedScroll 和 onNestedPreScroll 系列方法,這就是NestedScrollingParent 的思路范圍了

結(jié)尾

說(shuō)到這里基本 nestedScrolling 嵌套滾動(dòng)原理和 自定義Behavior 的原理接都清楚了,剩下的我們?nèi)ザ喽鄬?shí)踐才能靈活的掌握。文章開(kāi)頭的文章中,里面的例子有些難,不建議易上手就去看那個(gè)例子。

寫(xiě)幾個(gè)涉及的單詞:

  • ScrollAxes 滾動(dòng)方向
  • NestedFling 快速滾動(dòng),一般指手指已經(jīng)離開(kāi)屏幕,但是屏幕還在快速滾動(dòng)的狀態(tài)

參考文檔:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,634評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,653評(píng)論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,063評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,835評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,235評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,459評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,000評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,819評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,004評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,257評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,676評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,937評(píng)論 1 288
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,717評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,003評(píng)論 2 374

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