「译」MotionLayout介绍 (part III)

原文链接

前言

在本系列的第一部分和第二部分中,我们介绍了 MotionLayout,并给出了各种示例:

  • basic motion
  • swipe handling
  • custom attribute interpolation
  • keyframes

第一部分和第二部分概述了 MotionLayout 中引入的基本概念。

现在将介绍如何在现有应用程序中使用 MotionLayout,并将其集成到现有布局(如 CoordinatorLayout,DrawerLayout或ViewPager)中。

配合 CoordinatorLayout 使用

(注意,MotionLayout 可以用来实现类似于 CoordinatorLayout的行为。我们将在下一篇文章中展示这些例子)

利用 MotionLayout 的一个简单方法让屏幕中的部分内容做指定动画。通过这种方式,你可以向应用程序中的现有布局添加更多有趣的运动,而不必从头开始。

例如,你想要的效果可能如下:

with-coordinatorlayout-1

这里的基本思路是用 MotionLayout 替换 AppBarLayout 中的 Toolbar 元素。然后让 CoordinatorLayout 驱动动画进度。

由于你可以通过调用setProgress()来控制 MotionLayout 的过渡进度,因此我们可以创建一个简单的子类,通过监听 AppBarLayout 偏移量来跟踪改变:

package com.google.androidstudio.motionlayoutexample.utils

import android.content.Context
import android.support.constraint.motion.MotionLayout
import android.support.design.widget.AppBarLayout
import android.util.AttributeSet

class CollapsibleToolbar @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr), AppBarLayout.OnOffsetChangedListener {

    override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) {
        progress = -verticalOffset / appBarLayout?.totalScrollRange?.toFloat()!!
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        (parent as? AppBarLayout)?.addOnOffsetChangedListener(this)
    }
}

然后用这个子类代替 CoordinatorLayout XML 文件中的 Toolbar。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="false"
    android:background="@color/contentBackground">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:theme="@style/AppTheme.AppBarOverlay">

        <include layout="@layout/motion_09_coordinatorlayout_header"/>

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_scrolling" />

</android.support.design.widget.CoordinatorLayout>

接下来唯一要做的就是创建一个包含了我们想要动画的控件的 MotionLayout 文件。这里我们有一个 作为背景的 ImageView 和一个 TextView:

<?xml version="1.0" encoding="utf-8"?>
<com.google.androidstudio.motionlayoutexample.utils.CollapsibleToolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/motionLayout"
    app:layoutDescription="@xml/scene_09"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minHeight="50dp"
    android:fitsSystemWindows="false"
    app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed">

    <ImageView
        android:id="@+id/background"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="@color/colorAccent"
        android:scaleType="centerCrop"
        android:src="@drawable/monterey"/>

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:transformPivotX="0dp"
        android:transformPivotY="0dp"
        android:text="Monterey"
        android:textColor="#FFF"
        android:textSize="32dp" />

</com.google.androidstudio.motionlayoutexample.utils.CollapsibleToolbar>

最后在 MotionScene 中定义它的动画效果:

<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/end" />

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/background"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:alpha="1.0"
            motion:layout_constraintBottom_toBottomOf="parent"/>
        <Constraint
            android:id="@+id/label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:rotation="-90.0"
            motion:layout_constraintBottom_toBottomOf="@+id/background"
            motion:layout_constraintStart_toStartOf="parent"/>
    </ConstraintSet>
    
    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/background"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:alpha="0.2"
            motion:layout_constraintBottom_toBottomOf="parent"/>
        <Constraint
            android:id="@+id/label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginBottom="8dp"
            android:rotation="0.0"
            motion:layout_constraintBottom_toBottomOf="@+id/background"
            motion:layout_constraintStart_toStartOf="parent" />
    </ConstraintSet>
</MotionScene>

配合 DrawerLayout 使用

DrawerLayout 是另一个 Android framework 中的类,用于打开一个侧边栏。

相比通常的菜单,我们可能希望来一些更有趣的东西:

with_drawerlayout-1

和把 MotionLayout 整合到 CoordinatorLayout 中类似,我们需要创建一个设置 MotionLayout 进度的子类:

package com.google.androidstudio.motionlayoutexample.utils

import android.content.Context
import android.support.constraint.motion.MotionLayout
import android.support.v4.widget.DrawerLayout
import android.util.AttributeSet
import android.view.View

class DrawerContent @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr), DrawerLayout.DrawerListener {
    override fun onDrawerStateChanged(newState: Int) {
    }

    override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
        progress = slideOffset
    }

    override fun onDrawerClosed(drawerView: View) {
    }

    override fun onDrawerOpened(drawerView: View) {
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        (parent as? DrawerLayout)?.addDrawerListener(this)
    }
}

这个子类将通过onDrawerSlide() 回调来跟踪过渡进度。

使用这个子类,我们可以很容易地在DrawerLayout中集成MotionLayout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/motionLayout"
    android:background="@color/colorPrimaryDark">

    <include layout="@layout/motion_12_drawerlayout_content"/>

    <include layout="@layout/motion_13_drawerlayout_menu"/>

</android.support.v4.widget.DrawerLayout>

这个 xml 文件像我们前面 CoordinatorLayout 例子一样,包含了几个简单控件。

下面是一个使用了 MotionLayout 的菜单文件 (menu file):

<?xml version="1.0" encoding="utf-8"?>
<com.google.androidstudio.motionlayoutexample.utils.DrawerContent
    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/menu"
    android:layout_width="180dp"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:layoutDescription="@xml/scene_13_menu"
    android:background="@color/colorPrimaryDark">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="Monterey"
        android:textSize="20sp"
        android:textStyle="italic"
        android:typeface="serif"
        android:textColor="#FFF"
        app:layout_constraintBottom_toTopOf="@+id/textView3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0"
        app:layout_constraintVertical_chainStyle="packed" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Information"
        app:fontFamily="sans-serif-smallcaps"
        android:textColor="#FFF"
        app:layout_constraintBottom_toTopOf="@+id/textView4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view" />

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Directions"
        app:fontFamily="sans-serif-smallcaps"
        android:textColor="#FFF"
        app:layout_constraintBottom_toTopOf="@+id/textView5"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />

    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Sight-Seeing"
        app:fontFamily="sans-serif-smallcaps"
        android:textColor="#FFF"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView4" />

    <View
        android:id="@+id/view"
        android:background="#c2c1c1"
        android:layout_width="100dp"
        android:layout_height="1dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toTopOf="@+id/textView2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView3" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="California"
        android:textColor="#FFF"
        app:fontFamily="cursive"
        app:layout_constraintBottom_toTopOf="@+id/view"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />
</com.google.androidstudio.motionlayoutexample.utils.DrawerContent>

MotionScene 文件只是旋转不同的元素 (检查 rotation 属性)

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@+id/start"
        motion:duration="250" />

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            android:rotation="90"
            android:translationX="100dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView3"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintVertical_chainStyle="spread" />

        <Constraint
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:rotation="90"
            android:translationX="100dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView4"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/view" />

        <Constraint
            android:id="@+id/textView4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:rotation="90"
            android:translationX="100dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView5"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView2" />

        <Constraint
            android:id="@+id/textView5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:rotation="90"
            android:translationX="100dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView4" />

        <Constraint
            android:id="@+id/view"
            android:layout_width="100dp"
            android:layout_height="1dp"
            android:layout_marginTop="16dp"
            android:rotation="90"
            android:translationX="100dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView2"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView3" />

        <Constraint
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:rotation="90"
            android:translationX="100dp"
            motion:layout_constraintBottom_toTopOf="@+id/view"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView3"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintVertical_bias="0.0"
            motion:layout_constraintVertical_chainStyle="packed" />

        <Constraint
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView4"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/view" />

        <Constraint
            android:id="@+id/textView4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView5"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView2" />

        <Constraint
            android:id="@+id/textView5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView4" />

        <Constraint
            android:id="@+id/view"
            android:layout_width="100dp"
            android:layout_height="1dp"
            android:layout_marginTop="16dp"
            motion:layout_constraintBottom_toTopOf="@+id/textView2"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView3" />

        <Constraint
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toTopOf="@+id/view"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toBottomOf="@+id/textView" />
    </ConstraintSet>

在 ViewPager中使用

类似的,我们可能希望有一个有趣的 ViewPager 。

with-viewpag-1

我们也可以使用类似的技巧来集成 ViewPager 。创建一个子类来传递当前的位置。

package com.google.androidstudio.motionlayoutexample.utils

import android.content.Context
import android.support.constraint.motion.MotionLayout
import android.support.v4.view.ViewPager
import android.util.AttributeSet

class ViewpagerHeader @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr), ViewPager.OnPageChangeListener {

    override fun onPageScrollStateChanged(state: Int) {
    }

    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        var numPages = 3
        progress = (position + positionOffset) / (numPages - 1)
    }

    override fun onPageSelected(position: Int) {
    }
}

计算非常简单——onPageScrolled()给我们提供了页面的位置索引(我们有三个页面的时候,这里是0,1或2),偏移量(对应偏移位置从0到1)。动画的进度更新我们可以通过下面的公式来得到:

progress = (position + positionOffset) / (numPages-1)

配合 Lottie 使用

前面的例子用了简单的图片来作为头部。你也可以 Lottie集成到你的 MotionLayout中,然后直接设置它的进度从而播放它。

让我们把上一个例子改成 LottieAnimationView:

with-lottie-1

简单起见,我们把基于 MotionLayout 的 ViewPager Header 改成只包含一个 LottieAnimationView 的样式:

<?xml version="1.0" encoding="utf-8"?>
<com.google.androidstudio.motionlayoutexample.utils.ViewpagerHeader xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/motionLayout"
    app:layoutDescription="@xml/scene_23"
    android:layout_width="match_parent"
    app:progress="0"
    android:layout_height="230dp">

    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/animation_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:lottie_rawRes="@raw/walkthrough"/>

</com.google.androidstudio.motionlayoutexample.utils.ViewpagerHeader>

在 MotionScene 中关键的修改是使用motion:progress属性:

<Constraint
    android:id="@+id/animation_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    motion:progress="0"/>

由于 LottieAnimationView 有一个setProgress() 函数,这将导致 MotionLayout 通过它直接播放 Lottie 的对应进度。

完整的 MotionScene 文件如下:

<?xml version="1.0" encoding="utf-8"?>
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/end">
    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/animation_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            motion:progress="0"/>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/animation_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            motion:progress="1"/>
    </ConstraintSet>
    
</MotionScene>

总结

本篇介绍了如何在现有布局中轻松集成 MotionLayout。

你可以在 ConstraintLayout examples github repository找到这些例子的源码。

本系列文章还有更多内容:

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