Android 自定义电动车充电进度条

  最近在做电动车的充电功能,其中有个充电过程中,隔一段时间去更新充电状态的功能。充电过程中,相对于数据的改变,电池的电量更受用户关注。所以这里面就涉及到自定义View,整个过程主要涉及到测量和绘制,这其中又包括背景颜色绘制、线条的绘制、文本绘制、多边形的绘制。

测量

  测试量设置一个宽高最小值,并且整个自定义包含电池,剩余部分有文字绘制等,因此电池的宽高并不是测量的宽高。如果对测量模糊的,建议去学一下MeasureSpec,这是我之前写的一篇文章
MeasureSpec理解

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val dw = MIN_WIDTH + paddingLeft + paddingRight
        val dh = MIN_HEIGHT + paddingTop + paddingBottom
        measureWidth = resolveSizeAndState(dw, widthMeasureSpec, 0)
        measureHeight = resolveSizeAndState(dh, heightMeasureSpec, 0)
        batteryHeight = measureHeight / 8 * 5
        batteryWidth = if (measureWidth / 10 < limitTextWidth / 2) {
            measureWidth - (limitTextWidth / 2 - measureWidth / 10) - LIMIT_TEXT_OFFSET_X
        } else {
            measureWidth - LIMIT_TEXT_OFFSET_X
        }
        setMeasuredDimension(measureWidth, measureHeight)
    }

多边形绘制

  因为充电的电池属于不是长方形,因此不能使用绘制长方形的方式。通过计算进度过程中各个点的坐标,连接各个点使之形成一个不规则图形。项目中主要是绘制等腰梯形,计算每个进度下的四个坐标点,当进度有更新的时候去更新path路径,最后合闭路径形成梯形。主要代码如下:

/**
     * 绘制进度
     *
     * @param canvas
     * @param path
     * @param paint
     */
    private fun drawProgress(canvas: Canvas, path: Path, paint: Paint) {
        path.reset()
        path.moveTo(OFFSET, FLOAT_0)
        path.lineTo(FLOAT_0, batteryHeight.toFloat())
        path.lineTo((batteryWidth * progress / max).toFloat(), batteryHeight.toFloat())
        path.lineTo((batteryWidth - OFFSET * 2) * progress / max + OFFSET, FLOAT_0)
        path.lineTo(OFFSET, FLOAT_0)
        path.close()
        canvas.drawPath(path, paint)
    }

说明:其中OFFSET是为了形成等腰梯形设置一个偏移量,当OFFSET为0时候则是长方形。

线条绘制

  线条的绘制就很简单,直接调drawLine()方法,其中关于虚线的绘制,需要设置Paint的属性pathEffect,其中填虚实线各占多少。

/**
     * 绘制虚线
     *
     * @param canvas
     * @param paint
     */
    private fun drawDashLine(canvas: Canvas, paint: Paint) {
        val startX = (batteryWidth - OFFSET * 2) / 10f * 9 + OFFSET
        val endX = batteryWidth / 10f * 9
        canvas.drawLine(startX, FLOAT_0, endX, batteryHeight.toFloat(), paint)
    }

文本的绘制

  文字的绘制与其他图形绘制最大的不同就是其绘制的坐标点,文本的绘制是找文本的左下坐标为基点。并且文本的绘制还涉及到文本居中的问题,文本的绘制是以textbaseline为Y方向的坐标,这样才能使文本在垂直方向上居中。其中找textbaseline有两种方式:

  • 第一种:测算获取文本的高度,这种适合文本不再更改的情况
  • 第二种:测算文本FontMetrics属性,然后计算其baseline
/**
     * 绘制进度文本
     *
     * @param canvas
     * @param paint
     */
    private fun drawProgressText(canvas: Canvas, paint: Paint) {
        progressText = String.format(context.getString(R.string.charging_precent_placeholder), progress)
        val textBaseLine = batteryHeight / 2 - (progressFontMetrics.descent + progressFontMetrics.ascent) / 2
        canvas.drawText(progressText, 0, progressText.length, (batteryWidth - progressTextWidth) / 2.toFloat(), textBaseLine, paint)
    }

状态保存

自定义View的话有些时候遇到横竖屏切换,状态就会改变,因此像系统的很多View组件都是做了状态保存功能,如果对View状态保存不是太熟悉的可以上网搜一下相关的文章看一下。

//*************保存进度状态**********************
    override fun onSaveInstanceState(): Parcelable {
        val parcelable = super.onSaveInstanceState()
        val ss = SavedState(parcelable)
        ss.progress = progress
        return ss
    }

    override fun onRestoreInstanceState(state: Parcelable) {
        val ss = state as SavedState
        super.onRestoreInstanceState(ss.superState)
        progress = ss.progress
    }

    internal class SavedState : BaseSavedState {
        var progress = 0

        constructor(superState: Parcelable?) : super(superState)
        private constructor(save: Parcel) : super(save) {
            progress = save.readInt()
        }

        override fun writeToParcel(restore: Parcel, flags: Int) {
            super.writeToParcel(restore, flags)
            restore.writeValue(progress)
        }

        override fun describeContents(): Int {
            return 0
        }

        companion object CREATOR : Parcelable.Creator<SavedState> {
            override fun createFromParcel(parcel: Parcel): SavedState {
                return SavedState(parcel)
            }

            override fun newArray(size: Int): Array<SavedState?> {
                return arrayOfNulls(size)
            }
        }
    }

完整代码

代码
class ChargingProgressView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {

    companion object {
        //属性默认值
        const val PROGRESS = 10                                //默认进度
        const val LIMIT_MAX = 90                               //进度最大值默认
        const val PROGRESS_MAX = 100                           //默认最大进度
        const val BACKGROUND_COLOR = Color.GRAY                //默认背景颜色
        const val PROGRESS_COLOR = Color.GREEN                 //默认进度颜色
        const val SECONDARY_START_COLOR = Color.TRANSPARENT    //默认灰色进度开始颜色
        const val SECONDARY_END_COLOR = Color.GREEN            //默认灰色进度结束颜色
        const val LINE_COLOR = Color.WHITE                     //默认灰色进度结束颜色
        const val LINE_WIDTH = 1F                              //默认线条宽度
        const val PROGRESS_TEXT_COLOR = Color.WHITE            //默认进度文本字体颜色
        const val PROGRESS_TEXT_SIZE = 15F                     //默认进度文本字体大小
        const val LIMIT_TEXT_COLOR = Color.WHITE               //默认限制进度文本字体颜色
        const val LIMIT_TEXT_SIZE = 15F                        //默认限制进度文本字体大小

        //参数默认值
        const val MIN_WIDTH = 400           //默认最小宽度
        const val MIN_HEIGHT = 80           //默认最小宽度
        const val OFFSET = 0F               //上部偏移量
        const val FLOAT_0 = 0F              // float 0
        const val TRIANGLE_HEIGHT = 10      //三角形高度
        const val LIMIT_TEXT_OFFSET_X = 5   //限制进度文本X偏移量
        const val LIMIT_TEXT_OFFSET_Y = 5   //限制进度文本Y偏移量

    }

    //自定义属性
    private var max: Int
    private val backgroundColor: Int
    private val progressColor: Int
    private val secondaryStartColor: Int
    private val secondaryEndColor: Int
    private val lineColor: Int
    private val lineWidth: Float
    private val progressTextColor: Int
    private val progressTextSize: Float
    private val limitTextColor: Int
    private val limitTextSize: Float

    //可设置属性 目前只设置这两种
    var limit: Int = LIMIT_MAX
        set(value) {
            if (value < 0 || value >= max || value == limit) {
                return
            }
            field = value
            invalidate()
        }

    var progress: Int = PROGRESS
        set(value) {
            if (value < 0 || value > limit || value == progress) {
                return
            }
            field = value
            invalidate()
        }

    //参数
    private var measureWidth = 0 //宽度
    private var measureHeight = 0 //高度
    private var batteryWidth = 0 //电池的宽度
    private var batteryHeight = 0 //电池的高度

    //绘制背景
    private var backgroundPath: Path
    private var backgroundPaint: Paint

    //绘制线条
    private var linePaint: Paint
    private var dashLinePaint: Paint

    //绘制灰色进度
    private var secondaryPath: Path
    private var secondaryPaint: Paint

    //绘制进度
    private var progressPath: Path
    private var progressPaint: Paint

    //绘制进度文字
    private var progressText: String
    private var progressTextWidth = 0
    private val progressTextPaint: Paint
    private val progressTextBound: Rect
    private var progressFontMetrics: FontMetrics

    //绘制三角形标签
    private val trianglePaint: Paint
    private val trianglePath: Path
    private val triangleHeight = TRIANGLE_HEIGHT

    //绘制进度上限文本
    private var limitText: String
    private var limitTextWidth = 0
    private var limitTextHeight = 0
    private val limitTextBound: Rect
    private val limitTextPaint: Paint
    private val limitFontMetrics: FontMetrics

    init {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.ChargingProgressView)
        progress = ta.getInteger(R.styleable.ChargingProgressView_charging_progress, PROGRESS)
        max = ta.getInteger(R.styleable.ChargingProgressView_charging_max, PROGRESS_MAX)
        limit = ta.getInteger(R.styleable.ChargingProgressView_charging_limit, LIMIT_MAX)
        backgroundColor = ta.getColor(R.styleable.ChargingProgressView_charging_backgroundColor, BACKGROUND_COLOR)
        progressColor = ta.getColor(R.styleable.ChargingProgressView_charging_progressColor, PROGRESS_COLOR)
        secondaryStartColor = ta.getColor(R.styleable.ChargingProgressView_charging_secondaryStartColor, SECONDARY_START_COLOR)
        secondaryEndColor = ta.getColor(R.styleable.ChargingProgressView_charging_secondaryEndColor, SECONDARY_END_COLOR)
        lineColor = ta.getColor(R.styleable.ChargingProgressView_charging_lineColor, LINE_COLOR)
        lineWidth = ta.getFloat(R.styleable.ChargingProgressView_charging_lineWidth, LINE_WIDTH)
        progressTextColor = ta.getColor(R.styleable.ChargingProgressView_charging_progressTextColor, PROGRESS_TEXT_COLOR)
        progressTextSize = ta.getFloat(R.styleable.ChargingProgressView_charging_progressTextSize, PROGRESS_TEXT_SIZE)
        limitTextColor = ta.getColor(R.styleable.ChargingProgressView_charging_limitTextColor, LIMIT_TEXT_COLOR)
        limitTextSize = ta.getFloat(R.styleable.ChargingProgressView_charging_limitTextSize, LIMIT_TEXT_SIZE)
        ta.recycle()
        //绘制背景
        backgroundPath = Path()
        backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        backgroundPaint.color = backgroundColor
        backgroundPaint.style = Paint.Style.FILL
        //绘制线条
        linePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        linePaint.color = lineColor
        linePaint.strokeWidth = lineWidth
        linePaint.style = Paint.Style.FILL
        dashLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        dashLinePaint.color = lineColor
        dashLinePaint.strokeWidth = lineWidth
        dashLinePaint.pathEffect = DashPathEffect(floatArrayOf(5f, 5f), FLOAT_0)
        dashLinePaint.style = Paint.Style.FILL
        //绘制灰色进度
        secondaryPath = Path()
        secondaryPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        secondaryPaint.style = Paint.Style.FILL
        //绘制进度
        progressPath = Path()
        progressPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        progressPaint.color = progressColor
        progressPaint.style = Paint.Style.FILL
        //绘制进度文字
        progressTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        progressTextPaint.color = progressTextColor
        progressTextPaint.textSize = progressTextSize
        progressText = String.format(context.getString(R.string.charging_precent_placeholder), progress)
        progressTextBound = Rect()
        progressTextPaint.getTextBounds(progressText, 0, progressText.length, progressTextBound)
        progressTextWidth = progressTextBound.width()
        progressFontMetrics = progressTextPaint.fontMetrics
        //绘制三角形标签
        trianglePath = Path()
        trianglePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        trianglePaint.color = Color.WHITE
        trianglePaint.style = Paint.Style.FILL
        //绘制进度上限文本
        limitTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        limitTextPaint.color = limitTextColor
        limitTextPaint.textSize = limitTextSize
        limitText = String.format(context.getString(R.string.charging_limit_max), limit)
        limitTextBound = Rect()
        limitTextPaint.getTextBounds(limitText, 0, limitText.length, limitTextBound)
        limitTextWidth = limitTextBound.width()
        limitTextHeight = limitTextBound.height()
        limitFontMetrics = limitTextPaint.fontMetrics
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val dw = MIN_WIDTH + paddingLeft + paddingRight
        val dh = MIN_HEIGHT + paddingTop + paddingBottom
        measureWidth = resolveSizeAndState(dw, widthMeasureSpec, 0)
        measureHeight = resolveSizeAndState(dh, heightMeasureSpec, 0)
        batteryHeight = measureHeight / 8 * 5
        batteryWidth = if (measureWidth / 10 < limitTextWidth / 2) {
            measureWidth - (limitTextWidth / 2 - measureWidth / 10) - LIMIT_TEXT_OFFSET_X
        } else {
            measureWidth - LIMIT_TEXT_OFFSET_X
        }
        setMeasuredDimension(measureWidth, measureHeight)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //先绘制固定背景色
        drawBackground(canvas, backgroundPath, backgroundPaint)
        //绘制线条
        drawLine(canvas, linePaint)
        //绘制进度限制线条
        drawDashLine(canvas, dashLinePaint)
        //绘制灰色进度
        drawSecondaryProgress(canvas, secondaryPath, secondaryPaint)
        //绘制进度
        drawProgress(canvas, progressPath, progressPaint)
        //绘制进度文字
        drawProgressText(canvas, progressTextPaint)
        //绘制三角形标签
        drawTriangle(canvas, trianglePath, trianglePaint)
        //绘制最大阈值的文本
        drawLimitText(canvas, limitTextPaint)
    }

    /**
     * 绘制进度限度文本
     *
     * @param canvas
     * @param paint
     */
    private fun drawLimitText(canvas: Canvas, paint: Paint) {
        limitText = String.format(context.getString(R.string.charging_limit_max), limit)
        val textBaseLine = batteryHeight + triangleHeight +
                limitFontMetrics.descent - limitFontMetrics.ascent + LIMIT_TEXT_OFFSET_Y
        canvas.drawText(limitText, 0, limitText.length, (batteryWidth * limit / max - limitTextWidth / 2).toFloat(), textBaseLine, paint)
    }

    /**
     * 绘制进度限度三角标
     *
     * @param canvas
     * @param path
     * @param paint
     */
    private fun drawTriangle(canvas: Canvas, path: Path, paint: Paint) {
        path.reset()
        path.moveTo((batteryWidth * limit / max).toFloat(), (batteryHeight).toFloat())
        path.lineTo((batteryWidth * limit / max + triangleHeight / 2).toFloat(), (batteryHeight + triangleHeight).toFloat())
        path.lineTo((batteryWidth * limit / max - triangleHeight / 2).toFloat(), (batteryHeight + triangleHeight).toFloat())
        path.lineTo((batteryWidth * limit / max).toFloat(), (batteryHeight).toFloat())
        path.close()
        canvas.drawPath(path, paint)
    }

    /**
     * 绘制进度文本
     *
     * @param canvas
     * @param paint
     */
    private fun drawProgressText(canvas: Canvas, paint: Paint) {
        progressText = String.format(context.getString(R.string.charging_precent_placeholder), progress)
        val textBaseLine = batteryHeight / 2 - (progressFontMetrics.descent + progressFontMetrics.ascent) / 2
        canvas.drawText(progressText, 0, progressText.length, (batteryWidth - progressTextWidth) / 2.toFloat(), textBaseLine, paint)
    }

    /**
     * 绘制进度
     *
     * @param canvas
     * @param path
     * @param paint
     */
    private fun drawProgress(canvas: Canvas, path: Path, paint: Paint) {
        path.reset()
        path.moveTo(OFFSET, FLOAT_0)
        path.lineTo(FLOAT_0, batteryHeight.toFloat())
        path.lineTo((batteryWidth * progress / max).toFloat(), batteryHeight.toFloat())
        path.lineTo((batteryWidth - OFFSET * 2) * progress / max + OFFSET, FLOAT_0)
        path.lineTo(OFFSET, FLOAT_0)
        path.close()
        canvas.drawPath(path, paint)
    }

    /**
     * 绘制灰色进度
     *
     * @param canvas
     * @param path
     * @param paint
     */
    private fun drawSecondaryProgress(canvas: Canvas, path: Path, paint: Paint) {
        path.reset()
        path.moveTo((batteryWidth - OFFSET * 2) * progress / max + OFFSET, FLOAT_0)
        path.lineTo((batteryWidth - OFFSET * 2) * limit / max + OFFSET, FLOAT_0)
        path.lineTo((batteryWidth * limit / max).toFloat(), batteryHeight.toFloat())
        path.lineTo((batteryWidth * progress / max).toFloat(), batteryHeight.toFloat())
        path.lineTo((batteryWidth - OFFSET * 2) * progress / max + OFFSET, FLOAT_0)
        path.close()
        paint.shader = LinearGradient(
            FLOAT_0, FLOAT_0, batteryWidth.toFloat(),
            FLOAT_0, intArrayOf(secondaryStartColor, secondaryEndColor),
            null, Shader.TileMode.CLAMP
        )
        canvas.drawPath(path, paint)
    }

    /**
     * 绘制虚线
     *
     * @param canvas
     * @param paint
     */
    private fun drawDashLine(canvas: Canvas, paint: Paint) {
        val startX = (batteryWidth - OFFSET * 2) / 10f * 9 + OFFSET
        val endX = batteryWidth / 10f * 9
        canvas.drawLine(startX, FLOAT_0, endX, batteryHeight.toFloat(), paint)
    }

    /**
     * 绘制线条
     * @param canvas
     * @param paint
     */
    private fun drawLine(canvas: Canvas, paint: Paint) {
        for (i in 0..8) {
            if (i + 1 != limit * 10 / max) {
                val startX = (batteryWidth - 2 * OFFSET) / 10f * (i + 1) + OFFSET
                val endX = batteryWidth / 10f * (i + 1).toFloat()
                canvas.drawLine(startX, FLOAT_0, endX, batteryHeight.toFloat(), paint)
            }
        }
    }

    /**
     * 绘制背景
     * @param canvas
     * @param path
     * @param paint
     */
    private fun drawBackground(canvas: Canvas, path: Path, paint: Paint) {
        path.reset()
        path.moveTo((batteryWidth - OFFSET * 2) * progress / max + OFFSET, FLOAT_0)
        path.lineTo((batteryWidth * progress / max).toFloat(), batteryHeight.toFloat())
        path.lineTo(batteryWidth.toFloat(), batteryHeight.toFloat())
        path.lineTo(batteryWidth - OFFSET, FLOAT_0)
        path.lineTo((batteryWidth - OFFSET * 2) * progress / max + OFFSET, FLOAT_0)
        path.close()
        canvas.drawPath(path, paint)
    }

    //*************保存进度状态**********************
    override fun onSaveInstanceState(): Parcelable {
        val parcelable = super.onSaveInstanceState()
        val ss = SavedState(parcelable)
        ss.progress = progress
        return ss
    }

    override fun onRestoreInstanceState(state: Parcelable) {
        val ss = state as SavedState
        super.onRestoreInstanceState(ss.superState)
        progress = ss.progress
    }

    internal class SavedState : BaseSavedState {
        var progress = 0

        constructor(superState: Parcelable?) : super(superState)
        private constructor(save: Parcel) : super(save) {
            progress = save.readInt()
        }

        override fun writeToParcel(restore: Parcel, flags: Int) {
            super.writeToParcel(restore, flags)
            restore.writeValue(progress)
        }

        override fun describeContents(): Int {
            return 0
        }

        companion object CREATOR : Parcelable.Creator<SavedState> {
            override fun createFromParcel(parcel: Parcel): SavedState {
                return SavedState(parcel)
            }

            override fun newArray(size: Int): Array<SavedState?> {
                return arrayOfNulls(size)
            }
        }
    }
}
自定义属性
<declare-styleable name="ChargingProgressView">
        <!--进度-->
        <attr name="charging_progress" format="integer" />
        <!--最大进度-->
        <attr name="charging_max" format="integer" />
        <!--最大阈值-->
        <attr name="charging_limit" format="integer" />
        <!--进度条颜色-->
        <attr name="charging_progressColor" format="color|reference" />
        <!--灰色进度条开始颜色-->
        <attr name="charging_secondaryStartColor" format="color|reference" />
        <!--灰色进度条结束颜色-->
        <attr name="charging_secondaryEndColor" format="color|reference" />
        <!--背景颜色-->
        <attr name="charging_backgroundColor" format="color|reference" />
        <!--线条颜色-->
        <attr name="charging_lineColor" format="color|reference" />
        <!--线条粗细-->
        <attr name="charging_lineWidth" format="float|reference" />
        <!--进度文本颜色-->
        <attr name="charging_progressTextColor" format="color|reference" />
        <!--进度文本字体大小-->
        <attr name="charging_progressTextSize" format="float|reference" />
        <!--限制进度文本颜色-->
        <attr name="charging_limitTextColor" format="color|reference" />
        <!--进度文本字体大小-->
        <attr name="charging_limitTextSize" format="float|reference" />

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

推荐阅读更多精彩内容