以指定点(x,y)为中心绘制文字

本文部分图文摘录自这篇文章

自定义View的时候有时要在View里面绘制文字,就会调用Canvas的drawText系列方法,其中值得注意的就是drawText方法中的x和y这两个参数。按照Android的习惯,一般都会认为点(x,y)代表着文字所在的矩形的左上角的点,但实际上并不是!先来看看官方文档的解释:

@param x The x-coordinate of the origin of the text being drawn.The origin is interpreted based on the Align setting in the paint
@param y The y-coordinate of the baseline of the text being drawn

也就是说:
参数x代表文字开始被绘制的源点的横坐标,而文字从这个源点开始会如何被绘制则由Paint.Align这个枚举类型来决定(文章后面有效果图)。
Paint.Align有三个取值:
Paint.Align.LEFT 从源点开始向右绘制文字,即源点在整串文字的最左边;
Paint.Align.CENTER 从源点开始向左右两边绘制文字,即源点在整串文字的中间;
Paint.Align.RIGHT 从源点开始向左绘制文字,即源点在整串文字的最右边。
参数y代表文字的baseline的纵坐标,关于baseline的含义看下图:


除了基线以外,如上图所示,另外还有四条线,分别是ascent,descent,top,bottom,他们的意义分别是:

  • ascent: 系统建议的,绘制单个字符时,字符应当的最高高度所在线
  • descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
  • top: 可绘制的最高高度所在线
  • bottom: 可绘制的最低高度所在线

一般来说,文字都会落在ascent和descent这两条线之间,这也是系统建议的。一些特殊字符例外,但也不会超出top和bottom这两条线。由这几条线的值(可以由textPaint.getFontMetrics().bottom这样的方式得到,下面会提到)就可以比较精准的计算出文字区域的高度了。
所以,当文字的size和typeface(这个一般不用管,默认就行了)和绘制源点(x, y)都决定了之后,文字的位置也就决定了。
一般在绘制文字的时候,需求都是“在某个地方的中间绘制一串文字”,更通用的情况就是给定一个已知的点(originX, originY),将这个点作为将要绘制的字符串所在的矩形的四个顶点或中心绘制文字,这时就需要对文字的宽高进行测量。

网上搜集了一下测量文字的宽高主要有以下方法
测量文字宽度

private void measureTextWidth() {
        TextPaint textPaint = new TextPaint();
        textPaint.setTextSize(10);
        String str = ",";

        //方法一:利用textPaint的getTextBounds方法,可以得到文字所在的最小矩形的宽高
        Rect rect = new Rect();
        textPaint.getTextBounds(str, 0, str.length(), rect);
        Log.i("tag", "text's width by getTextBounds is: " + rect.width());

        //方法二:利用textPaint的measureText方法
        Log.i("tag", "text's width by measureText is: " + textPaint.measureText(str));

        //方法三:利用textPaint的getTextWidths方法,逐个计算出文字的宽,然后求和
        float[] widths = new float[str.length()];
        textPaint.getTextWidths(str, widths);
        float totalWidth = 0;
        for (float width : widths) {
            totalWidth += width;
        }
        Log.i("tag", "text's width by getTextWidths is: " + totalWidth);

    }

基本上方法二和方法三输出的结果是一样的,方法一的结果则不会大于方法二和方法三的结果。

测量文字的高度

private void measureTextHeight() {
        TextPaint textPaint = new TextPaint();
        textPaint.setTextSize(10);
        String str = ",";

        //方法一:利用textPaint的getTextBounds方法,可以得到文字所在的最小矩形的宽高
        Rect rect = new Rect();
        textPaint.getTextBounds(str, 0, str.length(), rect);
        Log.i("tag", "text's height by getTextBounds is: " + rect.height());

        //方法二:利用Paint.FontMetrics。
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        Log.i("tag", "text's height by getFontMetrics is: " + (fontMetrics.descent - fontMetrics.ascent));
        Log.i("tag", "text's height by getFontMetrics is: " + (fontMetrics.bottom - fontMetrics.top));
    }

以上方法中提到的文字所在的最小矩形就是下图的红色区域,绿色区域就是由top、bottom和x(Align.LEFT)以及textPaint.measureText(str)画出来的:

所以我一般是使用方法一来测量文字的宽高,毕竟是我们眼睛能看到的区域。

有了文字的宽高和已知的定点(x,y)就能比较准确的在想要的位置绘制文字了。来试一下以指定点(x,y)为中心绘制文字,代码如下:

public class DrawTextView extends View{

    public DrawTextView(Context context) {
        super(context);
        init();
    }

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

    public DrawTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private TextPaint textPaint;
    private String text = "g";
    private Rect minRect;
    private int textWidth;
    private int textHeight;

    private Paint rectPaint;
    private RectF rectF;

    private Paint pointPaint;
    private void init() {
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(200);
        textPaint.setTypeface(Typeface.DEFAULT);
        textPaint.setColor(Color.RED);
        minRect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), minRect);
        textWidth = minRect.width();
        textHeight = minRect.height();

        pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        pointPaint.setStrokeWidth(10);
        pointPaint.setColor(Color.BLACK);

        rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectPaint.setStyle(Paint.Style.FILL);
        rectPaint.setColor(Color.GREEN);
        rectF = new RectF();
    }

    private int originX;
    private int originY;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        originX = getMeasuredWidth() / 2;
        originY = getMeasuredHeight() / 2;


        /**
         *      _ _ _ _ _
         *     |         |
         *     |  (x,y)  |
         *     |----·    |height
         *     |    |    |
         *     |    |    |
         *     ·- - · - -·
         *        width
         *
         *  以指定点(x,y)为中心绘制文字,方框为待绘制文字所在的最小矩形,则矩形的各顶点均可以通过简单的逻辑换算得到。
         *  下面分别以Paint.Align.LEFT,Paint.Align.RIGHT和Paint.Align.CENTER三种方式绘制文字,
         *  其绘制源点分别为矩形底边的左右中三点
         */

        //Paint.Align.LEFT
        textPaint.setTextAlign(Paint.Align.LEFT);
        originY = 200;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX - textWidth / 2, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.RIGHT
        textPaint.setTextAlign(Paint.Align.RIGHT);
        originY = 500;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX + textWidth / 2, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.CENTER
        textPaint.setTextAlign(Paint.Align.CENTER);
        originY = 800;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

    }
}

运行效果如下:

发现绘制的字母“g”在Align.LEFT时偏右了;而在Align.RIGHT时偏左了;只有Align.CENTER时是正常的,虽然“g”并没有完全落到绿色的最小矩形里面,但是因为矩形的底边是它的baseline所以这样显示是正常的,下图有助于理解这一说法。

那为什么在Align.LEFT和Align.RIGHT时绘制文字会出现了一点偏移呢?原因我不太清楚,但是在查找原因的过程中我无意的将利用textPaint获得的文字所在的最小矩形minRect的位置信息的值打印了出来:

Log.i("tag",minRect.left + "");
Log.i("tag",minRect.top + "");
Log.i("tag",minRect.right + "");
Log.i("tag",minRect.bottom + "");

输出为:

11-09 22:08:17.047 22634-22634/com.eichinn.androidheroes I/tag: 9
11-09 22:08:17.047 22634-22634/com.eichinn.androidheroes I/tag: -108
11-09 22:08:17.047 22634-22634/com.eichinn.androidheroes I/tag: 99
11-09 22:08:17.047 22634-22634/com.eichinn.androidheroes I/tag: 42

这些数字很奇怪,如果将这个矩形在屏幕上画出来的话,会发现这个矩形的左下角非常接近(0,0),而且如果以(0,0)为源点绘制文字的话,则文字会完全落入到这个最小矩形里面。而textPaint的getTextBounds方法说明貌似也证实了这一点:

Return in bounds (allocated by the caller) the smallest rectangle that encloses all of the characters, with an implied origin at (0,0).

画出来看看:

public class DrawTextView extends View{

    public DrawTextView(Context context) {
        super(context);
        init();
    }

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

    public DrawTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private TextPaint textPaint;
    private String text = "g";
    private Rect minRect;
    private int textWidth;
    private int textHeight;

    private Paint rectPaint;
    private RectF rectF;

    private Paint pointPaint;
    private void init() {
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(200);
        textPaint.setTypeface(Typeface.DEFAULT);
        textPaint.setColor(Color.RED);
        minRect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), minRect);
        textWidth = minRect.width();
        textHeight = minRect.height();
        Log.i("tag", minRect.left + "");
        Log.i("tag", minRect.top + "");
        Log.i("tag", minRect.right + "");
        Log.i("tag", minRect.bottom + "");

        pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        pointPaint.setStrokeWidth(10);
        pointPaint.setColor(Color.BLACK);

        rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectPaint.setStyle(Paint.Style.FILL);
        rectPaint.setColor(Color.GREEN);
        rectF = new RectF();
    }

    private int originX;
    private int originY;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //以(0,0)为源点绘制文字的话,文字则会完全落入到这个最小矩形里面。
        textPaint.setTextAlign(Paint.Align.LEFT);
        originX = 0;
        originY = 0;
        canvas.drawRect(minRect, rectPaint);
        canvas.drawText(text, originX, originY,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        /**
         *      _ _ _ _ _
         *     |         |
         *     |  (x,y)  |
         *     |----·    |height
         *     |    |    |
         *     |    |    |
         *     ·- - · - -·
         *        width
         *
         *  以指定点(x,y)为中心绘制文字,方框为待绘制文字所在的最小矩形,则矩形的各顶点均可以通过简单的逻辑换算得到。
         *  下面分别以Paint.Align.LEFT,Paint.Align.RIGHT和Paint.Align.CENTER三种方式绘制文字,
         *  其绘制源点分别为矩形底边的左右中三点
         */

        originX = getMeasuredWidth() / 2;
        //Paint.Align.LEFT
        textPaint.setTextAlign(Paint.Align.LEFT);
        originY = 200;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX - textWidth / 2, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.RIGHT
        textPaint.setTextAlign(Paint.Align.RIGHT);
        originY = 500;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX + textWidth / 2, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.CENTER
        textPaint.setTextAlign(Paint.Align.CENTER);
        originY = 800;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

    }
}

效果图:

于是,对于在Align.LEFT和Align.RIGHT时绘制文字会出现一点偏移这个问题,我做出了如下猜想:
当textPaint的Align方式为LEFT或RIGHT时,系统不会在x处就开始往右或往左绘制文字,而是会留出一定的空间再开始绘制,目的应该就是当x为0或者为屏幕宽度时绘制的文字不会紧贴着屏幕边缘,那样会显得不美观。
那这个空间到底是多大呢?就是minRect.left的值。所以想要文字在x处就开始绘制的话,当Align为LEFT时,x的值要减去minRect.left,而当Align为RIGHT时,x的值要加上minRect.left。

修改的程序:

public class DrawTextView extends View{

    public DrawTextView(Context context) {
        super(context);
        init();
    }

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

    public DrawTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private TextPaint textPaint;
    private String text = "g";
    private Rect minRect;
    private int textWidth;
    private int textHeight;

    private Paint rectPaint;
    private RectF rectF;

    private Paint pointPaint;
    private void init() {
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(200);
        textPaint.setTypeface(Typeface.DEFAULT);
        textPaint.setColor(Color.RED);
        minRect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), minRect);
        textWidth = minRect.width();
        textHeight = minRect.height();
        Log.i("tag", minRect.left + "");
        Log.i("tag", minRect.top + "");
        Log.i("tag", minRect.right + "");
        Log.i("tag", minRect.bottom + "");

        pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        pointPaint.setStrokeWidth(10);
        pointPaint.setColor(Color.BLACK);

        rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectPaint.setStyle(Paint.Style.FILL);
        rectPaint.setColor(Color.GREEN);
        rectF = new RectF();
    }

    private int originX;
    private int originY;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //以(0,0)为源点绘制文字的话,文字则会完全落入到这个最小矩形里面。
        textPaint.setTextAlign(Paint.Align.LEFT);
        originX = 0;
        originY = 0;
        canvas.drawRect(minRect, rectPaint);
        canvas.drawText(text, originX, originY,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        /**
         *      _ _ _ _ _
         *     |         |
         *     |  (x,y)  |
         *     |----·    |height
         *     |    |    |
         *     |    |    |
         *     ·- - · - -·
         *        width
         *
         *  以指定点(x,y)为中心绘制文字,方框为待绘制文字所在的最小矩形,则矩形的各顶点均可以通过简单的逻辑换算得到。
         *  下面分别以Paint.Align.LEFT,Paint.Align.RIGHT和Paint.Align.CENTER三种方式绘制文字,
         *  其绘制源点分别为矩形底边的左右中三点
         */

        originX = getMeasuredWidth() / 2;
        //Paint.Align.LEFT
        textPaint.setTextAlign(Paint.Align.LEFT);
        originY = 200;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX - textWidth / 2 - minRect.left, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.RIGHT
        textPaint.setTextAlign(Paint.Align.RIGHT);
        originY = 500;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX + textWidth / 2 + minRect.left, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.CENTER
        textPaint.setTextAlign(Paint.Align.CENTER);
        originY = 800;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX, originY + textHeight / 2,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

    }
}

修改后的效果:


这就正常了。可能有人会发现在Paint.Align.RIGHT和Paint.Align.CENTER时其实文字还是有点偏左,没错,但是只要绘制另外的文字,比如说绘制字母“y”就完全没问题了,猜想可能是字母“g”这个字本身的特点,再者这点偏差也是在接受范围内的,特别是在没有那绿色区域可比较的时候就更不明显了。

然而事情并没有结束,虽然说“g”的尾巴在baseline的下面是正常的,但是由于需求的原因,还是希望“g”能整体落在最小矩形里面,毕竟需求是“以指定点(x,y)为中心绘制文字”嘛,有了上面的经验,马上就能想到了minRect.bottom,因为在以(0,0)为源点绘制“g”的时候,“g”在baseline以下的部分刚好出现在了屏幕里面。

这样的话是不是只要将绘制的源点往上移动minRect.bottom,即y - minRect.bottom就行了呢?试一试:

public class DrawTextView extends View{

    public DrawTextView(Context context) {
        super(context);
        init();
    }

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

    public DrawTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private TextPaint textPaint;
    private String text = "g";
    private Rect minRect;
    private int textWidth;
    private int textHeight;

    private Paint rectPaint;
    private RectF rectF;

    private Paint pointPaint;
    private void init() {
        textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(200);
        textPaint.setTypeface(Typeface.DEFAULT);
        textPaint.setColor(Color.RED);
        minRect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), minRect);
        textWidth = minRect.width();
        textHeight = minRect.height();
        Log.i("tag", minRect.left + "");
        Log.i("tag", minRect.top + "");
        Log.i("tag", minRect.right + "");
        Log.i("tag", minRect.bottom + "");

        pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        pointPaint.setStrokeWidth(10);
        pointPaint.setColor(Color.BLACK);

        rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        rectPaint.setStyle(Paint.Style.FILL);
        rectPaint.setColor(Color.GREEN);
        rectF = new RectF();
    }

    private int originX;
    private int originY;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //以(0,0)为源点绘制文字的话,文字则会完全落入到这个最小矩形里面。
        textPaint.setTextAlign(Paint.Align.LEFT);
        originX = 0;
        originY = 0;
        canvas.drawRect(minRect, rectPaint);
        canvas.drawText(text, originX, originY,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        /**
         *      _ _ _ _ _
         *     |         |
         *     |  (x,y)  |
         *     |----·    |height
         *     |    |    |
         *     |    |    |
         *     ·- - · - -·
         *        width
         *
         *  以指定点(x,y)为中心绘制文字,方框为待绘制文字所在的最小矩形,则矩形的各顶点均可以通过简单的逻辑换算得到。
         *  下面分别以Paint.Align.LEFT,Paint.Align.RIGHT和Paint.Align.CENTER三种方式绘制文字,
         *  其绘制源点分别为矩形底边的左右中三点
         */

        originX = getMeasuredWidth() / 2;
        //Paint.Align.LEFT
        textPaint.setTextAlign(Paint.Align.LEFT);
        originY = 200;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX - textWidth / 2 - minRect.left, originY + textHeight / 2 - minRect.bottom,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.RIGHT
        textPaint.setTextAlign(Paint.Align.RIGHT);
        originY = 500;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX + textWidth / 2 + minRect.left, originY + textHeight / 2 - minRect.bottom,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

        //Paint.Align.CENTER
        textPaint.setTextAlign(Paint.Align.CENTER);
        originY = 800;
        rectF.set(originX - textWidth / 2, originY - textHeight / 2, originX + textWidth / 2, originY + textHeight / 2);
        canvas.drawRect(rectF, rectPaint);
        canvas.drawText(text, originX, originY + textHeight / 2 - minRect.bottom,  textPaint);
        canvas.drawPoint(originX, originY, pointPaint);

    }
}

运行一下:

Good!!!再试试绘制其它文字:

没问题,OK,那么到此为止,“以指定点(x,y)为中心绘制文字”这个需求就算完成了。

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

推荐阅读更多精彩内容