Android TV開發(fā)按鍵與焦點(diǎn)深入分析(三)--按鍵事件轉(zhuǎn)換成焦點(diǎn)移動(dòng)的過(guò)程

上兩篇文章分別單獨(dú)分析了KeyEvent在View樹中分發(fā)View獲得焦點(diǎn)的過(guò)程炊汤,實(shí)際上這兩個(gè)并不是獨(dú)立的躬贡,當(dāng)我們按下按鍵的時(shí)候會(huì)發(fā)現(xiàn)如果我們不攔截按鍵事件抵拘,按鍵事件就會(huì)轉(zhuǎn)換成焦點(diǎn)View的切換祖凫,現(xiàn)在就開始分析這個(gè)轉(zhuǎn)換的過(guò)程。

1.ViewRootImpl中的整體過(guò)程

第一篇中提到過(guò)KeyEvent在View樹中分發(fā)是有Boolean返回值的蜕着,代碼注解如下:

View中
/**
    * Dispatch a key event to the next view on the focus path. This path runs
    * from the top of the view tree down to the currently focused view. If this
    * view has focus, it will dispatch to itself. Otherwise it will dispatch
    * the next node down the focus path. This method also fires any key
    * listeners.
    *
    * @param event The key event to be dispatched.
    * @return True if the event was handled, false otherwise.
    */
   public boolean dispatchKeyEvent(KeyEvent event) 

返回true代表這個(gè)按鍵事件已經(jīng)被消耗掉了谋竖,false代表還沒被消耗。默認(rèn)返回的是fasle承匣,只有我們?cè)谶@個(gè)過(guò)程中想要攔截蓖乘、處理了按鍵事件,才會(huì)返回true韧骗。
最后會(huì)把這個(gè)結(jié)果向上一層一層地反饋到按鍵事件產(chǎn)生的位置嘉抒,這個(gè)位置就是ViewRootImpl的processKeyEvent方法中,如下:

ViewRootImpl中
private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

            ......

            // Deliver the key to the view hierarchy.
            if (mView.dispatchKeyEvent(event)) {//View樹中分發(fā)按鍵事件
                return FINISH_HANDLED;//被處理了
            }
            //沒被處理  
            .......
            // Handle automatic focus changes.
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                if (groupNavigationDirection != 0) {
                    if (performKeyboardGroupNavigation(groupNavigationDirection)) {
                        return FINISH_HANDLED;
                    }
                } else {//尋找焦點(diǎn)View
                    if (performFocusNavigation(event)) {
                        return FINISH_HANDLED;
                    }
                }
            }
}

mView就是DecorView袍暴,是否已被消耗的結(jié)果的終點(diǎn)就是這里些侍。如果是被消耗了就直接返回,沒有政模,那就調(diào)用performFocusNavigation方法岗宣,從方法名字就可以看出這個(gè)方法就是要將按鍵事件轉(zhuǎn)換成焦點(diǎn)的移動(dòng),方法如下:

ViewRootImpl中
private boolean performFocusNavigation(KeyEvent event) {
            int direction = 0;
            //將按鍵事件的上下左右轉(zhuǎn)換成焦點(diǎn)移動(dòng)方向的上下左右
            switch (event.getKeyCode()) {
                case KeyEvent.KEYCODE_DPAD_LEFT:
                    if (event.hasNoModifiers()) {
                        direction = View.FOCUS_LEFT;
                    }
                    break;
                case KeyEvent.KEYCODE_DPAD_RIGHT:
                    if (event.hasNoModifiers()) {
                        direction = View.FOCUS_RIGHT;
                    }
                    break;
                case KeyEvent.KEYCODE_DPAD_UP:
                    if (event.hasNoModifiers()) {
                        direction = View.FOCUS_UP;
                    }
                    break;
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    if (event.hasNoModifiers()) {
                        direction = View.FOCUS_DOWN;
                    }
                    break;
                case KeyEvent.KEYCODE_TAB:
                    if (event.hasNoModifiers()) {
                        direction = View.FOCUS_FORWARD;
                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
                        direction = View.FOCUS_BACKWARD;
                    }
                    break;
            }
            if (direction != 0) {
                View focused = mView.findFocus();//找出此時(shí)這個(gè)有焦點(diǎn)的View
                if (focused != null) {
                    //調(diào)用它的focusSearch方法淋样,顧名思義尋找這個(gè)方向上的下一個(gè)獲得焦點(diǎn)的View
                    View v = focused.focusSearch(direction);
                    if (v != null && v != focused) {
                        // do the math the get the interesting rect
                        // of previous focused into the coord system of
                        // newly focused view
                        focused.getFocusedRect(mTempRect);
                        if (mView instanceof ViewGroup) {
                            ((ViewGroup) mView).offsetDescendantRectToMyCoords(
                                    focused, mTempRect);
                            ((ViewGroup) mView).offsetRectIntoDescendantCoords(
                                    v, mTempRect);
                        }
                        //調(diào)用它的requestFocus耗式,讓它獲得焦點(diǎn)
                        if (v.requestFocus(direction, mTempRect)) {
                            playSoundEffect(SoundEffectConstants
                                    .getContantForFocusDirection(direction));
                            return true;
                        }
                    }

                    // Give the focused view a last chance to handle the dpad key.
                    if (mView.dispatchUnhandledMove(focused, direction)) {
                        return true;
                    }
                } else {
                    if (mView.restoreDefaultFocus()) {
                        return true;
                    }
                }
            }
            return false;
        }

這個(gè)方法中一氣呵成完成了按鍵事件轉(zhuǎn)換成焦點(diǎn)View變化的全部過(guò)程,可以概括為以下4個(gè)步驟:

  1. 將按鍵事件的上下左右轉(zhuǎn)換成焦點(diǎn)移動(dòng)方向的上下左右
  2. 找出View樹中有焦點(diǎn)的View
  3. 調(diào)用焦點(diǎn)View的focusSearch方法尋找下一個(gè)獲得焦點(diǎn)的View
  4. 調(diào)用下一個(gè)獲得焦點(diǎn)的View的requestFocus方法习蓬,讓它獲得焦點(diǎn)

下面分別分析第2纽什、3步的過(guò)程措嵌,第4步請(qǐng)看上一篇的分析

2.尋找有焦點(diǎn)的View

調(diào)用的是View的findFocus方法躲叼,ViewGroup和View的findFoucs方法分別如下:

ViewGroup中
@Override
    public View findFocus() {
        if (isFocused()) {
            return this;
        }
        if (mFocused != null) {
            return mFocused.findFocus();
        }
        return null;
    }    
View中
    public boolean isFocused() {
        return (mPrivateFlags & PFLAG_FOCUSED) != 0;
    }
View中
    public View findFocus() {
        return (mPrivateFlags & PFLAG_FOCUSED) != 0 ? this : null;
    }

尋找的依據(jù)就是上一篇中分析的PFLAG_FOCUSED標(biāo)志位以及ViewGroup的mFocused成員變量,首先是ViewGroup判斷自己是不是有焦點(diǎn)企巢,然后再判斷自己是不是包含了有焦點(diǎn)的子View枫慷,多次按照焦點(diǎn)的路徑遍歷就找出了焦點(diǎn)View。

3.尋找下一個(gè)獲得焦點(diǎn)的View

View的focusSearch方法

此刻已經(jīng)找出了焦點(diǎn)View,需要調(diào)用它的focusSearch去尋找下一個(gè)焦點(diǎn)View或听,View的focusSearch方法如下:

View中
    public View focusSearch(@FocusRealDirection int direction) {
        if (mParent != null) {
            return mParent.focusSearch(this, direction);
        } else {
            return null;
        }
    }

直接調(diào)用了它的父View的方法探孝,如下:

ViewGroup中
@Override
    public View focusSearch(View focused, int direction) {
        if (isRootNamespace()) {
            // root namespace means we should consider ourselves the top of the
            // tree for focus searching; otherwise we could be focus searching
            // into other tabs.  see LocalActivityManager and TabHost for more info.
            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
        } else if (mParent != null) {
            return mParent.focusSearch(focused, direction);
        }
        return null;
    }
    

ViewGroup重寫了這個(gè)方法,如果自己是RootNamespace那就調(diào)用FocusFinder去尋找View誉裆,但什么時(shí)候isRootNamespace()成立顿颅,我現(xiàn)在還沒遇到過(guò)舔腾,所以一般情況下最后會(huì)調(diào)用到ViewRootImpl的focusSearch方法砰奕,這個(gè)方法如下:

ViewRootImpl中 
@Override
    public View focusSearch(View focused, int direction) {
        checkThread();
        if (!(mView instanceof ViewGroup)) {
            return null;
        }
        return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
    }

殊途同歸,最后還是調(diào)用了FocusFinder去尋找View尼夺,看來(lái)尋找下一個(gè)焦點(diǎn)View的重任全部都封裝在了這個(gè)FocusFinder類中斩跌。

FocusFinder單例

 private static final ThreadLocal<FocusFinder> tlFocusFinder =
            new ThreadLocal<FocusFinder>() {
                @Override
                protected FocusFinder initialValue() {
                    return new FocusFinder();
                }
            };

    /**
     * Get the focus finder for this thread.
     */
    public static FocusFinder getInstance() {
        return tlFocusFinder.get();
    }

值得注意的是FocusFinder居然是線程單例的绍些,而不是進(jìn)程單例的,這樣做的原因大概是一方面發(fā)揮單例優(yōu)勢(shì)耀鸦,避免頻繁創(chuàng)建FocusFinder柬批,畢竟這是一個(gè)比較基本的功能,節(jié)約資源提高效率袖订;另一方面氮帐,萬(wàn)一其他線程也要調(diào)用FocusFinder去做一些尋找View的事情,如果進(jìn)程單例那不就影響主線程的效率了洛姑?所以線程單例最合適吧揪漩。

添加所有可以獲得焦點(diǎn)的View

言歸正傳,繼續(xù)看它的findNextFocus方法吏口,如下:

FocusFinder中
private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
       View next = null;
       //找出焦點(diǎn)跳轉(zhuǎn)View的范圍
       ViewGroup effectiveRoot = getEffectiveRoot(root, focused);
       if (focused != null) {//找出在xml中指定的該方向的下一個(gè)獲得焦點(diǎn)的View
           next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction);
       }
       if (next != null) {//指定了直接返回
           return next;
       }
       ArrayList<View> focusables = mTempList;
       try {
           focusables.clear();
           //從最頂點(diǎn)奄容,將所有可以獲得焦點(diǎn)的View添加到focusables中
           effectiveRoot.addFocusables(focusables, direction);
           if (!focusables.isEmpty()) {
               //從中找出下一個(gè)可以獲得焦點(diǎn)的View
               next = findNextFocus(effectiveRoot, focused, focusedRect, direction, focusables);
           }
       } finally {
           focusables.clear();
       }
       return next;
   }

首先找出焦點(diǎn)跳轉(zhuǎn)的View的范圍,我這里測(cè)試時(shí)effectiveRoot就是DecorView产徊,也就是整個(gè)View樹中View都在考慮的范圍內(nèi)昂勒。然后調(diào)用findNextUserSpecifiedFocus方法去獲取我們手動(dòng)為這個(gè)View設(shè)置的上下左右的焦點(diǎn)View,對(duì)應(yīng)xml布局中的android:nextFocusXX

            android:nextFocusDown="@id/textView"
            android:nextFocusLeft="@id/textView"
            android:nextFocusRight="@id/textView"
            android:nextFocusUp="@id/textView"

如果設(shè)置了舟铜,那就直接返回設(shè)置的View戈盈,沒有則繼續(xù)尋找,addFocusables方法把View樹中所有可能獲得焦點(diǎn)的View都放進(jìn)了focusables這個(gè)list中谆刨。
ViewGroup中的addFocusables方法如下:

ViewGroup中
    @Override
    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
        final int focusableCount = views.size();

        final int descendantFocusability = getDescendantFocusability();
        final boolean blockFocusForTouchscreen = shouldBlockFocusForTouchscreen();
        final boolean focusSelf = (isFocusableInTouchMode() || !blockFocusForTouchscreen);

        if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {//攔截了焦點(diǎn)塘娶,只判斷、添加自己
            if (focusSelf) {
                super.addFocusables(views, direction, focusableMode);
            }
            return;
        }

        if (blockFocusForTouchscreen) {
            focusableMode |= FOCUSABLES_TOUCH_MODE;
        }
        //在所有子View之前添加自己到views
        if ((descendantFocusability == FOCUS_BEFORE_DESCENDANTS) && focusSelf) {
            super.addFocusables(views, direction, focusableMode);
        }

        int count = 0;
        final View[] children = new View[mChildrenCount];
        //挑出可見的View
        for (int i = 0; i < mChildrenCount; ++i) {
            View child = mChildren[i];
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                children[count++] = child;
            }
        }
        //對(duì)所有子View排序
        FocusFinder.sort(children, 0, count, this, isLayoutRtl());
        for (int i = 0; i < count; ++i) {//把所有子View按順序添加到views
            children[i].addFocusables(views, direction, focusableMode);
        }

        // When set to FOCUS_AFTER_DESCENDANTS, we only add ourselves if
        // there aren't any focusable descendants.  this is
        // to avoid the focus search finding layouts when a more precise search
        // among the focusable children would be more interesting.
        if ((descendantFocusability == FOCUS_AFTER_DESCENDANTS) && focusSelf
                && focusableCount == views.size()) {
            super.addFocusables(views, direction, focusableMode);
        }
    }

如果ViewGroup攔截焦點(diǎn)痊夭,那就不用再考慮子View了刁岸;如果ViewGroup在子View之前獲得焦點(diǎn),那就先添加她我,反之后添加虹曙;對(duì)于兄弟View迫横,在添加之前還要對(duì)它們進(jìn)行排序,排序的依據(jù)是從上到下酝碳、從左到右矾踱。

View的addFocusables方法

    public void addFocusables(ArrayList<View> views, @FocusDirection int direction,
            @FocusableMode int focusableMode) {
        if (views == null) {
            return;
        }
        if (!canTakeFocus()) {
            return;
        }
        if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
                && !isFocusableInTouchMode()) {
            return;
        }
        views.add(this);
    }

直接判斷自己是否可以獲得焦點(diǎn),可以的話就把自己加到views中去疏哗。

到這里所有的可以獲得焦點(diǎn)的View 都被添加到了focusables中去了呛讲,在這個(gè)過(guò)程中與方向還沒有關(guān)系,只是枚舉添加了所有可能的View返奉。

尋找最優(yōu)View

有了focusables列表圣蝎,這時(shí)調(diào)用同名方法findNextFocus在focusables找出最合適的那個(gè)View,方法如下:

private View findNextFocus(ViewGroup root, View focused, Rect focusedRect,
            int direction, ArrayList<View> focusables) {
        if (focused != null) {
            if (focusedRect == null) {
                focusedRect = mFocusedRect;
            }
            // fill in interesting rect from focused
            focused.getFocusedRect(focusedRect);
            //將focused的坐標(biāo)變成rootView下的坐標(biāo)
            root.offsetDescendantRectToMyCoords(focused, focusedRect);
        } else {
           ......
        }

        switch (direction) {
            case View.FOCUS_FORWARD:
            case View.FOCUS_BACKWARD:
                return findNextFocusInRelativeDirection(focusables, root, focused, focusedRect,
                        direction);
            case View.FOCUS_UP:
            case View.FOCUS_DOWN:
            case View.FOCUS_LEFT:
            case View.FOCUS_RIGHT:
     
                return findNextFocusInAbsoluteDirection(focusables, root, focused,
                        focusedRect, direction);
            default:
                throw new IllegalArgumentException("Unknown direction: " + direction);
        }
    }

到這里衡瓶,尋找的才與方向有關(guān)徘公,下面的分析以按右鍵為例,對(duì)應(yīng)的是View.FOCUS_RIGHT哮针,調(diào)用了findNextFocusInAbsoluteDirection方法:

View findNextFocusInAbsoluteDirection(ArrayList<View> focusables, ViewGroup root, View focused,
            Rect focusedRect, int direction) {
        // initialize the best candidate to something impossible
        // (so the first plausible view will become the best choice)
        mBestCandidateRect.set(focusedRect);
        switch(direction) {
            case View.FOCUS_LEFT:
                mBestCandidateRect.offset(focusedRect.width() + 1, 0);
                break;
            case View.FOCUS_RIGHT://向右尋找時(shí)关面,將初始的位置設(shè)為當(dāng)前View的最左邊
                mBestCandidateRect.offset(-(focusedRect.width() + 1), 0);
                break;
            case View.FOCUS_UP:
                mBestCandidateRect.offset(0, focusedRect.height() + 1);
                break;
            case View.FOCUS_DOWN:
                mBestCandidateRect.offset(0, -(focusedRect.height() + 1));
        }

        View closest = null;

        int numFocusables = focusables.size();
        //遍歷所有的可獲得焦點(diǎn)的View
        for (int i = 0; i < numFocusables; i++) {
            View focusable = focusables.get(i);
            //排除自己
            // only interested in other non-root views
            if (focusable == focused || focusable == root) continue;
            //獲得這個(gè)View的rect,并把它調(diào)整到和focusedRect一致
            // get focus bounds of other view in same coordinate system
            focusable.getFocusedRect(mOtherRect);
            root.offsetDescendantRectToMyCoords(focusable, mOtherRect);
            //判斷這個(gè)View是不是比mBestCandidateRect更優(yōu)
            if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {
                //更優(yōu)十厢,那么將它設(shè)置成mBestCandidateRect等太,并將closest賦值
                mBestCandidateRect.set(mOtherRect);
                closest = focusable;
            }
        }
        return closest;
    }

實(shí)際上尋找的過(guò)程就是在比較各個(gè)View的占用區(qū)域的相對(duì)關(guān)系,這里首先設(shè)置了一個(gè)mBestCandidateRect代表最合適的View的區(qū)域蛮放,對(duì)于向右缩抡,是焦點(diǎn)View左邊間距一像素的同等大小的一個(gè)區(qū)域,顯然這是最不合適的區(qū)域包颁,這只是一個(gè)初始值瞻想。然后就開始遍歷所有可能的View,調(diào)用isBetterCandidate方法去判斷娩嚼,這里就不展開這個(gè)方法蘑险,太啰嗦了,用下面的圖代替岳悟。
假如下圖中View1此時(shí)有焦點(diǎn)佃迄,按下右鍵時(shí)會(huì)怎么樣呢?


實(shí)際上判斷的依據(jù)和下圖中的畫的各種虛線有關(guān)
foucs_right

按下右鍵贵少,初始的最佳區(qū)域就是紅色虛線框的位置呵俏,顯示是最不適合的,然后再遍歷所有的可能的View滔灶,滿足以下兩點(diǎn)才可以擊敗紅色虛線框的位置成為待選的View:

  1. 以紫色虛線作為標(biāo)準(zhǔn)普碎,View的右邊線必須在紫色虛線的右邊
  2. 以黑色虛線作為標(biāo)準(zhǔn),View的左邊線必須在黑色虛線的右邊

顯然View2和View3都滿足宽气,這時(shí)就需要進(jìn)一步的比較了随常,進(jìn)一步比較需要參考View的上下兩邊,滿足以下兩點(diǎn)的最優(yōu):

  1. View的下邊線在上面的那條藍(lán)色虛線之下
  2. View的上邊線在下面的那條藍(lán)色虛線之上

也就是這個(gè)View的與焦點(diǎn)View在上下兩邊上有重疊的區(qū)域就可以了萄涯,所以View3是最優(yōu)的位置绪氛,View3將獲得焦點(diǎn)。
還有一種情況涝影,要是兩個(gè)View都與焦點(diǎn)View在上下兩邊有重疊的區(qū)域枣察,那誰(shuí)更優(yōu)呢?如下:


foucs_right

對(duì)于向右尋找焦點(diǎn)燃逻,這時(shí)判斷的依據(jù)就和圖中的兩個(gè)距離箭頭major和minor的長(zhǎng)度有關(guān)序目,計(jì)算的公式distance=13*major^2+minor^2,distance越小越有優(yōu)伯襟,13是一個(gè)常量系數(shù)猿涨,表示以左右間距作為主要的判斷依據(jù),但不排除上下間距逆襲的可能姆怪,所以越靠近焦點(diǎn)View的中心叛赚,就越有可能獲得焦點(diǎn)。
調(diào)試的布局如下稽揭,可以微微調(diào)整bias俺附,驗(yàn)證結(jié)論。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
        android:layout_height="match_parent"
        android:layout_width="match_parent">


    <TextView
            android:text="View1"
            android:background="@drawable/bg"
            android:focusable="true"
            android:gravity="center"
            android:layout_width="55dp"
            android:layout_height="344dp"
            android:id="@+id/textView"
            app:layout_constraintEnd_toStartOf="@id/guideline2"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_bias="0.99"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>
    <TextView
            android:text="View2"
            android:background="@drawable/bg"
            android:focusable="true"
            android:layout_width="60dp"
            android:gravity="center"
            android:layout_height="50dp"
            android:id="@+id/textView2"
            app:layout_constraintStart_toStartOf="@+id/guideline2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_bias="0.1"
            app:layout_constraintBottom_toTopOf="@+id/guideline"
            app:layout_constraintVertical_bias="0.7"/>
    <TextView
            android:text="View3"
            android:background="@drawable/bg"
            android:focusable="true"
            android:layout_width="60dp"
            android:gravity="center"
            android:layout_height="50dp"
            android:id="@+id/textView3"
            app:layout_constraintStart_toStartOf="@+id/guideline2"
            app:layout_constraintVertical_bias="0.3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline"
            app:layout_constraintHorizontal_bias="0.1"
            app:layout_constraintBottom_toBottomOf="parent"/>
    <android.support.constraint.Guideline
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintGuide_percent="0.3"
            android:id="@+id/guideline2"
            android:orientation="vertical"/>
    <android.support.constraint.Guideline
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintGuide_percent="0.5"
            android:id="@+id/guideline"
            android:orientation="horizontal"/>
</android.support.constraint.ConstraintLayout>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末溪掀,一起剝皮案震驚了整個(gè)濱河市事镣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌揪胃,老刑警劉巖璃哟,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異喊递,居然都是意外死亡沮稚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門册舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蕴掏,“玉大人,你說(shuō)我怎么就攤上這事调鲸∈⒔埽” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵藐石,是天一觀的道長(zhǎng)即供。 經(jīng)常有香客問(wèn)我,道長(zhǎng)于微,這世上最難降的妖魔是什么逗嫡? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任青自,我火速辦了婚禮,結(jié)果婚禮上驱证,老公的妹妹穿的比我還像新娘延窜。我一直安慰自己,他們只是感情好抹锄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布逆瑞。 她就那樣靜靜地躺著,像睡著了一般伙单。 火紅的嫁衣襯著肌膚如雪获高。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天吻育,我揣著相機(jī)與錄音念秧,去河邊找鬼。 笑死布疼,一個(gè)胖子當(dāng)著我的面吹牛出爹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缎除,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼严就,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了器罐?” 一聲冷哼從身側(cè)響起梢为,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎轰坊,沒想到半個(gè)月后铸董,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肴沫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年粟害,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颤芬。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悲幅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出站蝠,到底是詐尸還是另有隱情汰具,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布菱魔,位于F島的核電站留荔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏澜倦。R本人自食惡果不足惜聚蝶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一杰妓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碘勉,春花似錦巷挥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吸占。三九已至晴叨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間矾屯,已是汗流浹背兼蕊。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留件蚕,地道東北人孙技。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像排作,于是被迫代替她去往敵國(guó)和親牵啦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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