Android事件分發(fā)機(jī)制(ViewGroup篇)

上一篇我們介紹了View的事件分發(fā)機(jī)制淤翔,不熟悉的可以先了解一下

上一篇:Android事件分發(fā)機(jī)制(View篇)

引言

本篇我們接著上一篇,來繼續(xù)學(xué)習(xí)一下Android ViewGroup的事件分發(fā)機(jī)制

本來View的事件分發(fā)機(jī)制和ViewGroup的事件分發(fā)機(jī)制是緊密聯(lián)系在一起的胆屿,但是因?yàn)槠渲械脑聿皇侨齼删淠軌蛘f清楚的,也為了方便理解偶宫,就先拆開來講非迹,然后融合起來統(tǒng)一歸納總結(jié),這樣結(jié)構(gòu)更清晰纯趋,好了憎兽,廢話不多,我們進(jìn)入正文吵冒。

正文

本篇ViewGroup的事件分發(fā)機(jī)制的場景和上一篇Android事件分發(fā)機(jī)制(View篇)開始的場景是一樣的纯命,這里不再重復(fù)。

上篇也提到過對于ViewGroup我們關(guān)注三個方法:

ViewGroup 三個方法:

  • dispatchTouchEvent (MotionEvent event)//負(fù)責(zé)事件分發(fā)
  • onInterCeptTouchEvent(MotionEvent event)//處理是否攔截當(dāng)前事件
  • onTouchEvent(MotionEvent event)//當(dāng)前View自己處理當(dāng)前事件

我們先新建一個project: ViewGroupDemo

- 新建一個MyLinearLayout繼承LinearLayout

重寫dispatchTouchEvent痹栖、onInterceptTouchEvent亿汞、onTouchEvent三個方法并打印log

/**
* @author Charay
* @data 2017/10/31
*/

public class MyLinearLayout extends LinearLayout {
private static final String TAG = MyLinearLayout.class.getSimpleName();

public MyLinearLayout(Context context) {
    super(context);
}

public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG,"--MyLinearLayout--dispatchTouchEvent---ACTION_DOWN---");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG,"--MyLinearLayout--dispatchTouchEvent---ACTION_MOVE---");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG,"--MyLinearLayout--dispatchTouchEvent---ACTION_UP---");
            break;
    }
    return super.dispatchTouchEvent(ev);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG,"--MyLinearLayout--onInterceptTouchEvent---ACTION_DOWN---");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG,"--MyLinearLayout--onInterceptTouchEvent---ACTION_MOVE---");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG,"--MyLinearLayout--onInterceptTouchEvent---ACTION_UP---");
            break;
    }
    return super.onInterceptTouchEvent(ev);
//        return true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG,"--MyLinearLayout--onTouchEvent---ACTION_DOWN---");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG,"--MyLinearLayout--onTouchEvent---ACTION_MOVE---");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG,"--MyLinearLayout--onTouchEvent---ACTION_UP---");
            break;
    }
    return  super.onTouchEvent(event);
}
}

- 新建一個MyButton繼承Button

重寫dispatchTouchEvent、onTouchEvent兩個方法并打印log

/**
* @author Charay
* @data 2017/10/31
*/

public class MyButton extends Button {
private static final String TAG = MyButton.class.getSimpleName();

public MyButton(Context context) {
    super(context);
}

public MyButton(Context context, AttributeSet attrs) {
    super(context, attrs);
}



@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG,"--MyButton--dispatchTouchEvent---ACTION_DOWN---");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG,"--MyButton--dispatchTouchEvent---ACTION_MOVE---");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG,"--MyButton--dispatchTouchEvent---ACTION_UP---");
            break;
    }
    return super.dispatchTouchEvent(ev);
}



@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG,"--MyButton--onTouchEvent---ACTION_DOWN---");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG,"--MyButton--onTouchEvent---ACTION_MOVE---");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG,"--MyButton--onTouchEvent---ACTION_UP---");
            break;
    }
    return  super.onTouchEvent(event);
}


}

在布局文件中添加 MyLinearLayoutMyButton

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>

    <com.marco.viewgroupdemo.MyLinearLayout
        android:id="@+id/my_linearlayout"
        android:layout_width="300dp"
        android:layout_height="368dp"
        android:background="@color/colorPrimaryDark"
        android:gravity="center">

        <com.marco.viewgroupdemo.MyButton
            android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"
            android:text="按鈕"
            />
    </com.marco.viewgroupdemo.MyLinearLayout>
</LinearLayout>

MainActivity中初始化 MyLinearLayoutMyButton并添加setOnTouchListener,然后打印log

public class MainActivity extends Activity {

private static final String TAG = MainActivity.class.getSimpleName();
private MyLinearLayout mMyLinearLayout;
private MyButton mMyButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mMyLinearLayout = (MyLinearLayout) findViewById(R.id.my_linearlayout);
    mMyButton = (MyButton) findViewById(R.id.my_button);


    mMyLinearLayout.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch (motionEvent.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG,"--MyLinearLayout--onTouch---ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG,"--MyLinearLayout--onTouch---ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG,"--MyLinearLayout--onTouch---ACTION_UP");
                    break;
            }


            return false;
        }
    });
    mMyButton.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch (motionEvent.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG,"--mMyButton--onTouch---ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG,"--mMyButton--onTouch---ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG,"--mMyButton--onTouch---ACTION_UP");
                    break;
            }
            return false;
        }
    });


}
}

為了能完整看到事件分發(fā)攔截的整個流程揪阿,我們在上面代碼中沒有更改任何一個方法的返回值疗我,只是打印了log

現(xiàn)在我們運(yùn)行程序,點(diǎn)擊MyButton,打印日志log如下:

11-01 11:54:02.993 E/MyLinearLayout: --MyLinearLayout--dispatchTouchEvent---ACTION_DOWN---
11-01 11:54:02.993 E/MyLinearLayout: --MyLinearLayout--onInterceptTouchEvent---ACTION_DOWN---
11-01 11:54:02.993 E/MyButton: --MyButton--dispatchTouchEvent---ACTION_DOWN---
11-01 11:54:02.994 E/MainActivity: --mMyButton--onTouch---ACTION_DOWN
11-01 11:54:02.994 E/MyButton: --MyButton--onTouchEvent---ACTION_DOWN---

11-01 11:54:03.004 E/MyLinearLayout: --MyLinearLayout--dispatchTouchEvent---ACTION_MOVE---
11-01 11:54:03.004 E/MyLinearLayout: --MyLinearLayout--onInterceptTouchEvent---ACTION_MOVE---
11-01 11:54:03.004 E/MyButton: --MyButton--dispatchTouchEvent---ACTION_MOVE---
11-01 11:54:03.005 E/MainActivity: --mMyButton--onTouch---ACTION_MOVE
11-01 11:54:03.005 E/MyButton: --MyButton--onTouchEvent---ACTION_MOVE---

11-01 11:54:03.028 E/MyLinearLayout: --MyLinearLayout--dispatchTouchEvent---ACTION_UP---
11-01 11:54:03.028 E/MyLinearLayout: --MyLinearLayout--onInterceptTouchEvent---ACTION_UP---
11-01 11:54:03.028 E/MyButton: --MyButton--dispatchTouchEvent---ACTION_UP---
11-01 11:54:03.028 E/MainActivity: --mMyButton--onTouch---ACTION_UP
11-01 11:54:03.029 E/MyButton: --MyButton--onTouchEvent---ACTION_UP---

雖然日志比較長南捂,但是不要怕吴裤,上面把ACTION_DOWN、ACTION_MOVE溺健、ACTION_UP都打印了出來麦牺,
我們只需要看一組ACTION_DOWN即可,因?yàn)橐粋€View一旦消費(fèi)了ACTION_DOWN事件,那么其他兩個事件一定都是這個View消費(fèi)枕面。

log中我們發(fā)現(xiàn)ACTION_DOWN愿卒、ACTION_MOVE缚去、ACTION_UP三個手勢動作規(guī)律是一樣的潮秘,執(zhí)行順序都是從最外層ViewGroup向內(nèi)層View(或ViewGroup傳遞):先是MyLinearLayoutdispatchTouchEvent在這個方法中先執(zhí)行onInterceptTouchEvent判斷事都攔截這個事件,如果不攔截(默認(rèn)不攔截)就傳遞給MyButton,然后事件分發(fā)給MyButtondispatchTouchEvent易结,執(zhí)行MyButtondispatchTouchEvent枕荞,由于MyButton及其父View沒有onInterceptTouchEvent方法,所以直接在dispatchTouchEvent中先判斷onTouch的返回值搞动,默認(rèn)為false躏精,再執(zhí)行MyButtononTouchEvent

如果我們在MyLinearLayout中攔截了這個事件結(jié)果將是怎樣呢?

下面我們在MyLinearLayout中的重寫的onInterceptTouchEvent中把返回值改為true

onInterceptTouchEvent

 @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG,"--MyLinearLayout--onInterceptTouchEvent---ACTION_DOWN---");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG,"--MyLinearLayout--onInterceptTouchEvent---ACTION_MOVE---");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG,"--MyLinearLayout--onInterceptTouchEvent---ACTION_UP---");
            break;
    }
//  return super.onInterceptTouchEvent(ev);
    return true;
}

然后運(yùn)行程序分別點(diǎn)擊MyButton和MyLinearLayout,發(fā)現(xiàn)打印的log是一樣的:

//點(diǎn)擊MyButton
11-02 11:07:14.207 E/MyLinearLayout: --MyLinearLayout--dispatchTouchEvent---ACTION_DOWN---
11-02 11:07:14.207 E/MyLinearLayout: --MyLinearLayout--onInterceptTouchEvent---ACTION_DOWN---
11-02 11:07:14.207 E/MainActivity: --MyLinearLayout--onTouch---ACTION_DOWN
11-02 11:07:14.207 E/MyLinearLayout: --MyLinearLayout--onTouchEvent---ACTION_DOWN---
//點(diǎn)擊MyLinearLayout
11-02 11:07:21.667 E/MyLinearLayout: --MyLinearLayout--dispatchTouchEvent---ACTION_DOWN---
11-02 11:07:21.677 E/MyLinearLayout: --MyLinearLayout--onInterceptTouchEvent---ACTION_DOWN---
11-02 11:07:21.677 E/MainActivity: --MyLinearLayout--onTouch---ACTION_DOWN
11-02 11:07:21.677 E/MyLinearLayout: --MyLinearLayout--onTouchEvent---ACTION_DOWN---

一分鐘思考一下下面兩個問題:

  1. 為什么MyButton沒有任何有關(guān)log鹦肿,而且這次還執(zhí)行了MyLinearLayout的onTouchonTouchEvent方法矗烛?
  2. 為什么我們把返回值改為true之前,只執(zhí)行了MyLinearLayout的dispatchTouchEventonInterceptTouchEvent箩溃,而沒有執(zhí)行onTouchonTouchEvent方法瞭吃?

下面我們來看一下 ViewGroup 中的源碼(android-10,即2.3.3的源碼)

dispatchTouchEvent

/**
 * {@inheritDoc}
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (!onFilterTouchEventForSecurity(ev)) {
        return false;
    }

    final int action = ev.getAction();
    final float xf = ev.getX();
    final float yf = ev.getY();
    final float scrolledXFloat = xf + mScrollX;
    final float scrolledYFloat = yf + mScrollY;
    final Rect frame = mTempRect;

    boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

    if (action == MotionEvent.ACTION_DOWN) {
        if (mMotionTarget != null) {
            // this is weird, we got a pen down, but we thought it was
            // already down!
            // XXX: We should probably send an ACTION_UP to the current
            // target.
            mMotionTarget = null;
        }
        // If we're disallowing intercept or if we're allowing and we didn't
        // intercept
        if (disallowIntercept || !onInterceptTouchEvent(ev)) {
            // reset this event's action (just to protect ourselves)
            ev.setAction(MotionEvent.ACTION_DOWN);
            // We know we want to dispatch the event down, find a child
            // who can handle it, start with the front-most child.
            final int scrolledXInt = (int) scrolledXFloat;
            final int scrolledYInt = (int) scrolledYFloat;
            final View[] children = mChildren;
            final int count = mChildrenCount;

            for (int i = count - 1; i >= 0; i--) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                        || child.getAnimation() != null) {
                    child.getHitRect(frame);
                    if (frame.contains(scrolledXInt, scrolledYInt)) {
                        // offset the event to the view's coordinate system
                        final float xc = scrolledXFloat - child.mLeft;
                        final float yc = scrolledYFloat - child.mTop;
                        ev.setLocation(xc, yc);
                        child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                        if (child.dispatchTouchEvent(ev))  {
                            // Event handled, we have a target now.
                            mMotionTarget = child;
                            return true;
                        }
                        // The event didn't get handled, try the next view.
                        // Don't reset the event's location, it's not
                        // necessary here.
                    }
                }
            }
        }
    }

    boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
            (action == MotionEvent.ACTION_CANCEL);

    if (isUpOrCancel) {
        // Note, we've already copied the previous state to our local
        // variable, so this takes effect on the next event
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    }

    // The event wasn't an ACTION_DOWN, dispatch it to our target if
    // we have one.
    final View target = mMotionTarget;
    if (target == null) {
        // We don't have a target, this means we're handling the
        // event as a regular view.
        ev.setLocation(xf, yf);
        if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
            ev.setAction(MotionEvent.ACTION_CANCEL);
            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
        }
        return super.dispatchTouchEvent(ev);
    }

    // if have a target, see if we're allowed to and want to intercept its
    // events
    if (!disallowIntercept && onInterceptTouchEvent(ev)) {
        final float xc = scrolledXFloat - (float) target.mLeft;
        final float yc = scrolledYFloat - (float) target.mTop;
        mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
        ev.setAction(MotionEvent.ACTION_CANCEL);
        ev.setLocation(xc, yc);
        if (!target.dispatchTouchEvent(ev)) {
            // target didn't handle ACTION_CANCEL. not much we can do
            // but they should have.
        }
        // clear the target
        mMotionTarget = null;
        // Don't dispatch this event to our own view, because we already
        // saw it when intercepting; we just want to give the following
        // event to the normal onTouchEvent().
        return true;
    }

    if (isUpOrCancel) {
        mMotionTarget = null;
    }

    // finally offset the event to the target's coordinate system and
    // dispatch the event.
    final float xc = scrolledXFloat - (float) target.mLeft;
    final float yc = scrolledYFloat - (float) target.mTop;
    ev.setLocation(xc, yc);

    if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
        ev.setAction(MotionEvent.ACTION_CANCEL);
        target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
        mMotionTarget = null;
    }

    return target.dispatchTouchEvent(ev);
}

代碼比較多,我們拋開干擾涣旨,只看對我們有用的

先看這行boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

disallowIntercept的含義是禁用攔截事件,下面看 if (disallowIntercept || !onInterceptTouchEvent(ev))判斷,
進(jìn)入判斷條件的情況有兩種:

  1. disallowIntercepttrue,即禁用攔截事件歪架,這時候即使攔截事件onInterceptTouchEvent(ev)返回值為true,也不會攔截
  2. disallowInterceptfalse霹陡,即允許攔截和蚪,但是不攔截onInterceptTouchEvent(ev)返回值為false

默認(rèn)情況下是允許攔截的,即disallowInterceptfalse烹棉,只有當(dāng)我們調(diào)用mMyLinearLayout.requestDisallowInterceptTouchEvent(true);的時候攒霹,即禁止攔截,disallowIntercept的值才為true.
進(jìn)入到if判斷中后浆洗,我們看

for (int i = count - 1; i >= 0; i--) {
     final View child = children[i];

        ......

        if (child.dispatchTouchEvent(ev))  {
              // Event handled, we have a target now.
              mMotionTarget = child;
              return true;
                        }
        ......
}

這時遍歷子View,把事件傳給子ViewdispatchTouchEvent催束,如果子ViewViewGroup,那就繼續(xù)遍歷,直到遍歷到子ViewView類型的辅髓,然后調(diào)用調(diào)用這個子ViewdispatchTouchEvent泣崩,之后的判斷就干我們上一篇的Android事件分發(fā)機(jī)制(View篇)原理一致了。

而我們剛才的代碼中把onInterceptTouchEvent(ev)返回值改為true洛口,則不符合上面的兩種情況矫付,因此進(jìn)入不到if判斷中,事件被攔截了第焰,因此事件到不了MyButtondispatchTouchEvent方法买优,也就不會有MyButton的任何有log

下面我們看這幾行代碼:

// The event wasn't an ACTION_DOWN, dispatch it to our target if
    // we have one.
    final View target = mMotionTarget;
    if (target == null) {
        // We don't have a target, this means we're handling the
        // event as a regular view.
        ev.setLocation(xf, yf);
        if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
            ev.setAction(MotionEvent.ACTION_CANCEL);
            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
        }
        return super.dispatchTouchEvent(ev);
    }

雖然我們在onInterceptTouchEvent(ev)中返回true攔截了這個事件,但這并不代表我們當(dāng)前MyLinearLayout就是消費(fèi)當(dāng)前事件的消費(fèi)者杀赢,因?yàn)槲覀儧]有target烘跺,所以進(jìn)入了上面代碼中,最后一行return super.dispatchTouchEvent(ev);即進(jìn)入了ViewdispatchTouchEvent(ev)脂崔,同樣跟我們上一篇Android View的事件分發(fā)機(jī)制(上)原理一致滤淳,雖然和上面for循環(huán)中一樣也是在View中,但是是有區(qū)別的砌左,這個是MyLinearLayout這個(ViewGroup)繼承的View,其操作是針對MyLinearLayout的脖咐,即之后所走的onTouchonTouchEvent也都是針對MyLinearLayout的;而for循環(huán)中的是MyButton這個(View)繼承的View汇歹,其操作是針對MyMutton的屁擅,即之后所走的onTouchonTouchEvent也都是針對MyBUtton的。

既然我們在onInterceptTouchEvent(ev)中返回true攔截了這個事件产弹,攔截后派歌,就走到了MyLinearLayout的onTouch方法,默認(rèn)返回false痰哨,然后執(zhí)行了MyLinearLayoutonTouchEvent方法胶果,所以剛好符合我們上面的log

總結(jié)

下面我們總結(jié)一下ViewGroup的事件分發(fā)機(jī)制的關(guān)鍵點(diǎn)作谭,以及和View的事件分發(fā)機(jī)制的異同

  • ViewGroup比View多了一個攔截事件的方法onInterceptTouchEvent(ev)

  • ViewGroup比View中最先執(zhí)行的方法稽物,都是dispatchTouchEvent方法,然后View執(zhí)行了onTouch方法

  • 但是ViewGroup中在執(zhí)行onTouch方法之前多了一個onInterceptTouchEvent(ev)判斷折欠,這個判斷決定了事件在本ViewGroup中消費(fèi)贝或,還是將事件繼續(xù)分發(fā)給它的子View.

  • 而View是沒有onInterceptTouchEvent(ev)的,所以沒有攔截锐秦,事件能不能在View中消費(fèi)掉咪奖,關(guān)鍵是看這個View中的onTouch方法或者onTouchEvent的返回值,如果返回值都是false酱床,那就會將事件返回給其父View的onTouch和onTouchEvent方法羊赵,直到找到一個能消費(fèi)此次事件的ViewGroup。

好了關(guān)與Android中View和ViewGroup的事件分發(fā)機(jī)制兩篇都講完了扇谣,如果有不足或者不理解的地方可在評論中回復(fù)昧捷,我們共同進(jìn)步。
最后附上ViewGroupDemo源碼:github下載

上一篇:Android View的事件分發(fā)機(jī)制(上)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罐寨,一起剝皮案震驚了整個濱河市靡挥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸯绿,老刑警劉巖跋破,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件簸淀,死亡現(xiàn)場離奇詭異,居然都是意外死亡毒返,警方通過查閱死者的電腦和手機(jī)租幕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拧簸,“玉大人劲绪,你說我怎么就攤上這事〗铺瘢” “怎么了珠叔?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵蝎宇,是天一觀的道長弟劲。 經(jīng)常有香客問我,道長姥芥,這世上最難降的妖魔是什么兔乞? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮凉唐,結(jié)果婚禮上庸追,老公的妹妹穿的比我還像新娘。我一直安慰自己台囱,他們只是感情好淡溯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著簿训,像睡著了一般咱娶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上强品,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天膘侮,我揣著相機(jī)與錄音,去河邊找鬼的榛。 笑死咳秉,一個胖子當(dāng)著我的面吹牛犹菇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼溶推,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怜瞒?” 一聲冷哼從身側(cè)響起纪吮,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎要糊,沒想到半個月后纲熏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妆丘,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年局劲,在試婚紗的時候發(fā)現(xiàn)自己被綠了勺拣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡鱼填,死狀恐怖药有,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情苹丸,我是刑警寧澤愤惰,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站赘理,受9級特大地震影響宦言,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜商模,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一奠旺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧施流,春花似錦响疚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至银受,卻和暖如春践盼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚓土。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工宏侍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜀漆。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓谅河,卻偏偏與公主長得像,于是被迫代替她去往敵國和親确丢。 傳聞我的和親對象是個殘疾皇子绷耍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

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