ConstraintLayout动态添加View,改变约束

DEMO源码

使用的ConstraintLayout版本

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

如果不使用androidx的话可以使用下面的版本

implementation 'com.android.support.constraint:constraint-layout:1.1.3'

注意:使用不同的ConstraintLayout版本可能会有坑,如果在使用过程中发现实现不了想要添加的约束,可以尝试改变ConstraintLayout的版本如上所示。

1. 动态添加View

第一种情况:所有的View都是动态添加

举个例子

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/clRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/ivLeft"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_lake"
        app:layout_constraintDimensionRatio="h,16:9"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tvRight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="@string/lake_tahoe_title"
        android:textSize="30sp"
        app:layout_constraintLeft_toRightOf="@+id/ivLeft"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tvBottom"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="24dp"
        android:layout_marginEnd="8dp"
        android:text="@string/lake_discription"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ivLeft" />

</androidx.constraintlayout.widget.ConstraintLayout>

初始布局.jpg

上面的布局文件中呈现的效果如图所示,接下来我们用代码的方式动态添加View,实现上面的效果。

首先在res/values文件夹下新建一个ids.xml,在ids.xml中声明我们要添加的View的控件id。

ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="clRoot" type="id" />
    <item name="ivLeft" type="id" />
    <item name="tvRight" type="id" />
    <item name="tvBottom" type="id" />
</resources>

然后开始写代码

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    addViewUseLayoutParams()
}

使用ConstraintLayout.LayoutParams

    private fun addViewUseLayoutParams() {
        val constraintLayout = ConstraintLayout(this)
        constraintLayout.id = R.id.clRoot
        constraintLayout.layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
        //先设置根布局
        setContentView(constraintLayout)

        val ivLeft = ImageView(this)
        ivLeft.id = R.id.ivLeft
        ivLeft.scaleType = ImageView.ScaleType.CENTER_CROP
        ivLeft.setImageResource(R.drawable.ic_lake)

        val ivLeftLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            ScreenUtil.dpToPx(this, 100), 0
        )
        ivLeftLayoutParams.leftToLeft = R.id.clRoot
        ivLeftLayoutParams.marginStart = ScreenUtil.dpToPx(this, 8)
        ivLeftLayoutParams.topToTop = R.id.clRoot
        ivLeftLayoutParams.topMargin = ScreenUtil.dpToPx(this, 8)
        ivLeftLayoutParams.dimensionRatio = "h,16:9"

        ivLeft.layoutParams = ivLeftLayoutParams

        val tvRight = TextView(this)
        tvRight.id = R.id.tvRight
        tvRight.text = getString(R.string.lake_tahoe_title)
        tvRight.textSize = 30F
        val tvRightLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.WRAP_CONTENT,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        tvRightLayoutParams.startToEnd = R.id.ivLeft
        tvRightLayoutParams.topToTop = R.id.clRoot
        tvRightLayoutParams.marginStart = ScreenUtil.dpToPx(this, 16)
        tvRightLayoutParams.topMargin = ScreenUtil.dpToPx(this, 16)

        tvRight.layoutParams = tvRightLayoutParams


        val tvBottom = TextView(this)
        tvBottom.id = R.id.tvBottom
        tvBottom.text = getString(R.string.lake_discription)
        val tvBottomLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            0,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        tvBottomLayoutParams.startToStart = R.id.clRoot
        tvBottomLayoutParams.marginStart = ScreenUtil.dpToPx(this, 8)

        tvBottomLayoutParams.endToEnd = R.id.clRoot
        tvBottomLayoutParams.marginEnd = ScreenUtil.dpToPx(this, 8)

        tvBottomLayoutParams.topToBottom = R.id.ivLeft
        tvBottomLayoutParams.topMargin = ScreenUtil.dpToPx(this, 24)

        tvBottom.layoutParams = tvBottomLayoutParams

        constraintLayout.addView(ivLeft)
        constraintLayout.addView(tvRight)
        constraintLayout.addView(tvBottom)

    }

效果和上面是一样的,就不截图了。在上面的方法中,我们是使用ConstraintLayout.LayoutParams来实现添加view并指定约束的。接下来,我们换一种方式,使用ConstraintSet来添加view并指定约束。关于ConstraintSet的介绍请参考 ConstraintSet

使用ConstraintSet

    private fun addViewUseConstraintSet() {
        val constraintLayout = ConstraintLayout(this)
        constraintLayout.id = R.id.clRoot
        constraintLayout.layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
        //先设置根布局
        setContentView(constraintLayout)

        val constraintSet = ConstraintSet()

        val ivLeft = ImageView(this)
        ivLeft.id = R.id.ivLeft
        ivLeft.scaleType = ImageView.ScaleType.CENTER_CROP
        ivLeft.setImageResource(R.drawable.ic_lake)

        constraintSet.constrainWidth(R.id.ivLeft, ScreenUtil.dpToPx(this, 100))
        constraintSet.constrainHeight(R.id.ivLeft, 0)
        constraintSet.setDimensionRatio(R.id.ivLeft, "h,16:9")

        //layout_constraintTop_toTopOf
        constraintSet.connect(
            R.id.ivLeft, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP,
            ScreenUtil.dpToPx(this, 16)
        )

        constraintSet.connect(
            R.id.ivLeft, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START,
            ScreenUtil.dpToPx(this, 16)
        )

        val tvRight = TextView(this)
        tvRight.id = R.id.tvRight
        tvRight.text = getString(R.string.lake_tahoe_title)
        tvRight.textSize = 30F

        constraintSet.constrainHeight(R.id.tvRight, ConstraintLayout.LayoutParams.WRAP_CONTENT)
        constraintSet.constrainWidth(R.id.tvRight, ConstraintLayout.LayoutParams.WRAP_CONTENT)
        
        constraintSet.connect(
            R.id.tvRight, ConstraintSet.START, R.id.ivLeft, ConstraintSet.END,
            ScreenUtil.dpToPx(this, 16)
        )
        constraintSet.connect(
            R.id.tvRight, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP,
            ScreenUtil.dpToPx(this, 16)
        )

        val tvBottom = TextView(this)
        tvBottom.id = R.id.tvBottom
        tvBottom.text = getString(R.string.lake_discription)
        //设置高度
        constraintSet.constrainHeight(R.id.tvBottom, ConstraintLayout.LayoutParams.WRAP_CONTENT)

        constraintSet.connect(
            R.id.tvBottom, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START,
            ScreenUtil.dpToPx(this, 8)
        )

        constraintSet.connect(
            R.id.tvBottom, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END,
            ScreenUtil.dpToPx(this, 8)
        )

        constraintSet.connect(
            R.id.tvBottom, ConstraintSet.TOP, R.id.ivLeft, ConstraintSet.BOTTOM,
            ScreenUtil.dpToPx(this, 24)
        )

        constraintLayout.addView(ivLeft)
        constraintLayout.addView(tvRight)
        constraintLayout.addView(tvBottom)

        TransitionManager.beginDelayedTransition(constraintLayout)

        constraintSet.applyTo(constraintLayout)
    }

效果也是一样的。

第二种情况,动态添加个别View,感觉这种场景应该不多

在上面的例子中,我们假设tvBottom已经在布局中了。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/clRoot"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvBottom"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="24dp"
        android:layout_marginEnd="8dp"
        android:text="@string/lake_discription"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

31573289245_.pic.jpg

接下来,我们动态的把ivLefttvRight 添加到布局中去,实现和第一个例子中同样的效果。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //布局文件别忘了
        setContentView(R.layout.activity_main)
        addPartView()
    }
    private fun addPartView() {
        val ivLeft = ImageView(this)
        ivLeft.id = R.id.ivLeft
        ivLeft.scaleType = ImageView.ScaleType.CENTER_CROP
        ivLeft.setImageResource(R.drawable.ic_lake)

        val ivLeftLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            ScreenUtil.dpToPx(this, 100), 0
        )
        ivLeftLayoutParams.dimensionRatio = "h,16:9"
        ivLeftLayoutParams.topMargin = ScreenUtil.dpToPx(this, 16)
        ivLeftLayoutParams.marginStart = ScreenUtil.dpToPx(this, 16)
        ivLeftLayoutParams.leftToLeft = R.id.clRoot
        ivLeftLayoutParams.topToTop = R.id.clRoot

        ivLeft.layoutParams = ivLeftLayoutParams

        val tvRight = TextView(this)
        tvRight.id = R.id.tvRight
        tvRight.text = getString(R.string.lake_tahoe_title)
        tvRight.textSize = 30F
        val tvRightLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.WRAP_CONTENT,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        tvRightLayoutParams.startToEnd = R.id.ivLeft
        tvRightLayoutParams.topToTop = R.id.clRoot
        tvRightLayoutParams.marginStart = ScreenUtil.dpToPx(this, 16)
        tvRightLayoutParams.topMargin = ScreenUtil.dpToPx(this, 16)

        tvRight.layoutParams = tvRightLayoutParams


        val tvBottomLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            0,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        tvBottomLayoutParams.startToStart = R.id.clRoot
        tvBottomLayoutParams.marginStart = ScreenUtil.dpToPx(this, 8)

        tvBottomLayoutParams.endToEnd = R.id.clRoot
        tvBottomLayoutParams.marginEnd = ScreenUtil.dpToPx(this, 8)

        tvBottomLayoutParams.topToBottom = R.id.ivLeft
        tvBottomLayoutParams.topMargin = ScreenUtil.dpToPx(this, 24)

        tvBottomLayoutParams.bottomMargin = ScreenUtil.dpToPx(this, 8)

        //重新为布局中已经存在的tvBottom设置新的布局参数。
        tvBottom.layoutParams = tvBottomLayoutParams

        clRoot.addView(ivLeft)
        clRoot.addView(tvRight)

    }

这种方式要注意重新为布局中已经存在的控件设置新的布局参数。

动态改变约束

如果我们想动态改变布局中的View的约束该怎么做呢?比如我们想把上面的布局样式改成下图所示。


Screenshot_1559374001.png

其实,在上面我们已经给tvBottom动态改变约束了,就是给View重新设置布局参数就好了。

给View重新设置布局参数

下面我们在代码中,重新改变View的布局参数。

    private fun changeLayoutParams() {
        val ivLeftLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            0, 0
        )
        ivLeftLayoutParams.leftMargin = ScreenUtil.dpToPx(this, 16)
        ivLeftLayoutParams.rightMargin = ScreenUtil.dpToPx(this, 16)
        ivLeftLayoutParams.topMargin = ScreenUtil.dpToPx(this, 16)
        ivLeftLayoutParams.leftToLeft = R.id.clRoot
        ivLeftLayoutParams.rightToRight = R.id.clRoot
        ivLeftLayoutParams.dimensionRatio = "h,16:9"
        ivLeftLayoutParams.topToTop = R.id.clRoot

        //修改布局参数
        ivLeft.layoutParams = ivLeftLayoutParams

        val tvRightLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.WRAP_CONTENT,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        tvRightLayoutParams.leftToLeft = R.id.clRoot
        tvRightLayoutParams.topToBottom = R.id.ivLeft
        tvRightLayoutParams.marginStart = ScreenUtil.dpToPx(this, 16)
        tvRightLayoutParams.topMargin = ScreenUtil.dpToPx(this, 16)

        tvRight.layoutParams = tvRightLayoutParams


        val tvBottomLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
            0,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
        )
        tvBottomLayoutParams.startToStart = R.id.clRoot
        tvBottomLayoutParams.marginStart = ScreenUtil.dpToPx(this, 8)

        tvBottomLayoutParams.endToEnd = R.id.clRoot
        tvBottomLayoutParams.marginEnd = ScreenUtil.dpToPx(this, 8)

        tvBottomLayoutParams.topToBottom = R.id.tvRight
        tvBottomLayoutParams.topMargin = ScreenUtil.dpToPx(this, 24)

        tvBottomLayoutParams.bottomMargin = ScreenUtil.dpToPx(this, 8)

        tvBottom.layoutParams = tvBottomLayoutParams
    }

改变后的效果就不贴了。

使用ConstraintSet 动态修改约束

使用ConstraintSet 动态修改约束分四步。

  1. 首先要声明一下ConstraintSet对象
  val constraintSet = ConstraintSet()
  1. 复制一份现有的约束关系,这一步不是必须的。
//从一个constraintLayout中复制约束
set.clone(constraintLayout: ConstraintLayout);
//从一个ConstraintSet中复制约束
set.clone(set: ConstraintSet);
//从一个布局文件中复制约束
set.clone(context: Context, constraintLayoutId: Int);

如果说你要改变布局中某些控件的约束,但是还要保存其他控件的约束关系,那么你就需要从已有的根布局中复制一份约束,然后只更改哪些需要改变的控件的约束关系。

注意复制约束关系的时候,布局中的每个控件必都有id,不然会报下面的错误。

 java.lang.RuntimeException: All children of ConstraintLayout must have ids to use ConstraintSet

  1. 设置控件之间的约束
  2. 应用新的约束。在应用约束的时候,为了让约束改变的时候不是那么突兀,我们可以设置一个动画,来让约束改变平滑一点。
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic_add_view)
      
        ivLeft.setOnClickListener {
            changeConstraintSet()
        }
    }
    private fun changeConstraintSet() {
        val constraintSet = ConstraintSet()
        //从根布局中克隆约束参数
        constraintSet.clone(clRoot)

        //清空控件原有的约束
        constraintSet.clear(R.id.ivLeft)
        constraintSet.clear(R.id.tvRight)
        constraintSet.clear(R.id.tvBottom)

        constraintSet.constrainWidth(R.id.ivLeft, 0)
        constraintSet.constrainHeight(R.id.ivLeft, 0)
        //设置ivLeft顶部和父布局顶部对齐
        constraintSet.connect(
            R.id.ivLeft, ConstraintSet.TOP, R.id.clRoot, ConstraintSet.TOP,
            ScreenUtil.dpToPx(this, 16)
        )
        constraintSet.connect(
            R.id.ivLeft, ConstraintSet.START, R.id.clRoot, ConstraintSet.START,
            ScreenUtil.dpToPx(this, 16)
        )
        constraintSet.connect(
            R.id.ivLeft, ConstraintSet.END, R.id.clRoot, ConstraintSet.END,
            ScreenUtil.dpToPx(this, 16)
        )
        //设置宽高比
        constraintSet.setDimensionRatio(R.id.ivLeft, "h,16:9")


        constraintSet.constrainWidth(R.id.tvRight, ConstraintLayout.LayoutParams.WRAP_CONTENT)
        constraintSet.constrainHeight(R.id.tvRight, ConstraintLayout.LayoutParams.WRAP_CONTENT)

        constraintSet.connect(
            R.id.tvRight, ConstraintSet.TOP, R.id.ivLeft, ConstraintSet.BOTTOM,
            ScreenUtil.dpToPx(this, 24)
        )

        constraintSet.connect(
            R.id.tvRight, ConstraintSet.START, R.id.clRoot, ConstraintSet.START,
            ScreenUtil.dpToPx(this, 8)
        )

        constraintSet.constrainHeight(R.id.tvBottom, ConstraintLayout.LayoutParams.WRAP_CONTENT)

        constraintSet.connect(
            R.id.tvBottom, ConstraintSet.START, R.id.clRoot, ConstraintSet.START,
            ScreenUtil.dpToPx(this, 8)
        )
        constraintSet.connect(
            R.id.tvBottom, ConstraintSet.END, R.id.clRoot, ConstraintSet.END,
            ScreenUtil.dpToPx(this, 8)
        )

        constraintSet.connect(
            R.id.tvBottom, ConstraintSet.TOP, R.id.tvRight, ConstraintSet.BOTTOM,
            ScreenUtil.dpToPx(this, 24)
        )

        constraintSet.applyTo(clRoot)
        //设置一个动画效果,让约束改变平滑一点,这一步不是必须的
        TransitionManager.beginDelayedTransition(clRoot)
    }

效果如下所示

1559465809770862.gif

遇到的一个问题

在测试的时候,我想添加一个水平方向上的Guideline,让它在父布局竖直方向比例为0.4的地方,然后在Guideline之上添加一个ImageView。代码如下

    private fun addGuideLine() {
        val constraintLayout = ConstraintLayout(this)
        constraintLayout.id = R.id.clRoot
        constraintLayout.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT)
        //先设置根布局
        setContentView(constraintLayout)

        val guideline = Guideline(this)
        guideline.id = R.id.guideline

        val guideLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
                ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT)
        guideLayoutParams.guidePercent = 0.4f
        guideLayoutParams.topToTop = R.id.clRoot
        guideLayoutParams.bottomToBottom = R.id.clRoot
        //注意
        guideLayoutParams.orientation = ConstraintLayout.LayoutParams.VERTICAL

        guideline.layoutParams = guideLayoutParams

        constraintLayout.addView(guideline)

        val ivLeft = ImageView(this)
        ivLeft.id = R.id.ivLeft
        ivLeft.scaleType = ImageView.ScaleType.CENTER_CROP
        ivLeft.setImageResource(R.drawable.lake)

        val ivLeftLayoutParams: ConstraintLayout.LayoutParams = ConstraintLayout.LayoutParams(
                0, 0)
        ivLeftLayoutParams.dimensionRatio = "h,16:9"
        ivLeftLayoutParams.bottomToTop = R.id.guideline
        ivLeftLayoutParams.startToStart = R.id.clRoot
        ivLeftLayoutParams.endToEnd = R.id.clRoot

        ivLeft.layoutParams = ivLeftLayoutParams

        constraintLayout.addView(ivLeft)
    }

在测试的时候报了一个错误

java.lang.AssertionError: TOP at android.support.constraint.solver.widgets.Guideline.getAnchor(Guideline.java:159)


折腾了半天,发现是Guideline的方向写错了。

 guideLayoutParams.orientation = ConstraintLayout.LayoutParams.VERTICAL

正确的写法

 guideLayoutParams.orientation = ConstraintLayout.LayoutParams.HORIZONTAL

如果Guideline的方向写错了,会导致依赖Guideline的方向的控件的约束无法正确指定,所以会报错。如果遇到类似的问题请仔细检查,是否正确的设置了约束。

参考链接

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

推荐阅读更多精彩内容