2018-03-01

              Android开发之实现比特币走势图(仿股票走势图)

1、介绍

       最近有关区块链的项目层出不穷,在项目中碰到了绘制比特币涨幅走势图的需求,在网上搜了一些案例,大致就是利用绘制贝塞尔曲线来完成。

2、思路

思路来源于绘制股票走势图,主要内容在于自定义K_View,效果图:



大致步骤:

1、绘制X、Y坐标轴并绘制坐标;

2、根据数据绘制走势图,其实就是根据返回数据的多个点连成的平滑曲线;

3、根据手势找点,然后绘制当前所在点并显示具体数据。

相关数据对象类

public class FundMode  implements Serializable {

//x轴原始时间数据,ms

    public Stringtime;

public Stringlast;

public float dataY;

//在自定义view:FundView中的位置坐标

    public float floatX;

public float floatY;

}

public class XYEntityimplements Serializable{

public Listxlist;

public Listylist;

}

这里的内容只是让一些小白们看的更容易理解,早对象可以根据需求随意改动,各取所需。

Xml布局中代码

<XXXXX....K_VIew

    android:id="@+id/k_view"

    android:layout_width="match_parent"

    android:layout_height="@dimen/base130dp"

    app:xyTextSize="@dimen/base8sp"

    app:loadingTextSize="@dimen/base12sp"

    app:longPressTextSize="@dimen/base10sp"

    app:loadingText="正在加载数据...."

    app:xBottomTopPadding="@dimen/base8dp"

    app:reactWidth="@dimen/base100dp"

    app:reactHeight="@dimen/base38dp"

    app:reactTextMargin_top="@dimen/base8dp"

    app:reactTextMargin_bottom="@dimen/base23dp"

    app:paddingTop_FV="@dimen/base10dp"

    app:paddingBottom_FV="@dimen/base20dp"

    app:paddingRight_FV="@dimen/base10dp"

    app:yTextPadding="@dimen/base10dp"

    app:yTextRightPadding="@dimen/base5dp"

    android:background="#fff"/>


attrs.xml 中内容大致就是一些自定义属性,内容如下



activity 中需要执行的操作很少,从服务器获取到数据后 直接调用 k_view.setData(apiResult)即可;


下面是自定义view具体代码,所有逻辑都包括在里面了

public class K_View  extends View {

//控件默认宽高

private static final float DEF_WIDTH =650;

private static final float DEF_HIGHT =400;

//数据源

  ApiResult2<List< FundMode>,XYEntity>>apiResult;

//ApiResult2 服务器返回的数据,FundMode平滑曲线上的点,XYEntity X、Y对应坐标,下面贴出数据结构


//控件宽高

    int mWidth;

int mHeight;

//上下左右padding

    int mPaddingTop =30;

int mPaddingBottom =50;

int yTextPadding=20;

float mPaddingLeft =70;

int mPaddingRight =30;

int yTextRightPadding =5;

float textTopPadding=5;

int reactHeight=80;

int reactWidth=210;

int reactTextMargin_top=20;

int reactTextMargin_bottom=50

   FundMode mMinFundMode;

FundMode mMaxFundMode;

float maxY;

float minY;

//X、Y轴每一个data对应的大小

    float mPerX;

float mPerY;

//正在加载中

    Paint mLoadingPaint;

int mLoadingTextSize =20;

String mLoadingText ="";

boolean mDrawLoadingPaint =true;

Paint xyPaint;

//外围X、Y轴线文字

    Paint mXYPaint;

Paint longPressPaint;

//x、y轴指示文字字体的大小

    Paint rectTextPaint;

private int mXYTextSize =20;

//左侧文字距离左边线线的距离

    final float mLeftTxtPadding =5;

//底部文字距离底部线的距离

    int xBottomTopPadding =20;

float halfTextWidth_X=22;

private ListpointData=new ArrayList<>();

private int pointCount;

//内部X轴虚线

    Paint mInnerXPaint;

float mInnerXStrokeWidth =1;

//折线

    Paint mBrokenPaint;

Paint rectBGPaint;

Paint alphaPaint;

//单位:dp

//长按的十字线

    Paint mLongPressPaint;

Paint blueLinePaint;

boolean mDrawLongPressPaint =false;

//长按处理

    long mPressTime;

//默认多长时间算长按

    final long DEF_LONGPRESS_LENGTH =200;

float mPressX;

float mPressY;

//最上面默认显示累计收益金额

    final float mDefAllIncomeTextSize =20;

//长按情况下x轴和y轴要显示的文字

    Paint mLongPressTxtPaint;

int mLongPressTextSize =25;

public K_View(Context context) {

this(context,null);

}

public K_View(Context context, @Nullable AttributeSet attrs) {

this(context, attrs,0);

}

public K_View(Context context, @Nullable AttributeSet attrs,int defStyleAttr) {

super(context, attrs, defStyleAttr);

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.K_View);

mXYTextSize=a.getDimensionPixelSize(R.styleable.K_View_xyTextSize, mXYTextSize);

mLoadingTextSize=a.getDimensionPixelSize(R.styleable.K_View_loadingTextSize, mLoadingTextSize);

mLongPressTextSize=a.getDimensionPixelSize(R.styleable.K_View_longPressTextSize, mLongPressTextSize);

mLoadingText=a.getString(R.styleable.K_View_loadingText);

xBottomTopPadding=a.getDimensionPixelSize(R.styleable.K_View_xBottomTopPadding, xBottomTopPadding);

reactWidth=a.getDimensionPixelSize(R.styleable.K_View_reactWidth, reactWidth);

reactHeight=a.getDimensionPixelSize(R.styleable.K_View_reactHeight, reactHeight);

reactTextMargin_top=a.getDimensionPixelSize(R.styleable.K_View_reactTextMargin_top, reactTextMargin_top);

reactTextMargin_bottom=a.getDimensionPixelSize(R.styleable.K_View_reactTextMargin_bottom, reactTextMargin_bottom);

mPaddingTop=a.getDimensionPixelSize(R.styleable.K_View_paddingTop_FV, mPaddingTop);

mPaddingBottom=a.getDimensionPixelSize(R.styleable.K_View_paddingBottom_FV, mPaddingBottom);

mPaddingRight=a.getDimensionPixelSize(R.styleable.K_View_paddingRight_FV, mPaddingRight);

yTextPadding=a.getDimensionPixelSize(R.styleable.K_View_yTextPadding, yTextPadding);

yTextRightPadding =a.getDimensionPixelSize(R.styleable.K_View_yTextRightPadding, yTextRightPadding);

initAttrs();

}

@Override

protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);

int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

if (widthSpecMode == AT_MOST && heightSpecMode == AT_MOST) {

setMeasuredDimension((int) DEF_WIDTH, (int) DEF_HIGHT);

}else if (widthSpecMode == AT_MOST) {

setMeasuredDimension((int) DEF_WIDTH, heightSpecSize);

}else if (heightSpecMode == AT_MOST) {

setMeasuredDimension(widthSpecSize, (int) DEF_HIGHT);

}else {

setMeasuredDimension(widthSpecSize, heightSpecSize);

}

mWidth = getMeasuredWidth();

mHeight = getMeasuredHeight();

}

@Override

protected void onLayout(boolean changed,int left,int top,int right,int bottom) {

super.onLayout(changed, left, top, right, bottom);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//默认加载loading界面

        showLoadingPaint(canvas);

if (apiResult ==null || apiResult.data.size() ==0)return;

drawInnerXPaint(canvas);

drawBrokenPaint(canvas);

drawXYPaint(canvas);

drawLongPress(canvas);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

//                mPressTime = event.getDownTime();

                mPressX = event.getX();

mPressY = event.getY();

//处理长按后的逻辑

                showLongPressView();

break;

case MotionEvent.ACTION_MOVE:

mPressX = event.getX();

mPressY = event.getY();

//处理长按后的逻辑

                    showLongPressView();

//                }

                break;

case MotionEvent.ACTION_UP:

//处理松手后的逻辑

                hiddenLongPressView();

break;

default:

break;

}

return true;

}

private void initAttrs() {

intXYLintPaint();

initRectBGPaint();

initLoadingPaint();

initRectTextPaint();

initInnerXPaint();

initXYPaint();

initBrokenPaint();

initAlphaPaint();

initLongPressPaint();

initYLinePaint();

//        initTopTxt();

    }

private void intXYLintPaint() {

xyPaint =new Paint();

xyPaint.setColor(getColor(R.color.gray));

xyPaint.setStrokeWidth(mInnerXStrokeWidth);

}

private void initRectBGPaint() {

rectBGPaint = getRectBGPaint();

}

private void initLoadingPaint() {

mLoadingPaint =new Paint();

mLoadingPaint.setColor(getColor(R.color.gray));

mLoadingPaint.setTextSize(mLoadingTextSize);

mLoadingPaint.setAntiAlias(true);

}

//初始化绘制虚线的画笔

    private void initInnerXPaint() {

mInnerXPaint =new Paint();

mInnerXPaint.setColor(getColor(R.color.bg_lsj));

mInnerXPaint.setStrokeWidth(mInnerXStrokeWidth);

mInnerXPaint.setAlpha(40);

mInnerXPaint.setStyle(Paint.Style.STROKE);

}

private void initXYPaint() {

mXYPaint =new Paint();

mXYPaint.setColor(getColor(R.color.text_dz));

mXYPaint.setTextSize(mXYTextSize);

mXYPaint.setAntiAlias(true);

}

private void initLongPressTextPaint() {

longPressPaint =new Paint();

longPressPaint.setColor(getColor(R.color.text_dz));

longPressPaint.setTextSize(mXYTextSize);

longPressPaint.setAntiAlias(true);

}

private void initRectTextPaint() {

rectTextPaint =new Paint();

rectTextPaint.setColor(getColor(R.color.white));

rectTextPaint.setTextSize(mXYTextSize);

rectTextPaint.setAntiAlias(true);

}

private void initBrokenPaint() {

mBrokenPaint =new Paint();

mBrokenPaint.setColor(getColor(R.color.all_k_color));

mBrokenPaint.setStyle(Paint.Style.STROKE);

mBrokenPaint.setAntiAlias(true);

mBrokenPaint.setStrokeWidth(convertDp2Px(mInnerXStrokeWidth));

}

private void initAlphaPaint() {

alphaPaint =new Paint();

alphaPaint.setColor(getResources().getColor(R.color.all_k_color));

alphaPaint.setAntiAlias(true);

alphaPaint.setAlpha(40);

alphaPaint.setStyle(Paint.Style.FILL);

}

private void initLongPressPaint() {

mLongPressPaint =new Paint();

mLongPressPaint.setColor(getColor(R.color.white));

mLongPressPaint.setStyle(Paint.Style.FILL);

mLongPressPaint.setAntiAlias(true);

mLongPressPaint.setTextSize(mLongPressTextSize);

}

private void initYLinePaint() {

blueLinePaint =new Paint();

blueLinePaint.setColor(getColor(R.color.bg_lsj));

blueLinePaint.setStrokeWidth(1);

blueLinePaint.setAntiAlias(true);

}

private void showLoadingPaint(Canvas canvas) {

if (!mDrawLoadingPaint)return;

//这里特别注意,x轴的起始点要减去文字宽度的一半

        canvas.drawText(mLoadingText, mWidth /2 - mLoadingPaint.measureText(mLoadingText) /2, mHeight /2, mLoadingPaint);

}

private void drawInnerXPaint(Canvas canvas) {

//画5条横轴的虚线

//首先确定最大值和最小值的位置

        float perHight = (mHeight - mPaddingBottom - mPaddingTop) /3;

canvas.drawLine(mPaddingLeft,mPaddingTop,mPaddingLeft,mHeight - mPaddingBottom,xyPaint);

canvas.drawLine(mPaddingLeft,mHeight - mPaddingBottom,mPaddingLeft+mPerX*(apiResult.data.size()-1),mHeight - mPaddingBottom,xyPaint);

canvas.drawLine(0 + mPaddingLeft, mPaddingTop,

mPaddingLeft+mPerX*(apiResult.data.size()-1), mPaddingTop, mInnerXPaint);//最上面的那一条

        canvas.drawLine(0 + mPaddingLeft, mPaddingTop + perHight *1,

mPaddingLeft+mPerX*(apiResult.data.size()-1), mPaddingTop + perHight *1, mInnerXPaint);//2

        canvas.drawLine(0 + mPaddingLeft, mPaddingTop + perHight *2,

mPaddingLeft+mPerX*(apiResult.data.size()-1), mPaddingTop + perHight *2, mInnerXPaint);//4

    }

private void drawBrokenPaint(Canvas canvas) {

if(pointData.size()>0){//清空所有点

            pointData.clear();

}

//先画第一个点

        FundMode fundMode = apiResult.data.get(0);

Path path =new Path();

//这里需要说明一下,x轴的起始点,其实需要加上mPerX,但是加上之后不是从起始位置开始,不好看。

// 同理,for循环内x轴其实需要(i+1)。现在这样处理,最后会留一点空隙,其实挺好看的。

        float floatY = mHeight - mPaddingBottom - mPerY * ((Float.parseFloat(fundMode.last) - minY));

fundMode.floatX = mPaddingLeft;

fundMode.floatY = floatY;

Path timeAlphaPath =new Path();

timeAlphaPath.moveTo(mPaddingLeft, mHeight - mPaddingBottom);

timeAlphaPath.lineTo(mPaddingLeft, floatY);

for (int i =0; i < apiResult.data.size(); i++) {

FundMode fm = apiResult.data.get(i);

float floatX1 = mPaddingLeft + mPerX * i;

float floatY1 = mHeight - mPaddingBottom - mPerY * ((Float.parseFloat(fm.last) - minY));

fm.floatX = floatX1;

fm.floatY = floatY1;

if(i%pointCount==0||i==apiResult.data.size()-1){

pointData.add(fm);

}

if(i

float floatX2 = mPaddingLeft + mPerX * (i+1);

float floatY2 = mHeight - mPaddingBottom - mPerY * ( (Float.parseFloat(apiResult.data.get(i+1).last) - minY));

float wt = (floatX1 + floatX2) /2;

float floatX1_last=wt;

float floatY1_last =floatY1;

float floatX2_next=wt;

float floatY2_next = floatY2;

if(i==0){

path.moveTo(floatX1, floatY1);

}

path.cubicTo(floatX1_last, floatY1_last, floatX2_next, floatY2_next, floatX2, floatY2);

timeAlphaPath.cubicTo(floatX1_last, floatY1_last, floatX2_next, floatY2_next, floatX2, floatY2);

}else {

timeAlphaPath.lineTo(floatX1 , mHeight - mPaddingBottom);

Paint paint=new Paint();

paint.setColor(getColor(R.color.bg_lsj));

paint.setAntiAlias(true);

paint.setStrokeWidth(1);

paint.setStyle(Paint.Style.STROKE);

canvas.drawPath(path, paint);

paint.setAlpha(40);

paint.setStyle(Paint.Style.FILL);

canvas.drawPath(timeAlphaPath, paint);

}

}

}

private void drawXYPaint(Canvas canvas) {

//先处理y轴方向文字

        drawYPaint(canvas);

//处理x轴方向文字

        drawXPaint(canvas);

}

*

* @param canvas

*/

private void drawLongPress(Canvas canvas) {

if (!mDrawLongPressPaint)return;

//获取距离最近按下的位置的model

        float pressX = mPressX;

//循环遍历,找到距离最短的x轴的mode

        FundMode finalFundMode = pointData.get(0);

float minXLen = Integer.MAX_VALUE;

for (int i =0; i < pointData.size(); i++) {

FundMode currFunMode = pointData.get(i);

float abs = Math.abs(pressX - currFunMode.floatX);

if (abs < minXLen) {

finalFundMode = currFunMode;

minXLen = abs;

}

}

//x

        float topY=mHeight - mPaddingBottom - mPerY *  (maxY - minY);

int left=(int)finalFundMode.floatX-reactWidth>=mPaddingLeft?(int)finalFundMode.floatX-reactWidth:(int)finalFundMode.floatX;

int right=(int)finalFundMode.floatX-reactWidth>=mPaddingLeft?(int)finalFundMode.floatX:reactWidth+(int)finalFundMode.floatX;

int top=(int)finalFundMode.floatY-reactHeight>=(int)topY?(int)finalFundMode.floatY-reactHeight:(int)topY;

int bottom=(int)finalFundMode.floatY-reactHeight>=(int)topY?(int)finalFundMode.floatY:(int)topY+reactHeight;

Rect topDirty =new Rect(left, top, right, bottom);

canvas.drawRect(topDirty, rectBGPaint);

canvas.drawText(finalFundMode.time +"",

left+(reactWidth-mLongPressPaint.measureText(finalFundMode.time+""))/2,top+reactTextMargin_top+getFontHeight(mLongPressTextSize, mLongPressPaint) /2 , mLongPressPaint);

canvas.drawText("¥"+AppConfig.formatNum(finalFundMode.last) ,

left+(reactWidth-mLongPressPaint.measureText(finalFundMode.time+""))/2,top+reactTextMargin_bottom+getFontHeight(mLongPressTextSize, mLongPressPaint) /2 , mLongPressPaint);

}

public Paint getRectBGPaint() {

Paint paint =new Paint();

paint.setColor(Color.BLACK);

paint.setAntiAlias(true);

paint.setStrokeWidth(mInnerXStrokeWidth);

paint.setAlpha(85);

paint.setStyle(Paint.Style.FILL);

return paint;

}

//找到最大时间、最小时间和中间时间显示即可

    private void drawXPaint(Canvas canvas) {

if(apiResult.ext.xlist.size()>=6){

String firstX=apiResult.ext.xlist.get(0);

String secondX=apiResult.ext.xlist.get(1);

String thirdX=apiResult.ext.xlist.get(2);

String forthX=apiResult.ext.xlist.get(3);

String fifthX=apiResult.ext.xlist.get(4);

String sixX=apiResult.ext.xlist.get(5);

//x轴文字的高度

            halfTextWidth_X=mXYPaint.measureText(firstX)/2;

float hight = mHeight - mPaddingBottom + xBottomTopPadding;

canvas.drawText(firstX,

mPaddingLeft-halfTextWidth_X,

hight+textTopPadding, mXYPaint);

canvas.drawText(secondX,

mPaddingLeft + (mWidth - mPaddingLeft - mPaddingRight) /6-halfTextWidth_X,

hight+textTopPadding, mXYPaint);

canvas.drawText(thirdX,

mPaddingLeft + (mWidth - mPaddingLeft - mPaddingRight) /3f-halfTextWidth_X,

hight+textTopPadding, mXYPaint);

canvas.drawText(forthX,

mPaddingLeft + (mWidth - mPaddingLeft - mPaddingRight) /2-halfTextWidth_X,

hight+textTopPadding, mXYPaint);

canvas.drawText(fifthX,

mPaddingLeft + (mWidth - mPaddingLeft - mPaddingRight)*2 /3-halfTextWidth_X,

hight+textTopPadding, mXYPaint);

canvas.drawText(sixX,

mPaddingLeft + (mWidth - mPaddingLeft - mPaddingRight)*5/6-halfTextWidth_X,

hight+textTopPadding, mXYPaint);

}

}

private void drawYPaint(Canvas canvas) {

//现将最小值、最大值画好

//draw min

        if(apiResult.ext.ylist.size()>=4){

String firstY=apiResult.ext.ylist.get(0);

String secondY=apiResult.ext.ylist.get(1);

String thirdY=apiResult.ext.ylist.get(2);

String forthY=apiResult.ext.ylist.get(3);

float txtWigth = mXYPaint.measureText(firstY) ;

float perYWidth = (mHeight - mPaddingBottom - mPaddingTop) /3;

canvas.drawText(AppConfig.formatNum(firstY),

mPaddingLeft - txtWigth- yTextRightPadding,

mHeight - mPaddingBottom, mXYPaint);

//draw max

            canvas.drawText(AppConfig.formatNum(secondY),

mPaddingLeft - txtWigth- yTextRightPadding,

mPaddingTop+perYWidth*2+getFontHeight(mXYTextSize, mXYPaint) /4, mXYPaint);

canvas.drawText(AppConfig.formatNum(thirdY),

mPaddingLeft - txtWigth- yTextRightPadding,

mPaddingTop+perYWidth+getFontHeight(mXYTextSize, mXYPaint) /4, mXYPaint);

canvas.drawText(AppConfig.formatNum(forthY),

mPaddingLeft - txtWigth- yTextRightPadding,

mPaddingTop+getFontHeight(mXYTextSize, mXYPaint) /4, mXYPaint);

}

}

private void showLongPressView() {

mDrawLongPressPaint =true;

invalidate();

}

private void hiddenLongPressView() {

//实现蚂蚁金服延迟消失十字线

        postDelayed(new Runnable() {

@Override

public void run() {

mDrawLongPressPaint =false;

invalidate();

}

},200);

}

// 只需要把画笔颜色置为透明即可

    private void hiddenLoadingPaint() {

mLoadingPaint.setColor(0x00000000);

mDrawLoadingPaint =false;

}

private void showLoadingPaint() {

mLoadingPaint.setColor(getColor(R.color.gray));

mDrawLoadingPaint =true;

}

private int getColor(@ColorResint colorId) {

return getResources().getColor(colorId);

}

private float convertDp2Px(float dpValue) {

final float scale = getContext().getResources().getDisplayMetrics().density;

return (dpValue * scale +0.5f);

}

public float getFontHeight(float fontSize, Paint paint) {

paint.setTextSize(fontSize);

Paint.FontMetrics fm = paint.getFontMetrics();

return (float) (Math.ceil(fm.descent - fm.top) +2);

}

/**

* 程序入口,设置数据

*/

    public void setData(ApiResult2, XYEntity> apiResult) {

this.apiResult = apiResult;

if (apiResult ==null || apiResult.data.size() ==0) {

showLoadingPaint();

invalidate();

}else {

if(apiResult.ext.ylist.size()>=4){

mPaddingLeft= mXYPaint.measureText(apiResult.ext.ylist.get(3))+yTextPadding;

maxY=Float.parseFloat(apiResult.ext.ylist.get(3));

minY=Float.parseFloat(apiResult.ext.ylist.get(0));

}

mPerX = (mWidth - mPaddingLeft - mPaddingRight) / (apiResult.data.size()-1);

mPerY = ((mHeight - mPaddingTop - mPaddingBottom) /  (maxY - minY));

pointCount=(apiResult.data.size()-1)/24;

//数据过来,隐藏加载更多

            hiddenLoadingPaint();

//刷新界面

            invalidate();

}

}

}

3、总结

自定义K_View中代码没做封装提取,理解起来相对容易。具体内容都是一些尺寸计算,看起来有点烧脑,但是静下心来慢慢咀嚼,会发现其实整个实现过程非常简单。在这基础上朋友们可以根据需求随意改动,实现更丰富多彩的效果。



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容