ListView和RecyclerView详解

       刚接触Android开发同学可能很早就使用过ListView这个控件了,在我们做滑动列表的时候一般都会用到,然而到了Android 5.0,谷歌在新的系统特性中,提到了Matrial Design的设计风格,并且伴随着提供了一系列相关的控件,RecyclerView就是其中的控件,用过的同学都说RecyclerView可以直接替代Listview使用,是ListView的升级版,下面本人就从各个方面比较这两个控件,看看他们的区别?


知识点汇总:

一:Listview与RecyclerView实现原理的区别

二:Listview与RecyclerView接口使用的区别

三:Listview与RecyclerView优化手段及相关知识点解析

四:相关开源框架介绍与Rv控件其他UI实现

五:扩展阅读


一:Listview与RecyclerView实现原理的区别

区别一:缓存机制不同

       RecyclerView比ListView多两级缓存,支持多个离开屏幕ItemView缓存,支持开发者自定义缓存处理逻辑,支持所有RecyclerView共用同一个RecyclerViewPool(缓存池)。

具体来说: ListView(两级缓存):

RecyclerView的四级缓存实现:

ListView和RecyclerView缓存机制对比:

1、mActiveViews和mAttachedScrap功能相似,意义在于快速重用屏幕上可见的列表项ItemView,而不需要重新createView和bindView;

2、mScrapView和mCachedViews + mReyclerViewPool功能相似,意义在于缓存离开屏幕的ItemView,目的是让即将进入屏幕的ItemView重用.

3、RecyclerView的优势在于a.mCacheViews的使用,可以做到屏幕外的列表项ItemView进入屏幕内时也无须bindView快速重用;b.mRecyclerPool可以供多个RecyclerView共同使用,在特定场景下,如viewpaper+多个列表页下有优势.客观来说,RecyclerView在特定场景下对ListView的缓存机制做了补强和完善。

缓存不同:

1、RecyclerView缓存RecyclerView.ViewHolder,抽象可理解为: View + ViewHolder(避免每次createView时调用findViewById) + flag(标识状态);

2、ListView缓存View。

ListView获取缓存的流程:

RecyclerView获取缓存的流程:

区别二:缓存对象不同

1、RecyclerView缓存RecyclerView.ViewHolder,抽象可理解为:View +ViewHolder(避免每次createView时调用findViewById) + flag(标识状态);

2、ListView缓存View。

获取缓存对比:

1、 RecyclerView中mCacheViews(屏幕外)获取缓存时,是通过匹配pos获取目标位置的缓存,这样做的好处是,当数据源数据不变的情况下,无须重新bindView,而同样是离屏缓存,ListView从mScrapViews根据pos获取相应的缓存,但是并没有直接使用,而是重新getView(即必定会重新bindView)。

2、 ListView中通过pos获取的是view,即pos–>view; RecyclerView中通过pos获取的是viewholder,即pos –> (view,viewHolder,flag); 从流程图中可以看出,标志flag的作用是判断view是否需要重新bindView,这也是RecyclerView实现局部刷新的一个核心。

3、局部刷新:

  由上可知,RecyclerView的缓存机制确实更加完善,但还不算质的变化,RecyclerView更大的亮点在于提供了局部刷新的接口,通过局部刷新,就能避免调用许多无用的bindView,ListView和RecyclerView在数据源改变时的缓存的处理逻辑,ListView是”一锅端”,将所有的mActiveViews都移入了二级缓存mScrapViews,而RecyclerView则是更加灵活地对每个View修改标志位,区分是否重新bindView。


区别三:数据源改变时的处理

     ListView和RecyclerView在数据源改变时的缓存的处理逻辑,ListView是”一锅端”,将所有的mActiveViews都移入了二级缓存mScrapViews,而RecyclerView则是更加灵活地对每个View修改标志位,区分是否重新bindView。


区别四:预取功能(Prefetch)

解析:Android是通过每16ms刷新一次页面来保证ui的流畅程度,现在android系统中刷新ui会通过cpu产生数据,然后交给gpu渲染的形式来完成,从上图可以看出当cpu完成数据处理交给gpu后,就一直处于空闲状态,需要等待下一帧才会进行数据处理,rv会预取接下来可能要显示的item,在下一帧到来之前提前处理完数据,然后将得到的itemholder缓存起来,等到真正要使用的时候直接从缓存取出来即可。

接口:通过LinearLayoutManager的setInitialItemPrefetchCount()。

区别五:Item回收/复用方面

解析:ListView是以convertView作为回收单位,需要手动添加ViewHolder,需要复用时常常需要设置Tag,而RecyclerView则是以ViewHolder作为回收单位,convertView被内置到了ViewHolder中作为ViewHolder的成员变量,内置了Recycle、多级缓存。


二:Listview与RecyclerView接口使用的区别

Listview:

1、使用setAdapter,重载getView()函数,getItemCount()和getItemType()函数。

RecyclerView:

1、setLayoutManager()。

2、setAdapter(),重载onBindViewHolder(),onCreateViewHolder。

3、addItemDecoration(),getItemOffset函数。

4、setItemAnimator()。

5、SnapHelper。

       由于大家对ListView的使用已经比较熟悉了,这里就不描述了,后面优化部分会有Listview的代码示例,到时去一一了解,这里直接说关于RecyclerView的接口描述。

RecyclerView及相关成员介绍:

1、RecyclerView.Adapter - 处理数据集合并负责绑定视图

2、ViewHolder - 持有所有的用于绑定数据或者需要操作的View

3、LayoutManager - 负责摆放视图等相关操作

4、ItemDecoration - 负责绘制Item附近的分割线

5、ItemAnimator - 为Item的一般操作添加动画效果,如,增删条目等

图例:

Adapter类解析

1、根据不同ViewType创建与之相应的的Item-Layout。

2、访问数据集合并将数据绑定到正确的View上。

常用方法:

 一般常用的重写方法有以下这么几个:

1、public VH onCreateViewHolder(ViewGroup parent, int viewType)

    创建Item视图,并返回相应的ViewHolder


2、public void onBindViewHolder(VH holder, int position)

    绑定数据到正确的Item视图上。


3、public int getItemCount()

    返回该Adapter所持有的Itme数量


4、public int getItemViewType(int position)

    用来获取当前项Item(position参数)是哪种类型的布局

ViewHolder类解析:

作用:

1、adapter应当拥有ViewHolder的子类,并且ViewHolder内部应当存储一些子view,避免时间代价很大的findViewById操作。

2、其RecyclerView内部定义的ViewHolder类包含很多复杂的属性,内部使用场景也有很多,而我们经常使用的也就是onCreateViewHolder()方法和onBindViewHolder()方法,onCreateViewHolder()方法在RecyclerView需要一个新类型时,调用item的ViewHolder来创建一个ViewHolder,而onBindViewHolder()方法则当RecyclerView需要在特定位置的item展示数据时调用。

ViewHolder类解析:

作用:

1、adapter应当拥有ViewHolder的子类,并且ViewHolder内部应当存储一些子view,避免时间代价很大的findViewById操作。

2、其RecyclerView内部定义的ViewHolder类包含很多复杂的属性,内部使用场景也有很多,而我们经常使用的也就是onCreateViewHolder()方法和onBindViewHolder()方法,onCreateViewHolder()方法在RecyclerView需要一个新类型时,调用item的ViewHolder来创建一个ViewHolder,而onBindViewHolder()方法则当RecyclerView需要在特定位置的item展示数据时调用。

复用机制怎样的?

1、模拟场景:只有一种ViewType,上下滑动的时候需要的ViewHolder种类是只有一种,但是需要的ViewHolder对象数量并不止一个。所以在后面创建了9个ViewHolder之后,需要的数量够了,无论怎么滑动,都只需要复用是以前创建的对象就行了。

滑动列表时,log的输出:

对于ViewHolder对象的数量“够用”之后就停止调用onCreateViewHolder方法:

1、获取为给定位置初始化的视图。

2‘此方法应由{@link LayoutManager}实现使用,以获取视图来表示来自Adapter的数据。

3、如果共享池可用于正确的视图类型,则回收程序可以重用共享池中的废视图或分离视图。如果适配器没有指示给定位置上的数据已更改,则回收程序将尝试返回一个以前为该数据初始化的报废视图,而不进行重新绑定。

LayoutManager类解析:

作用:

1、LayoutManager的职责是摆放Item的位置,并且负责决定何时回收和重用Item。

2、RecyclerView 允许自定义规则去放置子 view,这个规则的控制者就是 LayoutManager。一个 RecyclerView 如果想展示内容,就必须设置一个 LayoutManager。

LayoutManager样式有哪些?

1、LinearLayoutManager 水平或者垂直的Item视图。

2、GridLayoutManager 网格Item视图。

3、StaggeredGridLayoutManager 交错的网格Item视图。

SnapHelper类解析:

作用:

       在某些场景下,卡片列表滑动浏览[有的叫轮播图],希望当滑动停止时可以将当前卡片停留在屏幕某个位置,比如停在左边,以吸引用户的焦点。那么可以使用RecyclerView + Snaphelper来实现。

LinearSnapHelper类分析:

        LinearSnapHelper 使当前Item居中显示,常用场景是横向的RecyclerView,类似ViewPager效果,但是又可以快速滑动(滑动多页)。

PagerSnapHelper类分析:

       PagerSnapHelper看名字可能就能猜到,使RecyclerView像ViewPager一样的效果,每次只能滑动一页(LinearSnapHelper支持快速滑动), PagerSnapHelper也是Item居中对齐。

ItemDecoration类解析:

作用:

1、通过设置recyclerView.addItemDecoration(new DividerDecoration(this));来改变Item之间的偏移量或者对Item进行装饰。

2、当然,你也可以对RecyclerView设置多个ItemDecoration,列表展示的时候会遍历所有的ItemDecoration并调用里面的绘制方法,对Item进行装饰。

自定义ItemDecoration有哪些重写方法 :

1、public void onDraw(Canvas c, RecyclerView parent)

解析:装饰的绘制在Item条目绘制之前调用,所以这有可能被Item的内容所遮挡。

2、public void  onDrawOver(Canvas c, RecyclerView parent)

解析:装饰的绘制在Item条目绘制之后调用,因此装饰将浮于Item之上。

3、public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)解析:与padding或margin类似,LayoutManager在测量阶段会调用该方法,计算出每一个Item的正确尺寸并设置偏移量。


三:Listview与RecyclerView优化手段及相关知识点

Listview的优化:

1、局部刷新

2、复用ViewHolder

3、Item的布局优化(尝试使用constrantlayout)

4、getView函数,注意尽量不用做创建对象的操作

5、getView函数,注意尽量不用做复杂运算操作

6、在滑动列表时,停止图片的动态加载(需要配合图片动态请求库)


RecyclerView的优化:(ListView大部分优化点也适用与rv)

1、setHasFixSize使用(嵌套RecyclerView)

2、RecyclerViewPool的使用(嵌套RecyclerView)

3、去除默认动画效果(如果不需要)

4、设置点击事件(不算优化点)

5、diffUtils的使用

Listview的优化:

下面就展示一下, 通用getView的代码示例

@Override 

    public View getView(int position, View convertView, ViewGroup viewGroup) {

        //不同类型的ViewHolder

        TitleViewHolder titleViewHolder = null;

        CompanyViewHolder contentViewHolder = null;

        //对类型进行判断,分别inflate不同的布局

        switch (getItemViewType(position)) {

            case TYPE_TITLE:

                titleViewHolder = new TitleViewHolder();

                if (convertView == null) {

                    convertView = View.inflate(context, R.layout.view_holder_company_index, null);

                    titleViewHolder.title = (TextView) convertView.findViewById(R.id.tv_title);

                    convertView.setTag(titleViewHolder);                       //setTag()

                } else {

                    titleViewHolder = (TitleViewHolder) convertView.getTag();  //getTag();

                }

                titleViewHolder.title.setText(mData.get(position).getName());

                break;

            case TYPE_CONTENT:

                contentViewHolder = new CompanyViewHolder();

                if (convertView == null) {

                    convertView = View.inflate(context, R.layout.view_holder_company, null);

                    contentViewHolder.content = (TextView) convertView.findViewById(R.id.tv_content);

                    convertView.setTag(contentViewHolder);

                } else {

                    contentViewHolder = (CompanyViewHolder) convertView.getTag();

                }

                contentViewHolder.content.setText(mData.get(position).getCode());

                break;

        }

        return convertView;

    }

       我们知道listview并没有提供给我们局部刷新的解接口,可能谷歌后面也意识到这个问题,在2012年的时候,官方也提供了listview局部刷新的代码示例:

google官方局部刷新代码:

private void updateSingleRow(ListView listView, long id) {

        if (listView != null) {

            int start = listView.getFirstVisiblePosition();

            for (int i = start, j = listView.getLastVisiblePosition(); i <= j; i++)

                if (id == ((Messages) listView.getItemAtPosition(i)).getId()) {

                    View view = listView.getChildAt(i - start);

                    getView(i, view, listView);

                    break;

                }

        }

    }

总结:核心就是找出你要更新Item的contentView.然后再去操作。因为ListView默认只会加载一屏的数据,所以要判断其可见范围。不可见的在滑动的时候getView会自动调用更新数据,最后要强调的一点就是关于布局优化,最好将item的高度设置为一个固定的值,这样能减少getView的调用次数。因为一个不确定的值,ListView会频繁调用多次getView去确定其高度和渲染。

下面也有一些热心网友也提供了相关的局部刷新代码,大体思路其实也是一致的。

局部刷新实现:

public void updateSingleRow(ListView mListView, int posi) {

        if (mListView != null) {

            //获取第一个显示的item

            int visiblePos = mListView.getFirstVisiblePosition();

            //计算出当前选中的position和第一个的差,也就是当前在屏幕中的item位置

            int offset = posi - visiblePos;

            int lenth = mListView.getChildCount();

            // 只有在可见区域才更新,因为不在可见区域得不到Tag,会出现空指针,所以这              是必须有的一个步骤

            if ((offset < 0) || (offset >= lenth)) return;

            View convertView = mListView.getChildAt(offset);

            ViewHolder viewHolder = (ViewHolder) convertView.getTag();

            //以下是处理需要的控件方法。。。。。

        }

    }


RecyclerView的优化及相关知识点:

优化一:setHasFixedSize的使用(嵌套RecyclerView)

解析:设置固定高度的rv,避免rv重复measure调用,当item嵌套了rv,并且rv没有设置wrap_content属性时,我们可以对该rv设置setHasFixedSize,这么做的一个最大的好处就是嵌套的rv不会触发requestLayout,从而不会导致外层的rv进行重绘。


优化二:复用pool缓存(嵌套RecyclerView)

使用场景:

1、一个Rv嵌套多个Rv。

2、ViewPager中多个界面都有相同布局的Tv。


   我们看到上面斗鱼App的两张图片UI,在不同的TAG,列表布局几乎完全一样,那么我们就可以尝试复用RecyclerView了。

   解析:

   由于ViewPager默认缓存左右两个fragment,所以进行fragment切换的时候,每次tab0和tab2都会重新生成,对应的onCreateViewHolder和onBindViewHolder都会执行。

       同样的操作,因为公用了RecycledViewPool,所以切换过程中只调用了onBindViewHolder,效果很明显。

接口使用:

1、 RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool();

2、 recyclerView.setRecycledViewPool();


优化三:去除默认动画

解析:默认在开启item动画的情况下会使rv额外处理很多的逻辑判断,notify的增删改操作都会对应相应的item动画效果,所以如果你的应用不需要这些动画效果的话可以直接关闭掉,这样可以在处理增删改操作时大大简化rv的内部逻辑处理。

接口使用:直接调用setItemAnimator(null)即可。


优化四:设置点击事件

解析: onBindViewHolder() 中频繁创建新的 onClickListener 实例没有必要,建议实际开发中应该在 onCreateViewHolder() 中每次为新建的 View 设置一次就行。

代码实例:

@NonNull

@Override

public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

final View view = LayoutInflater.from(mContext).inflate(R.layout.item_me_gv_grid, parent, false);

            final MyViewHolder holder = new MyViewHolder(view);

            view.setOnClickListener(new View.OnClickListener() {

                @Override

                public void onClick(View v) {

                    if (listener != null) {

                        listener.onItemClick(view, holder.getLayoutPosition());

                    }

                }

            });

            return holder;

        }

还有实现方式吗?

使用接口:addOnItemTouchListener();


优化五:diffUtils的使用

解析:diffutil是配合rv进行差异化比较的工具类,通过对比前后两个data数据集合,diffutil会自动给出一系列的notify操作,避免我们手动调用notifiy的繁琐。

使用:

1、实现DiffUtil.Callback抽象类

public abstract static class Callback {

        public Callback() {

        }

        public abstract int getOldListSize();

        public abstract int getNewListSize();

        public abstract boolean areItemsTheSame(int var1, int var2);

        public abstract boolean areContentsTheSame(int var1, int var2);

        @Nullable

        public Object getChangePayload(int oldItemPosition, int newItemPosition) {

            return null;

        }

    }

2、调用代码:

DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff( newDiffCallBack(), true);

diffResult.dispatchUpdatesTo(adapter);

adapter.setData(newData);

        最后介绍一个关于RecyclerVIew的Adapter的开源项目,BaseRecyclerViewAdapterHelper,里面就封装了DiffUtil的使用,可以查看接口setNewDiffData。


四:相关开源框架介绍与Rv实现常见UI介绍

项目一:BaseRecyclerViewAdapterHelper(18.9k)

项目简介:

1、优化Adapter代码:和原始的adapter相对,减少70%的代码量。

2、添加Item事件:Item的点击事件,Item的长按事件,Item子控件的点击事件,Item子控件的长按事件

3、添加列表加载动画:一行代码轻松切换5种默认动画。

4、添加头部、尾部:一行代码搞定,感觉又回到ListView时代。

5、自动加载:上拉加载无需监听滑动事件,可自定义加载布局,显示异常提示,自定义异常提示。同时支持下拉加载。

6、分组布局:随心定义分组头部。

7、多布局:简单配置、无需重写额外方法。

8、设置空布局:比Listview的setEmptyView还要好用。

9、添加拖拽、滑动删除:开启,监听即可,就是这么简单。

10、树形列表:比ExpandableListView还要强大,支持多级。

11、自定义ViewHolder:支持自定义ViewHolder,让开发者随心所欲。

12、扩展框架:组合第三方框架,轻松实现更多需求定制。

项目地址:https://github.com/CymChad/BaseRecyclerViewAdapterHelper


项目二:UltimateRecyclerView(7.1k)

项目简介:UltimateRecyclerview是一种功能强大的RecyclerView(advanced and flexible version ofListView),包括了下拉刷新,加载更多,多种动画,空数据提示,拖动排序,视差处理,工具栏渐变,滑动删除,自定义floating button,多种刷新效果,scrollbar等等元素。

项目地址:https://github.com/cymcsg/UltimateRecyclerView


项目三:vlayout(10k)

项目简介:VirtualLayout是一个针对RecyclerView的LayoutManager扩展,主要提供一整套布局方案和布局间的组件复用的问题。

设计思路:通过定制化的LayoutManager,接管整个RecyclerView的布局逻辑;LayoutManager管理了一系列LayoutHelper,LayoutHelper负责具体布局逻辑实现的地方;每一个LayoutHelper负责页面某一个范围内的组件布局;不同的LayoutHelper可以做不同的布局逻辑,因此可以在一个RecyclerView页面里提供异构的布局结构,这就能比系统自带的LinearLayoutManager、GridLayoutManager等提供更加丰富的能力。同时支持扩展LayoutHelper来提供更多的布局能力。

项目地址:https://github.com/alibaba/vlayout


Rv实现常见UI介绍:

一:抖音首页实现原理

旧方案思路:垂直的viewpager+下拉刷新上拉加载控件+ fragment的组合方案

新方案思路:RecyclerView+SnapHelper++ fragment的组合方案

什么是SnapHelper:

       SnapHelper是一个抽象类,官方提供了一个LinearSnapHelper的子类,可以让RecyclerView滚动停止时相应的Item停留中间位置。在25.1.0版本中,官方又提供了一个PagerSnapHelper的子类,可以使RecyclerView像ViewPager一样的效果,一次只能滑一页,而且居中显示。

二:ViewPager2实现原理(继承RecyclerView)

新功能:

1、支持RTL布局(Right To Left)

2、支持竖向滚动

3、完整支持notifyDataSetChanged

API的变动:

1、FragmentStateAdapter替换了原来的FragmentStatePagerAdapter。

2、RecyclerView.Adapter替换了原来的PagerAdapter。

3、registerOnPageChangeCallback替换了原来的addPageChangeListener。

实现下滑翻页:RecyclerView+LinearLayoutManager+PagerSnapHelper


五:扩展阅读

1、http://www.sohu.com/a/300876569_611601(看完感觉我RecyclerView白学了!)

2、https://juejin.im/post/5cce410551882541e40e471d(RecyclerView问题汇总)

3、//www.greatytc.com/p/193fb966e954(AndroidListView与RecyclerView对比浅析--缓存机制)

4、//www.greatytc.com/p/aedb2842de30(RecyclerView性能优化|安卓offer收割基)

5、//www.greatytc.com/p/45a43a117365(AndroidRecyclerView与ListView局部刷新)

6、//www.greatytc.com/p/122e68e9ddac(RecycledViewPool使用)

7、//www.greatytc.com/p/29352def27e6(RecyclerViewnotifyDataSetChanged导致图片闪烁的真凶)

8、https://blog.csdn.net/qq_21138819/article/details/83028693(【Android进阶】仿抖音系列之翻页上下滑切换视频)

9、https://juejin.im/post/5bb85f52f265da0af609c685(快速实现android版抖音主界面的心得)

10、//www.greatytc.com/p/e0bd595d6321(【Android进阶】仿抖音系列之翻页上下滑切换视频(四))

11、//www.greatytc.com/p/e54db232df62(让你明明白白的使用RecyclerView——SnapHelper详解)

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