記一次意外的自定義控件

有時(shí)候因妙,意外也許就會造成一個(gè)不經(jīng)意間的成功。

【注意:本文章前兩節(jié)盡是吐槽票髓,要看代碼攀涵,實(shí)現(xiàn)方案什么的,請直接看第三節(jié)】
【注意:本文章前兩節(jié)盡是吐槽洽沟,要看代碼以故,實(shí)現(xiàn)方案什么的,請直接看第三節(jié)】
【注意:本文章前兩節(jié)盡是吐槽裆操,要看代碼怒详,實(shí)現(xiàn)方案什么的,請直接看第三節(jié)】
重要的話要說三遍踪区。昆烁。。

咳咳缎岗,静尼,,咱們不是專業(yè)寫手传泊,就不要那么裝文藝了鼠渺,還是逗比點(diǎn)好。
不如咱們先上個(gè)圖眷细?


效果圖

咳咳拦盹,請忽略我豎屏錄制了啦。溪椎。普舆。。還有池磁,請忽略為啥那條線會在屏幕邊邊走奔害,在下不拘束它的自由←_←

起因

事情的起源是這樣滴,因?yàn)槟撤N需求地熄,咱們需要擼一個(gè)這樣子的控件(為了不泄露設(shè)計(jì)圖华临,咱們就拿MPAndroidChart的圖展示吧,反正需求都一樣):

偽設(shè)計(jì)圖

拿到設(shè)計(jì)圖端考,第一想法雅潭,這有多難揭厚,直接上MP庫唄,于是把庫放到MethodsCount一查扶供,哭了筛圆。。椿浓。2K多個(gè)方法欸太援,2K欸!0獍L岵怼!2KK癯ā<蠲伞!夯巷!

方法統(tǒng)計(jì)

遂放棄赛惩,,趁餐,還是自己開干吧

看到曲線什么的喷兼,第一時(shí)間**貝塞爾曲線**走起~ 于是,最為一個(gè)面向搜索引擎編程的程序員后雷,當(dāng)然谷歌一下貝塞爾褒搔。。喷面。

隨便搜搜,于是就看到CSDN的一篇文章文章點(diǎn)我走孽。

啊~好細(xì)致惧辈,好贊啊?拇伞:谐荨!可惜在下沒法短時(shí)間內(nèi)理解啊TAT困食。然而边翁,按照我平時(shí)的經(jīng)驗(yàn),還是擼個(gè)初步的東西出來吧硕盹。符匾。。

OMG

OMG....這神馬啊瘩例,這尖尖啊胶,都快能戳死人了好嗎甸各。。焰坪。趣倾。
于是,選擇戰(zhàn)略性撤退某饰,休息一晚再開干儒恋。

意外

第二天,毫無疑問的繼續(xù)一臉蒙逼黔漂。诫尽。。
這時(shí)候瘟仿,一位老朋友叫我?guī)退麚競€(gè)圖箱锐,是的,你沒看錯(cuò)劳较,摳圖驹止。。观蜗。臊恋。如果有看過我的一起擼個(gè)朋友圈系列文章的人,或許會知道墓捻,在下也會AE這個(gè)視頻后期軟件抖仅。。砖第。

摳就摳吧撤卢。。梧兼。放吩。但!S鸾堋渡紫!
意外就這么來了。考赛。惕澎。。摳圖的時(shí)候颜骤,為了邊緣平滑唧喉,我經(jīng)常調(diào)節(jié)錨點(diǎn),使曲線更加的平滑,然后居然讓我發(fā)現(xiàn)了一個(gè)規(guī)律0.0欣喧,大致原理如下吧:

AE

如圖腌零,如果多看幾遍,也許你會發(fā)現(xiàn)唆阿,當(dāng)兩個(gè)控制點(diǎn)的x位置在前后兩個(gè)坐標(biāo)內(nèi)益涧,而y分別與前后兩個(gè)坐標(biāo)平齊的時(shí)候,轉(zhuǎn)折點(diǎn)的銜接最為平滑驯鳖,否則妥妥的出現(xiàn)尖尖(嗯闲询。。浅辙。我還特地用鼠標(biāo)繞了幾圈標(biāo)出尖尖位置)扭弧。

媽蛋,得來毫不費(fèi)功夫啊记舆。鸽捻。。泽腮。真的想抱著我朋友親幾口御蒲,可惜在下不搞基- -

實(shí)現(xiàn)

既然找到了突破口,那妥妥的開干啊诊赊。

于是興沖沖的繼承View厚满,開始我們的偉業(yè):

public class TestView extends View {
    // 最大值
    private final float maxValue = 100f;
    // 測試數(shù)據(jù)
    private float[] testDatas = { 55f, 38f, 50f, 44f, 31f, 22f, 9f, 19f, 50f, 78f, 62f, 51f, 45f, 66f, 79f, 50f, 33f,
            24f, 26f, 58f };
    //private float[] testDatas = { 60f, 55f, 57f, 50f ,56f,70f};
    //private float[] testDatas = { 60f, 55f};

    // 點(diǎn)記錄
    private List<Point> datas;

    private final int num = 12;

    // 路徑
    private Path clicPath;
    // 漸變填充
    private Paint mPaint;
    
    // 輔助性畫筆
    private Paint controllPaintA;
    private Paint controllPaintB;
    private Path linePath;

    
    private PathMeasure mPathMeasure;
    private float[] mCurrentPosition = new float[2];
    private float[] mPrePosition = new float[2];

    LinearGradient mGradient;

    int width;
    int height;
    int offSet;

...構(gòu)造器初始化以上的東西

我們定義了一個(gè)最大值,和一組測試數(shù)據(jù)碧磅。這個(gè)最大值的作用是用來計(jì)算當(dāng)前數(shù)據(jù)在屏幕的y位置碘箍,比如這樣:最大值100,我們的數(shù)值15鲸郊,但我們的屏幕是720*1280丰榴,那么當(dāng)然不可以只畫15像素了,這怎么看得到嘛秆撮,我們的y位置判定為:

屏幕高度*(1-(15/100))

為什么要用1減去百分比多艇,因?yàn)樵c(diǎn)不在左下角而在左上角,所以我們需要減掉像吻。

接下來到measure初始化我們的點(diǎn)。

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
        height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
        offSet = width / testDatas.length;
        if (datas.size() == 0) {
            for (int i = 0; i < testDatas.length; i++) {
                float ratio = testDatas[i] / maxValue;
                Point point;
                if (i == 0) {
                    point = new Point(0, (int) (height * (1 - ratio)));
                }
                else if (i == testDatas.length - 1) {
                    point = new Point(width, (int) (height * (1 - ratio)));
                }
                else {
                    point = new Point(i * offSet, (int) (height * (1 - ratio)));
                }
                datas.add(point);
            }
        }
        if (mGradient == null) {
            mGradient = new LinearGradient(getMeasuredWidth() >> 1, getMeasuredHeight() >> 1, getMeasuredWidth() >> 1,
                    getMeasuredHeight(), Color.parseColor("#e0cab3"), Color.parseColor("#ffffff"),
                    Shader.TileMode.CLAMP);
            mPaint.setShader(mGradient);
        }
    }

其中我們的offSet是偏移量复隆,其作用是使點(diǎn)在屏幕上的x位置是均分的拨匆,然后初始化一個(gè)線性漸變。

這時(shí)候我們的點(diǎn)是這樣的(為了更方便查看挽拂,我們設(shè)定為橫屏并給上線條):

點(diǎn)和點(diǎn)之間的x偏移都是一致的(最后一個(gè)除外)

然后我們在onDraw開始繪制():

 @Override
    protected void onDraw(Canvas canvas) {
        clicPath.reset();
        super.onDraw(canvas);
        //clicPath.moveTo(datas.get(0).x, datas.get(0).y);
        for (int i = 0; i < datas.size() - 1; i++) {
            Point startPoint = datas.get(i);
            Point endPoint = datas.get(i + 1);
            if (i == 0) clicPath.moveTo(startPoint.x, startPoint.y);

            int controllA_X = (startPoint.x + endPoint.x) >>1;
            int controllA_Y = startPoint.y;
            int controllB_X = (startPoint.x + endPoint.x) >>1;
            int controllB_Y = endPoint.y;
            clicPath.cubicTo(controllA_X, controllA_Y, controllB_X, controllB_Y, endPoint.x, endPoint.y);

            // 控制點(diǎn)展示
            canvas.drawCircle(controllA_X,controllA_Y,5,controllPaintA);
            canvas.drawCircle(controllB_X,controllB_Y,5,controllPaintB);


            canvas.drawCircle(startPoint.x,startPoint.y,5,mPaint);


            //控制點(diǎn)展示
            canvas.drawLine(startPoint.x,startPoint.y,controllA_X,controllA_Y,mPaint);
            canvas.drawLine(endPoint.x,endPoint.y,controllB_X,controllB_Y,mPaint);

        }
        clicPath.lineTo(datas.get(datas.size() - 1).x, height);
        clicPath.lineTo(datas.get(0).x, height);
        clicPath.lineTo(datas.get(0).x, datas.get(0).y);
        canvas.drawPath(clicPath, mPaint);
    }

這里解析一下:
當(dāng)i==0惭每,也就是畫第一個(gè)點(diǎn)的時(shí)候,我們需要把畫筆移到我們第一個(gè)點(diǎn)的位置,否則永遠(yuǎn)都會從0台腥,0開始宏赘,以后就不需要移動了,因?yàn)楫嬐暌粭l線后黎侈,畫筆位置會停留在最后一個(gè)點(diǎn)察署。

我們可以看到兩個(gè)控制點(diǎn)的坐標(biāo),跟我們上面AE展示出來的是一樣的峻汉,x位置都是取兩個(gè)點(diǎn)的中間贴汪,y則是分別跟兩邊平齊,這樣的曲線最為圓滑

clicPath.cubicTo這個(gè)方法休吠,前面4個(gè)參數(shù)分別代表著控制點(diǎn)1的xy扳埂,控制點(diǎn)2的xy,最后一個(gè)參數(shù)則是結(jié)束點(diǎn)的xy瘤礁,在下一次循環(huán)到來之時(shí)阳懂,最后一個(gè)參數(shù)則會作為下一次繪制的起點(diǎn)。

最后別忘了在循環(huán)外面將path封閉起來柜思,我們不可以直接用path.close()岩调,因?yàn)閏lose方法是最后一個(gè)點(diǎn)與第一個(gè)點(diǎn)直接連一條直線的,但我們需要填充曲線下方酝蜒。

為了方便展示誊辉,我們添加了參考點(diǎn)以及將線條設(shè)置為stroke,先不填充:

預(yù)覽圖

可以看到亡脑,我們的控制點(diǎn)都很好的分布在兩點(diǎn)之間堕澄,曲線看起來十分平滑。

為了更清晰霉咨,我們將測試數(shù)據(jù)減少一點(diǎn):

private float[] testDatas = { 60f, 30f, 57f, 41f ,88f,70f};
預(yù)覽圖2

現(xiàn)在看起來更加的清晰蛙紫,然后我們填充一下并取消掉輔助線條和輔助點(diǎn)。

預(yù)覽圖3

現(xiàn)在初步達(dá)到我們的效果了途戒。坑傅。

然而,程序員的冤家產(chǎn)品卻說:哎喷斋,這太單調(diào)了唁毒,給個(gè)動畫唄。星爪。浆西。。

媽蛋M缣凇=恪!!久信!

不過罵完還是得干啊-T-

于是這次我們需要借助PathMeasure這個(gè)類

這個(gè)類通常用于將某個(gè)path轉(zhuǎn)換為一個(gè)具體的position窖杀,更多情況下是用作路徑動畫。

還記得我們之前定義的變量里面有些什么嗎:

    private PathMeasure mPathMeasure;
    private float[] mCurrentPosition = new float[2];
    private float[] mPrePosition = new float[2];

根據(jù)命名裙士,也很清楚是干啥的入客。

接下來繼續(xù)開工:

首先定義一個(gè)公用方法給外部調(diào)用:

public void startAnima(long duration) {}

我們通過這個(gè)方法來繪制線條

然后我們利用ValueAnimator來動態(tài)獲取我們path的坐標(biāo)

 public void startAnima(long duration) {
        if (mPathMeasure == null) mPathMeasure = new PathMeasure(clicPath, true);
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
        valueAnimator.setDuration(duration);
        // 減速插值器
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (Float) animation.getAnimatedValue();
                // 獲取當(dāng)前點(diǎn)坐標(biāo)封裝到mCurrentPosition
                mPathMeasure.getPosTan(value, mCurrentPosition, null);
                invalidate();
                if (value == mPathMeasure.getLength()) animaFirst = true;
            }
        });
        valueAnimator.start();
    }

為了防止onDraw里面多次繪制,我們定義一個(gè)animaFirst潮售。

然后補(bǔ)充我們的onDraw方法:

  @Override
    protected void onDraw(Canvas canvas) {
    ...
        if (animaFirst) {
            linePath.moveTo(datas.get(0).x, datas.get(0).y);
            mPrePosition[0] = datas.get(0).x;
            mPrePosition[1] = datas.get(0).y;
            animaFirst = false;
        }
        else {
            int controllA_X = (int) ((mPrePosition[0] + mCurrentPosition[0]) /2);
            int controllA_Y = (int) mPrePosition[1];
            int controllB_X = (int) ((mPrePosition[0] + mCurrentPosition[0]) /2);
            int controllB_Y = (int) mCurrentPosition[1];
            linePath.cubicTo(controllA_X, controllA_Y, controllB_X, controllB_Y, mCurrentPosition[0],
                    mCurrentPosition[1]);
            mPrePosition[0] = mCurrentPosition[0];
            mPrePosition[1] = mCurrentPosition[1];
        }
        canvas.drawPath(linePath, controllPaintA);
    }

如果動畫剛啟動痊项,我們就把點(diǎn)移到第一個(gè)點(diǎn)的位置,同時(shí)記錄
如果動畫已經(jīng)啟動了酥诽,我們就重復(fù)前面的步驟畫出貝塞爾鞍泉,當(dāng)然,你也可以直接lineTo肮帐,然后將當(dāng)前點(diǎn)付給前一個(gè)點(diǎn)咖驮。

最后,我們在onDetachedFromWindow清掉各種信息训枢,畢竟那啥托修,內(nèi)存還是挺珍貴的對吧-V-

   @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        
        datas.clear();
        clicPath=null;
        controllPaintA=null;
        controllPaintB=null;
        mPathMeasure=null;
        
    }

最終效果圖(未修復(fù)到屏幕邊邊繼續(xù)畫的問題。恒界。睦刃。,以及貌似有些地方有點(diǎn)偏差):

preview

【附】所有代碼(可以直接copy使用十酣,因?yàn)槭菧y試demo涩拙,所以并沒有封裝什么的,同時(shí)measure那里也沒有指定wrap_content時(shí)的大小耸采,大家可以自行封裝或修復(fù)或擴(kuò)展哈哈-V-):

/**
 * Created by 大燈泡 on 2016/2/29.
 */
public class TestView extends View {
    // 最大值
    private final float maxValue = 100f;
    // 測試數(shù)據(jù)
    //private float[] testDatas = { 55f, 38f, 50f, 44f, 31f, 22f, 9f, 19f, 50f, 78f, 62f, 51f, 45f, 66f, 79f, 50f, 33f,
    //        24f, 26f, 58f };
    private float[] testDatas = { 60f, 30f, 57f, 41f, 88f, 70f };
    //private float[] testDatas = { 60f, 55f};

    // 點(diǎn)記錄
    private List<Point> datas;
    // 路徑
    private Path clicPath;
    // 漸變填充
    private Paint mPaint;
    // 輔助性畫筆
    private Paint controllPaintA;
    private Paint controllPaintB;
    private Path linePath;

    private PathMeasure mPathMeasure;
    private float[] mCurrentPosition = new float[2];
    private float[] mPrePosition = new float[2];
    LinearGradient mGradient;
    int width;
    int height;
    int offSet;

    public TestView(Context context) {
        this(context, null);
    }

    public TestView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        clicPath = new Path();
        linePath = new Path();
        datas = new ArrayList<>();
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //mPaint.setStyle(Paint.Style.STROKE);
        controllPaintA = new Paint(Paint.ANTI_ALIAS_FLAG);
        controllPaintA.setStyle(Paint.Style.STROKE);
        controllPaintA.setStrokeWidth(5);
        controllPaintA.setColor(0xffff0000);

        controllPaintB = new Paint(Paint.ANTI_ALIAS_FLAG);
        controllPaintB.setStyle(Paint.Style.STROKE);
        controllPaintB.setColor(0xff00ff00);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
        height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
        offSet = width / testDatas.length;
        if (datas.size() == 0) {
            for (int i = 0; i < testDatas.length; i++) {
                float ratio = testDatas[i] / maxValue;
                Point point;
                if (i == 0) {
                    point = new Point(0, (int) (height * (1 - ratio)));
                }
                else if (i == testDatas.length - 1) {
                    point = new Point(width, (int) (height * (1 - ratio)));
                }
                else {
                    point = new Point(i * offSet, (int) (height * (1 - ratio)));
                }
                datas.add(point);
            }
        }
        if (mGradient == null) {
            mGradient = new LinearGradient(getMeasuredWidth() >> 1, getMeasuredHeight() >> 1, getMeasuredWidth() >> 1,
                    getMeasuredHeight(), Color.parseColor("#e0cab3"), Color.parseColor("#ffffff"),
                    Shader.TileMode.CLAMP);
            mPaint.setShader(mGradient);
        }
    }

    private boolean animaFirst = true;
    @Override
    protected void onDraw(Canvas canvas) {
        clicPath.reset();
        super.onDraw(canvas);
        for (int i = 0; i < datas.size() - 1; i++) {
            Point startPoint = datas.get(i);
            Point endPoint = datas.get(i + 1);
            if (i == 0) clicPath.moveTo(startPoint.x, startPoint.y);

            int controllA_X = (startPoint.x + endPoint.x) >> 1;
            int controllA_Y = startPoint.y;
            int controllB_X = (startPoint.x + endPoint.x) >> 1;
            int controllB_Y = endPoint.y;
            clicPath.cubicTo(controllA_X, controllA_Y, controllB_X, controllB_Y, endPoint.x, endPoint.y);
            /**輔助點(diǎn)和線**/
            //canvas.drawCircle(controllA_X,controllA_Y,5,controllPaintA);
            //canvas.drawCircle(controllB_X,controllB_Y,5,controllPaintB);

            //canvas.drawCircle(startPoint.x,startPoint.y,5,mPaint);

            //canvas.drawLine(startPoint.x,startPoint.y,controllA_X,controllA_Y,mPaint);
            //canvas.drawLine(endPoint.x,endPoint.y,controllB_X,controllB_Y,mPaint);

        }
        clicPath.lineTo(datas.get(datas.size() - 1).x, height);
        clicPath.lineTo(datas.get(0).x, height);
        clicPath.lineTo(datas.get(0).x, datas.get(0).y);
        canvas.drawPath(clicPath, mPaint);

        if (animaFirst) {
            linePath.moveTo(datas.get(0).x, datas.get(0).y);
            mPrePosition[0] = datas.get(0).x;
            mPrePosition[1] = datas.get(0).y;
            animaFirst = false;
        }
        else {
            int controllA_X = (int) ((mPrePosition[0] + mCurrentPosition[0]) / 2);
            int controllA_Y = (int) mPrePosition[1];
            int controllB_X = (int) ((mPrePosition[0] + mCurrentPosition[0]) / 2);
            int controllB_Y = (int) mCurrentPosition[1];
            linePath.cubicTo(controllA_X, controllA_Y, controllB_X, controllB_Y, mCurrentPosition[0],
                    mCurrentPosition[1]);
            mPrePosition[0] = mCurrentPosition[0];
            mPrePosition[1] = mCurrentPosition[1];
        }
        canvas.drawPath(linePath, controllPaintA);
    }

    public void startAnima(long duration) {
        if (mPathMeasure == null) mPathMeasure = new PathMeasure(clicPath, true);
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
        valueAnimator.setDuration(duration);
        // 減速插值器
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (Float) animation.getAnimatedValue();
                // 獲取當(dāng)前點(diǎn)坐標(biāo)封裝到mCurrentPosition
                mPathMeasure.getPosTan(value, mCurrentPosition, null);
                Log.d("curX",""+mCurrentPosition[0]);
                invalidate();
                if (value == mPathMeasure.getLength())
                    animaFirst = true;
            }
        });
        valueAnimator.start();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        datas.clear();
        clicPath = null;
        controllPaintA = null;
        controllPaintB = null;
        mPathMeasure = null;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末兴泥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子虾宇,更是在濱河造成了極大的恐慌搓彻,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘱朽,死亡現(xiàn)場離奇詭異旭贬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)搪泳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門稀轨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人森书,你說我怎么就攤上這事。” “怎么了凛膏?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵杨名,是天一觀的道長。 經(jīng)常有香客問我猖毫,道長台谍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任吁断,我火速辦了婚禮趁蕊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仔役。我一直安慰自己掷伙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布又兵。 她就那樣靜靜地躺著任柜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沛厨。 梳的紋絲不亂的頭發(fā)上宙地,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音逆皮,去河邊找鬼宅粥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛电谣,可吹牛的內(nèi)容都是我干的秽梅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辰企,長吁一口氣:“原來是場噩夢啊……” “哼风纠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起牢贸,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤竹观,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后潜索,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體臭增,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年竹习,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了誊抛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡整陌,死狀恐怖拗窃,靈堂內(nèi)的尸體忽然破棺而出瞎领,到底是詐尸還是另有隱情,我是刑警寧澤随夸,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布九默,位于F島的核電站,受9級特大地震影響宾毒,放射性物質(zhì)發(fā)生泄漏驼修。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一诈铛、第九天 我趴在偏房一處隱蔽的房頂上張望乙各。 院中可真熱鬧,春花似錦幢竹、人聲如沸耳峦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妇萄。三九已至,卻和暖如春咬荷,著一層夾襖步出監(jiān)牢的瞬間冠句,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工幸乒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懦底,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓罕扎,卻偏偏與公主長得像聚唐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子腔召,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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