Android開發(fā)之分組列表懸浮頂部欄(吸頂效果)

之前寫過一篇文章《Android開發(fā)之仿微博詳情頁(滑動固定頂部欄效果)》,當時采用的解決方案是用一個ScrollView去包裹內容布局,通過監(jiān)聽滑動狀態(tài)惜论,在適當?shù)臅r候,移入/移出所要固定的布局止喷,這樣雖然可以達到想要的視覺效果馆类,但這種實現(xiàn)方式并不優(yōu)雅,比如被包裹內容布局中帶有滑動特性的View(ListView弹谁,RecyclerView等)乾巧,這樣做就需要我們額外的去處理滑動沖突,而且這種方式的包裹會使得它們的緩存機制失效预愤,為了一個視覺效果去犧牲它們最具靈動的特性的一面卧抗,我不提倡這種做法。

這里介紹另外一種解決方案鳖粟,可以更加優(yōu)雅的實現(xiàn)這種視覺效果社裆,而且不會有滑動沖突,也不需要犧牲緩存機制向图,在文章末尾會給出思路泳秀,需要你先看完文章哈~

先來看下今天要實現(xiàn)的效果圖:


效果圖

要實現(xiàn)這個效果很簡單,只需要一個RecyclerView就可以實現(xiàn)了榄攀,不需要多余的布局控件嗜傅,當然網(wǎng)上也有另外的一些實現(xiàn)方式,比如利用幀布局或者相對布局在RecyclerView上面再蓋上要固定的ViewGroup檩赢,通過滑動去判斷是否需要動態(tài)的將固定布局移入/移出吕嘀,其實和上面提到的文章實現(xiàn)思路一樣,這樣做,很明顯的會有幾個缺點偶房,比如增加了布局的深度或者在業(yè)務發(fā)生變化的時候需要同時去改動至少兩處代碼等趁曼,如果中間還耦合著一些業(yè)務操作,出錯幾率也會對應的增加棕洋。

列表的組成

這是一個帶有分組的列表挡闰,我們可以把它拆分成3部分,頭部數(shù)據(jù)+列表數(shù)據(jù)+分割線

列表數(shù)據(jù):
RecyclerView的基本使用掰盘,這里我就不再重復闡述了摄悯。

分割線:
要實現(xiàn)分割線,如果是在以前的ListView愧捕,我們通過設置divider奢驯,dividerHeigh等屬性就可以很輕松的達到目的,或者直接在布局文件中畫上一個帶有高度和背景色的View來實現(xiàn)次绘。到了RecyclerView這里瘪阁,我們可不再需要這樣做了,官方給我們提供了一個強大的裝飾器ItemDecoration断盛,它可以幫助我們實現(xiàn)分割線的功能,但它可不僅僅只能實現(xiàn)分割線愉舔,一會下文會介紹钢猛。

頭部數(shù)據(jù):
要繪制這個頭部,以前我們在ListView里轩缤,可能有些人會這樣做命迈,讓每個Item布局都帶上這個頭部布局,然后根據(jù)是否是每組數(shù)據(jù)的第一個來動態(tài)控制頭部布局是顯示還是隱藏火的,當然這樣做也可以實現(xiàn)我們想要的效果壶愤,但卻會多余的去耗費一定的性能,因為明明每組數(shù)據(jù)只需要繪制一個頭部馏鹤,而你卻每個Item都去繪制征椒,最終每組卻又只需要一個,所以這里我們依然可以采用官方提供的ItemDecoration來解決這個問題湃累。

什么是ItemDecoration勃救?

說了這么多ItemDecoration,我們來看下官方給出的介紹吧:

An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter's data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.

大概意思是ItemDecoration允許給特定的item視圖添加特性的繪制以及布局間隔治力。它可以用來實現(xiàn)item之間的分割線蒙秒,高亮,分組邊界等宵统。

public class ItemDecoration extends RecyclerView.ItemDecoration {

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
    }
}

ItemDecoration是RecyclerView下的抽象方法晕讲,我們要使用它只需要繼承它,并實現(xiàn)對應的方法即可,然后讓RecyclerView去調用它

mRecyclerView.addItemDecoration(new ItemDecoration());

具體來看下這3個方法瓢省,順便來一張圖幫助理解:


方法介紹圖

getItemOffsets:它是用來給Item設置間距的弄息,可以這樣理解在Item外還有一層布局,而這個方法是用來設置布局的Padding净捅。

onDraw:它的繪制和Item之下疑枯,它繪制的東西會在Item的下面。

onDrawOver:它的繪制在Item之上蛔六,它繪制的東西會覆蓋在Item的上面荆永。

事實上并不是真的有層次之分,這里只是為了方便理解国章,最根本的原因是因為它們方法的調用方法的順序具钥,又因為都作用于同一個Canvas上,才出現(xiàn)這種覆蓋的層次的效果液兽。

知道了這些方法的作用后骂删,我們配合RecyclerView給我們的一些API方法,要做其它事情容易多了四啰,隨意舉2個例子:
1宁玫、如果我們想要繪制分割線,只需要先調用getItemOffsets柑晒,讓Item空出一定的間隙欧瘪,然后再調用onDraw在這個間隙上填充顏色即可。

2匙赞、我們經(jīng)常會遇到一些節(jié)假日活動的需求佛掖,需要在列表上的邊角處標記“活動”,“特價”等特殊符號涌庭,這時候我們只需要調用onDrawOver在Item上繪制即可芥被。

言歸正傳,我們來看下今天我們要實現(xiàn)的效果坐榆,帶有吸頂效果的分組列表拴魄,上文已經(jīng)提及了可以分為3部分來看,頭部數(shù)據(jù)+列表數(shù)據(jù)+分割線席镀,其中列表數(shù)據(jù)是最基礎的RecyclerView的使用羹铅,這個我們就不說了,我們來看下其它2部分愉昆。

為了測試方便职员,這里我建立了一些本地數(shù)據(jù):

數(shù)據(jù)實體:

public class Bean {

    private String text;
    private String groupName;

    public Bean(String text, String groupName) {
        this.text = text;
        this.groupName = groupName;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }
}

數(shù)據(jù)集合:

       List<Bean> beanList = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            beanList.add(new Bean(String.format("第一組%d號", i + 1), "第一組"));
        }
        for (int i = 0; i < 6; i++) {
            beanList.add(new Bean(String.format("第二組%d號", i + 1), "第二組"));
        }
        for (int i = 0; i < 6; i++) {
            beanList.add(new Bean(String.format("第三組%d號", i + 1), "第三組"));
        }
        for (int i = 0; i < 6; i++) {
            beanList.add(new Bean(String.format("第四組%d號", i + 1), "第四組"));
        }

分割線的實現(xiàn):

首先我們需要在getItemOffsets方法中讓Item間空出空隙:

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.bottom = 1;
    }

然后我們在onDraw方法中去對這個空隙繪制顏色(繪制一個帶有顏色矩形)

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int count = parent.getChildCount();
        for (int i = 0; i < count; i++) {
            View view = parent.getChildAt(i);
            c.drawRect(0, view.getBottom(), parent.getWidth(), view.getBottom() + 1, mLinePaint);
        }
    }

這里需要注意的一個地方是RecyclerView的getChildCount方法只會拿到當前可視區(qū)域的Item項,然后我們對Item進行遍歷繪制矩形(分割線)跛溉。

就這么簡單焊切,我們的分割線已經(jīng)畫好了扮授,看下實現(xiàn)效果:

分割線實現(xiàn)效果

頭部布局的實現(xiàn):

頭布局的實現(xiàn)和分割線是一樣的,它一樣需要讓Item空出空隙专肪,然后填充顏色刹勃,只是空出的空隙距離和顏色不一樣罷了,所以我們需要知道什么時候空出的分割線的空隙嚎尤,什么時候空出頭部布局的空隙荔仁,這個就和我們數(shù)據(jù)源有關系了,我們寫一個方法來判斷當前position所對應的Item項是不是每組數(shù)據(jù)的第一項:

    /**
     * 判斷position對應的Item是否是組的第一項
     *
     * @param position
     * @return
     */
    public boolean isItemHeader(int position) {
        if (position == 0) {
            return true;
        } else {
            String lastGroupName = mList.get(position - 1).getGroupName();
            String currentGroupName = mList.get(position).getGroupName();
            //判斷上一個數(shù)據(jù)的組別和下一個數(shù)據(jù)的組別是否一致芽死,如果不一致則是不同組乏梁,也就是為第一項(頭部)
            if (lastGroupName.equals(currentGroupName)) {
                return false;
            } else {
                return true;
            }
        }
    }

然后來看下getItemOffsets方法,如果是每組第一項我們空出頭部布局的高度关贵,如果不是遇骑,我們則空出分割線的高度:

    /**
     * 設置Item的間距
     *
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (parent.getAdapter() instanceof RecyclerViewAdapter) {
            RecyclerViewAdapter adapter = (RecyclerViewAdapter) parent.getAdapter();
            int position = parent.getChildLayoutPosition(view);
            boolean isHeader = adapter.isItemHeader(position);
            if (isHeader) {
                outRect.top = mItemHeaderHeight;
            } else {
                outRect.top = 1;
            }
        }
    }

然后一樣的在onDraw方法里繪制背景顏色和文字即可,關于繪制的知識點這邊就不說了揖曾,屬于基礎的自定義View需要掌握的知識:

    /**
     * 繪制Item的分割線和組頭
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getAdapter() instanceof RecyclerViewAdapter) {
            RecyclerViewAdapter adapter = (RecyclerViewAdapter) parent.getAdapter();
            int count = parent.getChildCount();
            for (int i = 0; i < count; i++) {
                View view = parent.getChildAt(i);
                int position = parent.getChildLayoutPosition(view);
                boolean isHeader = adapter.isItemHeader(position);
                if (isHeader) {
                    c.drawRect(0, view.getTop() - mItemHeaderHeight, parent.getWidth(), view.getTop(), mItemHeaderPaint);
                    mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length(), mTextRect);
                    c.drawText(adapter.getGroupName(position), mTextPaddingLeft, (view.getTop() - mItemHeaderHeight) + mItemHeaderHeight / 2 + mTextRect.height() / 2, mTextPaint);
                } else {
                    c.drawRect(0, view.getTop() - 1, parent.getWidth(), view.getTop(), mLinePaint);
                }
            }
        }
    }

此時我們的頭部布局也畫好了落萎,看下實現(xiàn)效果:


分組布局實現(xiàn)效果

吸頂效果的實現(xiàn):

關于吸頂?shù)男Ч鋵嵵灰覀兝砬宄牧鞒叹蜁l(fā)現(xiàn)其實并不復雜炭剪,50行代碼不到就可以把它完成练链。
首先我們需要知道以下幾點:
1、當我們滑動列表的時候奴拦,第一個頭布局是固定在我們的列表頂部的媒鼓。
2、通過滑動列表粱坤,當下一個頭布局和第一個頭布局相碰的時候隶糕,會把第一個布局“頂出去”瓷产,當?shù)谝粋€頭布局完全被“頂出去”后站玄,第二個頭布局并替代了第一個頭布局固定在列表頂部。
知道了上面2點后濒旦,有時候我們所看到的視覺效果會把我們帶入一個思維誤區(qū)株旷,比如這個吸頂效果,有的朋友可能會這樣去考慮尔邓,是不是需要在滑動的時候晾剖,動態(tài)的去改變getItemOffsets的空隙大小和在onDraw的繪制高度。如果真的這樣去做梯嗽,你會發(fā)現(xiàn)實現(xiàn)起來十分困難齿尽。

我們換一種思維,既然頂部的布局是固定不動的灯节,是不是可以利用onDrawOver在RecyclerView的上繪制一個和頭部布局一模一樣的布局呢循头,讓它覆蓋住了第一個頭布局绵估,在視覺上我們是不會有所察覺的,然后當列表滑動的時候卡骂,其實“原來的頭布局”早已經(jīng)滑動走了国裳,留下的其實是我們繪制的固定布局而已,等到下一個頭部布局“碰頭”的時候全跨,讓它隨著滑動的速度慢慢改變布局的高度缝左,當布局高度為0的時候,也就是被頂出去的時候浓若,然后再讓高度改變回來渺杉,覆蓋住第二個布局,然后不斷重復以上步驟七嫌。

可能說的有點抽象少办,我們來一張圖看一下,這次我故意把頭布局顏色改成紅色诵原,不清楚的朋友多看幾次就可以理解了趁餐。


吸頂效果實現(xiàn)

看下具體代碼,我們先通過findFirstVisibleItemPosition拿到第一個可見的Item的position量愧,那我們就可以根據(jù)position+1可以知道下一個Item是否是另一組的頭布局(判斷組名是否發(fā)生了變化)鸣个,如果不是,我們的依舊繪制固定布局即可吗蚌,如果是腿倚,我們根據(jù)第一個可見Item的getBottom值的變小,慢慢的改變固定布局的高度蚯妇,直到被“頂出去”敷燎。

    /**
     * 繪制Item的頂部布局(吸頂效果)
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getAdapter() instanceof RecyclerViewAdapter) {
            RecyclerViewAdapter adapter = (RecyclerViewAdapter) parent.getAdapter();
            int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
            View view = parent.findViewHolderForAdapterPosition(position).itemView;
            boolean isHeader = adapter.isItemHeader(position + 1);
            if (isHeader) {
                int bottom = Math.min(mItemHeaderHeight, view.getBottom());
                c.drawRect(0, view.getTop() - mItemHeaderHeight, parent.getWidth(), bottom, mItemHeaderPaint);
                mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length(), mTextRect);
                c.drawText(adapter.getGroupName(position), mTextPaddingLeft, mItemHeaderHeight / 2 + mTextRect.height() / 2 - (mItemHeaderHeight - bottom), mTextPaint);
            } else {
                c.drawRect(0, 0, parent.getWidth(), mItemHeaderHeight, mItemHeaderPaint);
                mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length(), mTextRect);
                c.drawText(adapter.getGroupName(position), mTextPaddingLeft, mItemHeaderHeight / 2 + mTextRect.height() / 2, mTextPaint);
            }
        }

    }

吸頂效果就這么簡單的完成了,其實關鍵就在于onDrawOver這個方法箩言。
這里附上完整的ItemDecoration代碼(避免太多參數(shù)增加代碼閱讀難度硬贯,上面的講解沒有考慮RecyclerView存在Padding的情況,這邊已給出補充):

package com.lcw.view.stickheaderview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * 自定義裝飾器(實現(xiàn)分組+吸頂效果)
 * Create by: chenWei.li
 * Date: 2018/11/2
 * Time: 上午1:14
 * Email: lichenwei.me@foxmail.com
 */
public class StickHeaderDecoration extends RecyclerView.ItemDecoration {

    //頭部的高
    private int mItemHeaderHeight;
    private int mTextPaddingLeft;

    //畫筆陨收,繪制頭部和分割線
    private Paint mItemHeaderPaint;
    private Paint mTextPaint;
    private Paint mLinePaint;

    private Rect mTextRect;


    public StickHeaderDecoration(Context context) {
        mItemHeaderHeight = dp2px(context, 40);
        mTextPaddingLeft = dp2px(context, 6);

        mTextRect = new Rect();

        mItemHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mItemHeaderPaint.setColor(Color.BLUE);

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextSize(46);
        mTextPaint.setColor(Color.WHITE);

        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setColor(Color.GRAY);

    }

    /**
     * 繪制Item的分割線和組頭
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getAdapter() instanceof RecyclerViewAdapter) {
            RecyclerViewAdapter adapter = (RecyclerViewAdapter) parent.getAdapter();
            int count = parent.getChildCount();//獲取可見范圍內Item的總數(shù)
            for (int i = 0; i < count; i++) {
                View view = parent.getChildAt(i);
                int position = parent.getChildLayoutPosition(view);
                boolean isHeader = adapter.isItemHeader(position);
                int left = parent.getPaddingLeft();
                int right = parent.getWidth() - parent.getPaddingRight();
                if (isHeader) {
                    c.drawRect(left, view.getTop() - mItemHeaderHeight, right, view.getTop(), mItemHeaderPaint);
                    mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length(), mTextRect);
                    c.drawText(adapter.getGroupName(position), left + mTextPaddingLeft, (view.getTop() - mItemHeaderHeight) + mItemHeaderHeight / 2 + mTextRect.height() / 2, mTextPaint);
                } else {
                    c.drawRect(left, view.getTop() - 1, right, view.getTop(), mLinePaint);
                }
            }
        }
    }


    /**
     * 繪制Item的頂部布局(吸頂效果)
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (parent.getAdapter() instanceof RecyclerViewAdapter) {
            RecyclerViewAdapter adapter = (RecyclerViewAdapter) parent.getAdapter();
            int position = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
            View view = parent.findViewHolderForAdapterPosition(position).itemView;
            boolean isHeader = adapter.isItemHeader(position + 1);
            int top = parent.getPaddingTop();
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();
            if (isHeader) {
                int bottom = Math.min(mItemHeaderHeight, view.getBottom());
                c.drawRect(left, top + view.getTop() - mItemHeaderHeight, right, top + bottom, mItemHeaderPaint);
                mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length(), mTextRect);
                c.drawText(adapter.getGroupName(position), left + mTextPaddingLeft, top + mItemHeaderHeight / 2 + mTextRect.height() / 2 - (mItemHeaderHeight - bottom), mTextPaint);
            } else {
                c.drawRect(left, top, right, top + mItemHeaderHeight, mItemHeaderPaint);
                mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length(), mTextRect);
                c.drawText(adapter.getGroupName(position), left + mTextPaddingLeft, top + mItemHeaderHeight / 2 + mTextRect.height() / 2, mTextPaint);
            }
            c.save();
        }

    }

    /**
     * 設置Item的間距
     *
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (parent.getAdapter() instanceof RecyclerViewAdapter) {
            RecyclerViewAdapter adapter = (RecyclerViewAdapter) parent.getAdapter();
            int position = parent.getChildLayoutPosition(view);
            boolean isHeader = adapter.isItemHeader(position);
            if (isHeader) {
                outRect.top = mItemHeaderHeight;
            } else {
                outRect.top = 1;
            }
        }
    }


    /**
     * dp轉換成px
     */
    private int dp2px(Context context, float dpValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}

額外補充:

對于文章開頭說的那個仿微博固定頂部欄的效果:


仿微博固定頂部欄

我相信可以理解這篇文章的朋友應該都可以很輕松的用一個RecyclerView做出來了饭豹,簡單的說下思路,首先是微博內容务漩,我們把它當成是RecyclerView的HeaderView即可拄衰,也是Item的一項,然后下面的評論列表就是基礎的RecyclerView使用了饵骨,然后中間固定的布局翘悉,就可以這篇文章所講的ItemDecoration里的getItemOffsets和onDrawOver來配合實現(xiàn)了。

好了居触,到這里內容就結束了妖混,有什么疑問包吝,歡迎大家在評論給我留言~

源碼下載:

這里附上源碼地址(歡迎Star,歡迎Fork):StickHeaderView

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末源葫,一起剝皮案震驚了整個濱河市诗越,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌息堂,老刑警劉巖嚷狞,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異荣堰,居然都是意外死亡床未,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門振坚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薇搁,“玉大人,你說我怎么就攤上這事渡八】醒螅” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵屎鳍,是天一觀的道長宏娄。 經(jīng)常有香客問我,道長逮壁,這世上最難降的妖魔是什么孵坚? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮窥淆,結果婚禮上卖宠,老公的妹妹穿的比我還像新娘。我一直安慰自己忧饭,他們只是感情好扛伍,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眷昆,像睡著了一般蜒秤。 火紅的嫁衣襯著肌膚如雪汁咏。 梳的紋絲不亂的頭發(fā)上亚斋,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音攘滩,去河邊找鬼帅刊。 笑死,一個胖子當著我的面吹牛漂问,可吹牛的內容都是我干的赖瞒。 我是一名探鬼主播女揭,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼栏饮!你這毒婦竟也來了吧兔?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤袍嬉,失蹤者是張志新(化名)和其女友劉穎境蔼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伺通,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡箍土,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了罐监。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吴藻。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖弓柱,靈堂內的尸體忽然破棺而出沟堡,到底是詐尸還是另有隱情,我是刑警寧澤矢空,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布弦叶,位于F島的核電站,受9級特大地震影響妇多,放射性物質發(fā)生泄漏伤哺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一者祖、第九天 我趴在偏房一處隱蔽的房頂上張望立莉。 院中可真熱鬧,春花似錦七问、人聲如沸蜓耻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刹淌。三九已至,卻和暖如春讥耗,著一層夾襖步出監(jiān)牢的瞬間有勾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工古程, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蔼卡,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓挣磨,卻偏偏與公主長得像雇逞,于是被迫代替她去往敵國和親荤懂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容