Android动画 1:视图动画(Tween Animation)、属性动画

1、视图动画Animation (Tween Animation)

通过不断的对view对象做图像变换(渐变、平移、缩放、旋转)而产生动画效果,这种动画只适用于View对象。
1、AlphaAnimation

AlphaAnimation(float fromAlpha, float toAlpha)

其中,1:不透明;0:透明

2、RotateAnimation

RotateAnimation(float fromDegrees, float toDegrees)
---
RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)
---
RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

注意:

  • 旋转的中心点,默认是view的左上角;
  • pivotX、pivotY,是旋转的中心点的偏移量;
  • pivotXType的取值可以是RELATIVE_TO_PARENT、RELATIVE_TO_SELF,代表的是中心点(默认是view的左上角)偏移量大小的取值是相对于自身还是相对于parent,最终都是相对于view的左上角进行偏移。
RotateAnimation(0,360,300,300)
RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_PARENT,0.5f,RotateAnimation.RELATIVE_TO_PARENT,0.5f)

即旋转中心点从view的左上角,偏移到了右下角(相对于parent50%的地方)

RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f)

3、TranslateAnimation

TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
---
TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
        int fromYType, float fromYValue, int toYType, float toYValue) 

4、ScaleAnimation

ScaleAnimation(float fromX, float toX, float fromY, float toY)
---
ScaleAnimation(float fromX, float toX, float fromY, float toY, 
       float pivotX, float pivotY) 
---
ScaleAnimation(float fromX, float toX, float fromY, float toY,
        int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

相对于某个中心点缩放,和RotateAnimation类似

ScaleAnimation(1,0,1,0,ScaleAnimation.RELATIVE_TO_PARENT,0.2f,ScaleAnimation.RELATIVE_TO_PARENT,0.2f)

代码如下:

    //透明动画效果
    AlphaAnimation alphaAnimation=new AlphaAnimation(0,1);
    alphaAnimation.setDuration(1000);
    view.startAnimation(alphaAnimation);

---

    //旋转动画效果
    RotateAnimation rotateAnimation=new RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_PARENT,0.1f,
                        RotateAnimation.RELATIVE_TO_PARENT,0.1f);
    rotateAnimation.setDuration(1000);
    v.startAnimation(rotateAnimation);

---

    //平移动画效果
    TranslateAnimation translateAnimation=new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,0,
            TranslateAnimation.RELATIVE_TO_PARENT,0.2f,
            TranslateAnimation.RELATIVE_TO_PARENT,0,
            TranslateAnimation.RELATIVE_TO_PARENT,0.2f);
    translateAnimation.setDuration(1000);
    v.startAnimation(translateAnimation);

---

    //缩放动画效果
    ScaleAnimation scaleAnimation=new ScaleAnimation(1,0,1,0,ScaleAnimation.RELATIVE_TO_PARENT,0.2f,
                    ScaleAnimation.RELATIVE_TO_PARENT,0.2f);
    scaleAnimation.setDuration(2000);
    v.startAnimation(scaleAnimation);   
---

 v.startAnimation(AnimationUtils.loadAnimation(this,R.anim.rotate));

    //透明动画效果
    <alpha android:duration="1000"
        android:fromAlpha="1"
        android:toAlpha="0"/>

--- 

    //旋转动画效果
    <rotate android:duration="1000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:pivotX="50%"
        android:pivotY="50%"/>

---

    //平移动画效果
    <translate android:duration="1000"
        android:fromXDelta="0"
        android:toXDelta="20%p"
        android:fromYDelta="0"
        android:toYDelta="30%p"/>

注意:

  • num%、num%p表示相对于自身或者父控件,跟RELATIVE_TO_PARENT、RELATIVE_TO_SELF效果类似;如果以浮点数字表示,是一个绝对值,代表相对自身原始位置的像素值;
  • RepeatMode:
    Animation.RESTART,表示动画重新从头开始执行,当一次动画执行结束之后,图片将重新从头开始执行。
    Animation.REVERSE,表示动画反方向执行,当一次动画执行结束之后,图片将向反方向运动。
  • RepeatCount:动画的重复次数,Animation.INFINITE 无限循环
  • fillBefore:动画结束时画面停留在此动画的第一帧
    fillAfter:动画结束是画面停留在此动画的最后一帧
  • 加载xml文件中的动画
    AnimationUtils类调用loadAnimation(context, id)方法来加载xml文件

5、可以通过AnimationSet,将动画以组合的形式展示

        AnimationSet animationSet=new AnimationSet(true);
        animationSet.setDuration(2000);

        ScaleAnimation scaleAnimation=new ScaleAnimation(1,0,1,0,ScaleAnimation.RELATIVE_TO_PARENT,0.2f,
              ScaleAnimation.RELATIVE_TO_PARENT,0.2f);
        scaleAnimation.setDuration(2000);
        animationSet.addAnimation(scaleAnimation);

        ...

        v.startAnimation(animationSet);
AnimationSet

6、自定义动画
实现Animation 接口的applyTransformation()方法,还可以覆盖initialize()方法,做一些初始化的操作

public class ReversalAnimation extends Animation {
    private int height;
    private int width;

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        setDuration(1000);
        setInterpolator(new CycleInterpolator(0.25f));
        this.height = height * 2;
        this.width = width;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        Matrix matrix = t.getMatrix();
        matrix.preScale(1 - (float) Math.sin((interpolatedTime) * Math.PI), 1, width / 2, 0);
        matrix.preTranslate(0, -(float) Math.sin(interpolatedTime * Math.PI) * height);
    }
}

其中interpolatedTime的取值为0~1,0标识动画开始执行,1标识动画执行完了
Transformation,可以通过它获得当前矩阵对象matrix,通过matrix将动画效果执行出来

自定义动画

7、插值器
如下

8、监听器
可以设置AnimationListener,获取动画开始、结束、重复事件

    translateAnimation.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
            Toast.makeText(Main2Activity.this,"onAnimationStart",Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            Toast.makeText(Main2Activity.this,"onAnimationEnd",Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });

9、执行动画
通过view的startAnimation

但是视图动画不具备交互性,在缩放和平移一个View的时候,并没有改变控件内部的属性值,它的有效点击区域,依然保持原来的大小和位置,如图在动画的结束位置点击,是没有效果的,只有在原始位置(矩形区域),点击才有效果

视图动画

参考:Android 动画学习 View Animation

2、属性动画Animator

在Android3.0(level 11)引入的,通过动态地改变对象的属性值而达到动画效果,可以为任何的对象添加动画(当然也包括View在内)。
属性动画改变了View的属性,比如对于View的最终位置x=left+translationX,所以在最终位置就可以响应点击事件;

Property Animation的工作流程

1、ValueAnimator

ValueAnimator本身不提供任何的动画效果,它可以产生有一定规律的数字,可以在AnimatorUpdateListener中监听这些数字的变化,从而完成动画效果

ValueAnimator动画的整个过程

ValueAnimator动画的整个过程如下:
(0)、 ofInt(0,400)表示指定动画的数字区间,是从0运动到400;
(1)、 在动画过程中, Animator会根据动画总时间和已进行的时间自动计算出一个时间比例因子,大小介于0和1之间,0表示开始。

时间比例因子 = 当前已进行的时间/动画执行的总时间

(2)、Interpolator(差值器/加速器):Animator会根据时间比例因子,返回当前动画进度所对应的数字进度:这个数字进度是百分制的,以小数表示。

publicinterfaceTimeInterpolator{
floatgetInterpolation(floatinput);
}

但是我们通过监听器拿到的是当前动画所对应的具体数值,而不是百分制的进度。那么就必须有一个地方会根据当前的数字进度,将其转化为对应的数值,这个地方就是Evaluator;
(3)、 Evaluator:Evaluator将从加速器返回的数字进度转成对应的数字值。

publicinterfaceTypeEvaluator<T>{
T  evaluate(floatfraction,T startValue,T endValue)
}

(4)、 监听器:通过在AnimatorUpdateListener监听器使用animation.getAnimatedValue()函数拿到Evaluator中返回的数字值,为需要执行动画的对象设置对应属性的属性值。onAnimationUpdate()在动画每播放一帧时都会调用。

动画在的整个过程中,会根据我们当初设置的TimeInterpolator 和TypeEvaluator的计算方式计算出的不同的属性值,从而不断地改变对象属性值的大小,进而产生各式各样的动画效果。

创建ValueAnimator的常用方法如下:

  • ValueAnimator ofInt(int... values)
  • ValueAnimator ofArgb(int... values)
  • ValueAnimator ofFloat(float... values)
  • ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)

这些方法都有默认的加速器和Evaluator,不指定则使用默认的;
其中ofInt()的默认Evaluator是IntEvaluator,ofFloat是FloatEvalutor,ofArgb是ArgbEvaluator;
参数的类型是定值

代码如下:

    ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,1);
    valueAnimator.setTarget(v);
    valueAnimator.setDuration(1000);
    valueAnimator.start();
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float timeAnim= (float) animation.getAnimatedValue();
            if(timeAnim<0.4f){
                imageView.setTranslationY(-1000*timeAnim);
            }else if(timeAnim>=0.4f && timeAnim<=0.8f){
                imageView.setScaleX((float) Math.abs((timeAnim-0.6)/0.2));
            }else {
                imageView.setTranslationY(-1000*(1-timeAnim));
            }
        }
    });
属性动画4.gif
  • ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
    ofInt()只能传入Integer类型的值,而ofFloat()只能传入Float类型的值。如果我们需要操作其它类型的变量,可以使用以上方法

TypeEvaluator

public class PointXYEvaluator implements TypeEvaluator<PointXY> {
    @Override
    public PointXY evaluate(float fraction, PointXY startValue, PointXY endValue) {
        int pointX= (int) (startValue.getPointX()+(endValue.getPointX()-startValue.getPointX())*fraction);
        int pointy= (int) (startValue.getPointY()+(endValue.getPointY()-startValue.getPointY())*fraction);
        return new PointXY(pointX,pointy);
    }
}

自定义参数类型

public class PointXY {
    private int pointX;
    private int pointY;

    public PointXY(int pointX, int pointY) {
        this.pointX = pointX;
        this.pointY = pointY;
    }

    public int getPointX() {
        return pointX;
    }

    public void setPointX(int pointX) {
        this.pointX = pointX;
    }

    public int getPointY() {
        return pointY;
    }

    public void setPointY(int pointY) {
        this.pointY = pointY;
    }
}

使用

ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointXYEvaluator(),
            new PointXY(pointXY.getLeft(),pointXY.getTop()),new PointXY(pointXY.getLeft()+200,pointXY.getTop()+200));
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            PointXY pointValue= (PointXY) animation.getAnimatedValue();
            pointXY.setTranslationX(pointValue.getPointX());
            pointXY.setTranslationY(pointValue.getPointY());
        }
    });

    valueAnimator.start();
ValueAnimator ofObject 定义TypeEvaluator

参考:
自定义控件三部曲之动画篇(六)——ValueAnimator高级进阶(二)
Android动画学习(三)之使用ValueAnimator和ObjectAnimator实现动画实例

2、 ObjectAnimator

ObjectAnimator动画原理

在ObjectAnimator中,则是先根据属性值拼装成对应的set函数的名字,比如这里的scaleY的拼装方法就是将属性的第一个字母强制大写后,与set拼接,所以就是setScaleY。然后通过反射找到对应控件的setScaleY(float scaleY)函数,将当前数字值做为setScaleY(float scale)的参数将其传入。

常用的构造函数有:

  • ObjectAnimator ofFloat(Object target, String propertyName, float... values)
    其中,target指定这个动画要操作的是哪个控件
    propertyName指定这个动画要操作这个控件的哪个属性
    values是可变长参数,就是指这个属性值是从哪变到哪
  • ObjectAnimator ofFloat(Object target, String xPropertyName, String yPropertyName,Path path)
  • ObjectAnimator ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)

代码如下:

ObjectAnimator translationAnimator=ObjectAnimator.ofFloat(view,"translationY",0,-400).setDuration(400);
translationAnimator.start();

---

Path translationPath=new Path();
translationPath.moveTo(0,0);
translationPath.lineTo(200, 300);
ObjectAnimator translationAnimator=ObjectAnimator.ofFloat(v,"translationX","translationY",translationPath);
translationAnimator.start();

也可以在xml中进行配置

    <objectAnimator android:duration="400"
        android:propertyName="translationY"
        android:valueFrom="0dp"
        android:valueTo="-200dp"
        android:valueType="floatType"/>

---

    Animator animator= AnimatorInflater.loadAnimator(Main2Activity.this,R.animator.object_animator);
    animator.setTarget(v);
    animator.start();

1、View属性动画默认的属性值:translationX、translationY、x 、 y(view对象在它容器中的最终位置)、rotation、rotationX、rotationY、scaleX、scaleY、pivotX、pivotY(旋转和缩放都是以此为中心展开的,缺省值是 View 对象的中心点)、alpha...
2、如果在调用 ObjectAnimator 的某个工厂方法时,我们只为 values... 参数指定了一个值,那此值将被认定为动画属性的结束值。 这样的话,动画显示的属性必须带有一个 getter 方法,用于获取动画的起始值。
3、还可以自定义属性值,但要在操作的控件中,实现对应的属性的set方法

  • ObjectAnimator ofObject(T target, @NonNull Property<T, V> property, @Nullable TypeConverter<PointF, V> converter, Path path)
  • <T> ObjectAnimator ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path)
  • <T, V> ObjectAnimator ofObject(T target, @NonNull Property<T, V> property, @Nullable TypeConverter<PointF, V> converter, Path path)
    其中Property<T, V>是一个属性包装器,如下


    Property<View, Float> TRANSLATION_X

自定义Property,代码如下:

public class ImgMoveView extends View {
    private static final String PROPERTY_NAME="position";
    public ImgMoveView(Context context) {
        super(context);
    }
    public ImgMoveView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setPosition(PointF position){
        int x = Math.round(position.x);
        int y = Math.round(position.y);
        setTranslationX(x);
        setTranslationY(y);
    }
    public PointF getPosition(){
        return new PointF(getX(),getY());
    }
    /**
     * A Property wrapper around the position functionality handled by the
     * ImgMoveView#setPosition(PointF) and ImgMoveView#getPosition() methods.
     */
    public static final Property<ImgMoveView, PointF> POSITION = new Property<ImgMoveView, PointF>(PointF.class,PROPERTY_NAME) {
        @Override
        public PointF get(ImgMoveView object) {
            return object.getPosition();
        }
        @Override
        public void set(ImgMoveView object, PointF value) {
            object.setPosition(value);
        }
    };
}

调用:

    /*ObjectAnimator pathMoveAnim=ObjectAnimator.ofFloat(imgMove, "translationX", "translationY", path);*/
    //ObjectAnimator pathMoveAnim=ObjectAnimator.ofFloat(imgMove, View.TRANSLATION_X, View.TRANSLATION_Y, path);
    ObjectAnimator pathMoveAnim=ObjectAnimator.ofObject(imgMove, ImgMoveView.POSITION,
            null, path);
使用Property

参考:自定义控件三部曲之动画篇(七)——ObjectAnimator基本使用自定义控件三部曲之动画篇(八)——PropertyValuesHolder与Keyframe

3、监听器
属性动画提供了AnimatorListener(监听Start、End、Cancel、Repeat事件)、AnimatorUpdateListener (onAnimationUpdate())两个监听器用于动画在播放过程中的重要动画事件

    animatorSet.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
        }
    });

4、AnimatorSet
如果一个动画用到了一个对象的多个属性,可以使用AnimatorSet;
可以通过playTogether()、playSequentially()、play().with()、after()、before()来控制执行的顺序

  • AnimatorSet
  • playTogether:同时开始执行
  • playSequentially:依次按先后顺序执行,前一个动画执行完,再执行后一个动画
  • AnimatorSet.Builder
  • play():调用AnimatorSet中的play方法,获取AnimatorSet.Builder对象,
  • with():和前面动画同时开始执行
  • after(A):A先执行,然后执行前面的动画
  • before(A):前面的动画先执行,A后执行

代码如下:

AnimatorSet animatorSet=new AnimatorSet();
animatorSet.setDuration(1000);
Path translationPath=new Path();
translationPath.moveTo(0,0);
translationPath.lineTo(200, 300);
Path scalePath=new Path();
scalePath.moveTo(1,1);
scalePath.lineTo(0.5f,0.5f);
scalePath.lineTo(1f,1f);
ObjectAnimator translationAnimator=ObjectAnimator.ofFloat(v,"translationX","translationY",translationPath);
ObjectAnimator rotationAnimator=ObjectAnimator.ofFloat(v,"rotation",0,360);
ObjectAnimator scaleAnimator=ObjectAnimator.ofFloat(v,"scaleX","scaleY",scalePath);
//animatorSet.playTogether(translationAnimator,rotationAnimator);//同时
//animatorSet.playSequentially(translationAnimator,rotationAnimator);//相继的
animatorSet.play(rotationAnimator).after(translationAnimator).before(scaleAnimator);
animatorSet.start();

---

AnimatorSet animatorSet=new AnimatorSet();
animatorSet.setDuration(1000);
ObjectAnimator translationAnimator=ObjectAnimator.ofFloat(v,"translationY",0,-400).setDuration(400);
ObjectAnimator translationAnimator2=ObjectAnimator.ofFloat(v,"translationY",-400,0).setDuration(400);
ObjectAnimator scaleAnimator=ObjectAnimator.ofFloat(v,"scaleX",1,0.1f,1).setDuration(200);
animatorSet.playSequentially(translationAnimator,scaleAnimator,translationAnimator2);//相继的
animatorSet.start();
属性动画2.gif
属性动画3.gif

或者使用XML定义

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">

    <objectAnimator android:duration="400"
        android:propertyName="translationY"
        android:valueFrom="0dp"
        android:valueTo="-200dp"
        android:valueType="floatType"/>

    <objectAnimator android:duration="100"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="0.1"
        android:valueType="floatType"/>

        ...

</set>

当然,对于同一个对象的多个属性,同时作用动画效果,也可以使用PropertyValuesHolder,代码如下:


    PropertyValuesHolder translationHolder=PropertyValuesHolder.ofFloat("translationY",-200);
    PropertyValuesHolder rotationHolder=PropertyValuesHolder.ofFloat("rotation",360);
    ObjectAnimator objectAnimator=ObjectAnimator.ofPropertyValuesHolder(v, translationHolder, rotationHolder);
    objectAnimator.setDuration(1000).start();
    objectAnimator.setInterpolator(new BounceInterpolator());

但是,它不能像AnimatorSet 一样,控制动画执行的顺序

PropertyValueHolder

5、插值器
插值器定义了动画变化过程中的变化规则,需要实现Interpolator接口

publicinterfaceTimeInterpolator{
floatgetInterpolation(floatinput);
}

其中

  • input参数:只与时间有关,取值范围是0到1,表示当前动画的进度,取0时表示动画刚开始,取1时表示动画结束
  • 返回值:动画的当前 数值进度。取值可以超过1也可以小于0,超过1表示已经超过目标值,小于0表示小于开始位置。但这个数值是百分比的,不是具体数值,在监听器返回之前,还需要Evaluator进行转换

Android系统本身内置了一些通用的Interpolator(插值器),如下:
@android:anim/accelerate_interpolator: 越来越快
@android:anim/decelerate_interpolator:越来越慢
@android:anim/accelerate_decelerate_interpolator:先快后慢
@android:anim/anticipate_interpolator: 先后退一小步然后向前加速
@android:anim/overshoot_interpolator:快速到达终点超出一小步然后回到终点
@android:anim/anticipate_overshoot_interpolator:到达终点超出一小步然后回到终点
@android:anim/bounce_interpolator:到达终点产生弹球效果,弹几下回到终点
@android:anim/linear_interpolator:均匀速度。

左右循环摆动cycleInterpolator
<!--@anim/cycle_2  -->
<!--定义cycleInterpolator  -->
<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:cycles="2"/>

<!--@anim/shake_error  -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0%"
    android:toXDelta="2%"
    android:duration="300"
    android:interpolator="@anim/cycle_2"/>

//加载动画
Login.startAnimation(AnimationUtils.loadAnimation(LoginActivity.this,R.anim.shake_error));

默认的插值器

参考:
android动画 Interpolator自定义控件三部曲之动画篇——插值器

6、Evaluator
根据插值器返回的数字进度转成具体数值;在AnimatorUpdateListener的animation.getAnimatedValue()函数拿到的就是Evaluator中返回的数值;系统本身内置了一些Evalutor,如IntEvaluator、FloatEvaluator、ArgbEvalutor

自定义Evaluator,需要实现TypeEvaluator<T>,要注意动画数值类型

publicinterfaceTypeEvaluator<T>{
T  evaluate(floatfraction,T startValue,T endValue)
}
  • fraction:加速器中的返回值,表示当前动画的数值进度
  • startValue和endValue分别对应ofInt(int start,int end)中的start和end的数值;

参考:自定义控件三部曲之动画篇——EvaluatorAndroid 动画学习(二)之Property Animation初步介绍

7、Keyframe关键帧
KeyframeSet是关键帧的集合,Keyframe是一个动画保存time/value(时间与值)对

关键帧

ofInt就是记录了target,propName,values(是将我们传入的int型values,辗转转化成了PropertyValuesHolder),以及一个mValueMap,这个map的key是propName,value是PropertyValuesHolder,在PropertyValuesHolder内部又存储了proprName, valueType , keyframeSet等等

自定义控件三部曲之动画篇(八)——PropertyValuesHolder与KeyframeAndroid 属性动画 源码解析 深入了解其内部实现Android Property Animation(属性动画)原理分析

8、View的animate()方法

可以使用View的animate()方法直接驱动属性动画

    view.animate().y(100).scaleX(0.1f).setDuration(1000).withStartAction(new Runnable() {
        @Override
        public void run() {
        }
    }).withEndAction(new Runnable() {
        @Override
        public void run() {
        }
    }).start();

</br>

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

推荐阅读更多精彩内容