仿京东商城系列6------下拉刷新上拉加载的商品列表

本项目来自菜鸟窝,有兴趣者点击http://www.cniao5.com/course/

项目已经做完,
https://github.com/15829238397/CN5E-shop


仿京东商城系列0------项目简介
仿京东商城系列1------fragmentTabHost实现底部导航栏
仿京东商城系列2------自定义toolbar
仿京东商城系列3------封装Okhttp
仿京东商城系列4------轮播广告条
仿京东商城系列5------商品推荐栏
仿京东商城系列6------下拉刷新上拉加载的商品列表
仿京东商城系列7------商品分类页面
仿京东商城系列8------自定义的数量控制器
仿京东商城系列9------购物车数据存储器实现
仿京东商城系列10------添加购物车,管理购物车功能实现
仿京东商城系列11------商品排序功能以及布局切换实现(Tablayout)
仿京东商城系列12------商品详细信息展示(nativie与html交互)
仿京东商城系列13------商品分享(shareSDK)
仿京东商城系列14------用户登录以及app登录拦截
仿京东长城系列15------用户注册,SMSSDK集成
仿京东商城系列16------支付SDK集成
仿京东商城系列17------支付功能实现
仿京东商城系列18------xml文件读取(地址选择器)
仿京东商城系列19------九宫格订单展示
仿京东商城系列20------终章


前言

本篇文章所设计的效果,将用到前文讲述到的Okhttp封装工具,adapter封装工具,以及本文新涉及到的,新的图片加载工具,缓冲框架等等。废话少说,来看效果。

内容

Fresco简介

Fresco 的详细介绍

  1. Fresco 是什么

首先,Fresco 是一款开源的图片加载组件,也是目前最强大的图片加载组件。
其次,Fresco 中设计有一个叫做 image pipeline 的模块。它负责从网络,从本地文件系统,本地资源加载图片。 为了最大限度节省空间和CPU时间,它含有3级缓存设计(2级内存,1级文件)。
另外,还有一个特别的地方,Fresco 中设计有一个叫做 Drawees 模块, 方便地显示 loading 图,当图片不再显示在屏幕上时,及时地释放内存和空间占用。
同时,Fresco 支持 Android 2.3(API level 9)及其以上版本。

  1. Fresco 的独特之处

Fresco 作为众多开源图片加载组件之一,可以受到广大开发者的喜爱,自然有着它的独特之处。

2.1 内存管理

一个没有未压缩的图片,即 Android 中的 Bitmap,占用大量的内存。大的内存占用势必引发更加频繁的GC。 在5.0以下,GC 将会显著地引发界面卡顿。
在5.0以下系统,Fresco 将图片放到一个特别的内存区域,也就是 Ashmem (系统匿名共享内存)。当然,在图片不显示的时候,占用的内存会自动被释放。 这会使得 APP 更加流畅,减少因图片内存占用而引发的 OOM。

2.2 渐进式呈现图片

Fresco 加载图片时,可以实现渐进式呈现图片,渐进式的 JPEG 图片格式已经流行数年了,渐进式图片格式先呈现大致的图片轮廓,然后随着图片下载的继续, 呈现逐渐清晰的图片,这对于移动设备,尤其是慢网络有极大的利好,可带来更好的用户体验。

2.3 支持Gif图和WebP格式

作为加载组件,Fresco 不仅支持简单的 JPG、PNG 格式的图片加载,还同时支持 Gif 和 WebP 格式的图片加载,非常强大。

2.4 图像的呈现方式

Fresco 的图像呈现方式也很特别,可以自定义居中焦点(对人脸等图片显示非常有帮助),支持圆角图,当然圆圈也行,并且下载失败之后,点击可以重新下载,特别是可以自定义占位图,自定义 overlay, 或者进度条,同时可以指定用户按压时的 overlay。

2.5 图像的加载

图像的加载时很重要的一部分,Fresco 可以为同一个图片指定不同的远程路径,或者使用已经存在本地缓存中的图片,加载时先显示一个低解析度的图片,等高清图下载完之后再显示高清图,等到加载完成时回调通知,并且对于本地图,如有 EXIF 缩略图,在大图加载完成之前,可先显示缩略图。至于缩放或者旋转图片和处理已下载的图片,Fresco 都是可以做到的。

Fresco 的基本使用

前面已经对 Fresco 做了非常详细的介绍了,对于 Fresco 一定很感兴趣吧,下面就开始使用它吧。

  1. 添加 Gradle 依赖

在使用 Fresco 之前,一定要记得先使用 Gradle 添加对 Fresco 的依赖,代码如下。、

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'com.facebook.fresco:fresco:0.12.0'
}
  1. 添加权限

加载网络图片时,需要一定的网络权限,所以必须添加网络权限。

<uses-permission android:name="android.permission.INTERNET"/>
  1. 初始化 Fresco

在加载图片之前,你必须初始化 Fresco 类。你只需要调用 Fresco.initialize() 一次即可完成初始化,在 Application 里面做这件事再适合不过了(如下面的代码),注意多次的调用初始化是无意义的。

public class CniaoApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Fresco.initialize(this,config);
    }
}

下面就是需要在 AndroidManifest.xml 中指定相应的 Application 类。

<application
        android:name=".CniaoApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
     .......
</application>
  1. 添加 SimpleDraweeView

首先,在 xml 布局文件 Layout 中, 加入命名空间。

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:layout_height="match_parent"
    android:layout_width="match_parent">
   ......
</LinearLayout>

然后在布局中加入 SimpleDraweeView。

<com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/image_view"
        android:layout_width="500dp"
        android:layout_height="300dp"
        fresco:placeholderImage="@drawable/default_loading"
/>
  1. 加载图片

在 Activity/Fragment 中写入图片加载即可。

public class MainActivity extends AppCompatActivity {
    private  String img_url="http://img4q.duitang.com/uploads/item/201411/20/20141120132318_3eAuc.thumb.700_0.jpeg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Uri uri = Uri.parse(img_url);
        SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.image_view);
        draweeView.setImageURI(uri);
     }
}

MaterialRefreshLayout简介

MaterialRefreshLayout是github上的一个开源控件,这款组件在原本官方的控件 SwipeRefreshLayout 上做了扩展,不仅可以支持下拉刷新,还可以支持加载更多。

  • 使用方式
  1. 添加依赖
 compile 'com.cjj.materialrefeshlayout:library:1.3.0'
  1. 在布局文件中使用,本例使用了MaterialRefreshLayout+RecyclerView组合的方式,实现下拉刷新,加载更多功能,具体代码如下:
<com.cjj.MaterialRefreshLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/materialRefreshLayout">

    <android.support.v7.widget.RecyclerView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/resyclerView"></android.support.v7.widget.RecyclerView>

    </com.cjj.MaterialRefreshLayout>
  1. 在Activity或者Fragment中进行使用。
    3.1.实例化MaterialRefreshLayout和 RecyclerView
@ViewInject(R.id.recycler_view_hot)
private RecyclerView mRecyclerView;

@ViewInject(R.id.refresh)
private MaterialRefreshLayout materialRefreshLayout;

3.2.获得数据,计算当前页,总页数和每页的数目,用于MaterialRefreshLayout进行分页处理

private int totlaPage = 1;
private int curPage = 1;
private int pageSize = 10;
private OkHttpHelper httpHelper = OkHttpHelper.getInstance();
private List<Wares> waresList = new ArrayList<>();
private void getData( ){
    httpHelper.get(url, new SpotsCallBack<Page<Wares>>(getContext()) {
        @Override
        public void onSuccess(Response response, Page<Wares> waresPage) {
            waresList = waresPage.getList();
            curPage = waresPage.getCurrentPage();
            pageSize = waresPage.getPageSize();
            totlaPage = waresPage.getTotalPage();
            showData();
        }
        @Override
        public void onError(Response response, int code, Exception e) {
        }
    });
}

3.3.RecyclerView显示数据,此时RecyclerView不知是按照之前的方式(设置适配器)来显示数据了,而是按照不同的状态来显示数据

private static final int STATE_NORMAL = 0;
private static final int STATE_REFREN = 1;
private static final int STATE_MORE = 2;
private int state = STATE_NORMAL;
private void showData(){
    switch (state){
        case STATE_NORMAL:
         //此处完成正常加载操作
            break;

        case STATE_REFREN:
           //此处完成刷新操作
            materialRefreshLayout.finishRefresh();
            break;

        case STATE_MORE:
           //此处完成加载更多操作
            materialRefreshLayout.finishRefreshLoadMore();
            break;
    }
}

3.4为MaterialRefreshLayout添加监听器,来实现下拉刷新和上拉加载时的逻辑

private void initMaterialRefreshLayout(){
    materialRefreshLayout.setLoadMore(true);
    materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {
           @Override
           public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {
                   //refreshing...
               refreshData();
           }
           @Override
           public void onRefreshLoadMore(MaterialRefreshLayout materialRefreshLayout) {
               //load more refreshing...
               if(curPage <= totlaPage)
                     loadMore();
               else {
                   Toast.makeText(getContext(), "没有更多了...", Toast.LENGTH_LONG).show();
                   materialRefreshLayout.finishRefreshLoadMore();
               }
           }
    });
}
public void refreshData(){
    curPage = 1;
    state = STATE_REFREN;
    getData();
}
private void loadMore(){
    curPage = curPage + 1;
    state = STATE_MORE;
    getData();
}

3.5适配器,用Fresco来加载图片

public class HotWaresAdapter extends RecyclerView.Adapter<HotWaresAdapter.ViewHolder>  {
    private List<Wares> mDatas;
    private LayoutInflater mInflater;
    public HotWaresAdapter(List<Wares> wares){
        mDatas = wares;
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        mInflater = LayoutInflater.from(parent.getContext());
        View view = mInflater.inflate(R.layout.template_hot_wares,null);
        return new ViewHolder(view);
    }
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Wares wares = getData(position);
        holder.draweeView.setImageURI(Uri.parse(wares.getImgUrl()));
        holder.textTitle.setText(wares.getName());
        holder.textPrice.setText("¥"+wares.getPrice());
    }
    public Wares getData(int position){
        return mDatas.get(position);
    }
    public List<Wares> getDatas(){
        return  mDatas;
    }
    public void clearData(){
        mDatas.clear();
        notifyItemRangeRemoved(0,mDatas.size());
    }
    public void addData(List<Wares> datas){

        addData(0,datas);
    }
    public void addData(int position,List<Wares> datas){
        if(datas !=null && datas.size()>0) {
            mDatas.addAll(datas);
            notifyItemRangeChanged(position, mDatas.size());
        }
    }
    @Override
    public int getItemCount() {
        if(mDatas!=null && mDatas.size()>0)
            return mDatas.size();
        return 0;
    }
    class ViewHolder extends RecyclerView.ViewHolder{
        SimpleDraweeView draweeView;
        TextView textTitle;
        TextView textPrice;
        public ViewHolder(View itemView) {
            super(itemView);
            draweeView = (SimpleDraweeView) itemView.findViewById(R.id.drawee_view);
            textTitle= (TextView) itemView.findViewById(R.id.text_title);
            textPrice= (TextView) itemView.findViewById(R.id.text_price);
        }
    }
}

封装下拉刷新页面功能

  • 将分页所需信息,封装在一个数据类中
package com.example.shoppingstore.Bean;

import java.util.List;

/**
 * Created by 博 on 2017/7/13.
 */

public class Page<T> {

    private int totalCount ;
    private int currentPage ;
    private int totalPage ;
    private int pageSize ;
    private List<Ware> list ;

    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public List<Ware> getList() {
        return list;
    }

    public void setList(List<Ware> list) {
        this.list = list;
    }
}

  • 对分页功能进行封装,我们来先看一下代码,我们再逐步分析。
package com.example.cne_shop.utils;

import android.content.Context;
import android.widget.Toast;

import com.cjj.MaterialRefreshLayout;
import com.cjj.MaterialRefreshListener;
import com.example.cne_shop.bean.Page;
import com.example.cne_shop.bean.Ware;
import com.example.cne_shop.okhttp.OkhttpHelper;
import com.example.cne_shop.okhttp.loadingSpotsDialog;
import com.squareup.okhttp.Response;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by 博 on 2017/7/15.
 */

public class Pager {

    private List<Ware> mData ;
    private static final int STATUS_NORMAL = 0 ;
    private static final int STATUS_FRESH = 1 ;
    private static final int STATUS_LOADMARE = 2 ;

    private int status = STATUS_NORMAL ;

    //定义一个静态实例,接收数据,生成一个Pager实例对象
    private static Pager.Builder builder ;

    private OkhttpHelper okhttpHelper;

    private Pager(){

        okhttpHelper = OkhttpHelper.getOkhttpHelper() ;
        getData() ;
        setMaterialRefreshLayoutListener(builder.materialRefreshLayout) ;

    }

    public void changeParamsInUri(String key , Integer value){

        builder.params.put(key , value+"") ;

    }

    public int getTotalCount (){
        return this.builder.totalCount ;
    }

    //对MaterialRefreshLayout添加事件。实现上滑和下拉。
    public void setMaterialRefreshLayoutListener (MaterialRefreshLayout materialRefreshLayout ){
        materialRefreshLayout.setLoadMore(true);
        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {
            @Override
            public void onRefresh(MaterialRefreshLayout materialRefreshLayout) {

                Toast.makeText(materialRefreshLayout.getContext() , "刷新" , Toast.LENGTH_SHORT).show();
                freshData() ;
            }

            @Override
            public void onRefreshLoadMore(MaterialRefreshLayout materialRefreshLayout) {

                if( builder.curPage*builder.pageSize < builder.totalCount ){
                    Toast.makeText(materialRefreshLayout.getContext() , "加载更多" , Toast.LENGTH_SHORT).show();
                    loadMore() ;
                }else {
                    materialRefreshLayout.finishRefreshLoadMore();
                    Toast.makeText(materialRefreshLayout.getContext() , "已经到底啦" , Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    //实现刷新数据功能
    public void freshData(){
        status = STATUS_FRESH ;
        builder.curPage = 1 ;

        builder.putParams("curPage" , builder.curPage) ;
        getData();

    }

    //实现加载更多功能
    public void loadMore(){
        status = STATUS_LOADMARE ;
        builder.curPage = builder.curPage + 1 ;

        builder.putParams("curPage" , builder.curPage) ;
        getData();
    }

    //从服务器获得数据
    public void getData(){
        okhttpHelper.doGet(builder.uri , new PageCallBack(builder.context) , builder.params);
    }

    //展示得到的数据
    public <T> void showData(List<T> mData , int totalPage , int pageSize){

        switch (status){
            case STATUS_NORMAL :

                if (builder.onPageListener !=null ){
                    builder.onPageListener.loadNormal(mData , totalPage ,pageSize);
                }

                break;
            case STATUS_LOADMARE :

                if (builder.onPageListener !=null ){
                    builder.onPageListener.loadMoreData(mData , totalPage ,pageSize);
                }
                builder.materialRefreshLayout.finishRefreshLoadMore();
                status = STATUS_NORMAL ;
                break;
            case STATUS_FRESH :

                if (builder.onPageListener !=null ){
                    builder.onPageListener.refData(mData , totalPage ,pageSize);
                }
                builder.materialRefreshLayout.finishRefresh();
                status = STATUS_NORMAL ;
                break;
        }
    }

    public static Pager.Builder getBuilder(){

        return  builder = new Builder() ;
    }

    public interface OnPageListener<T>{
        void loadNormal(List<T> mData , int totalPage , int pageSize) ;
        void loadMoreData(List<T> mData , int totalPage , int pageSize);
        void refData(List<T> mData , int totalPage , int pageSize) ;
    }

    /**
     * 用来盛放所需要的所有数据。只涉及到数据的存取
     */
    public static class Builder {

        public int curPage = 1 ;
        private int pageSize = 10 ;
        public int totalCount = 1 ;
        private String uri ;
        private MaterialRefreshLayout materialRefreshLayout ;
        private Type type ;
        private Context context ;
        private OnPageListener onPageListener ;
        private List<Ware> mData ;
        public Map<String, String> params ;

        public Map<String, String> getParams() {
            return params;
        }

        public Builder (){
            params = new HashMap<>();
        }

        public Builder setOnPageListener(OnPageListener onPageListener){
            builder.onPageListener = onPageListener ;
            return builder;
        }

        public Builder setMaterialRefreshLayout(MaterialRefreshLayout materialRefreshLayout){

            builder.materialRefreshLayout = materialRefreshLayout ;
            return builder ;

        }

        public Builder setUri(String uri){

            builder.uri = uri ;
            return builder ;

        }

        public Pager build(Context context , Type type) throws Exception {

            builder.type = type ;
            builder.context = context ;

            isVisible() ;

            return new Pager() ;
        }

        public void isVisible() throws Exception {
            if(builder.uri == null || builder.curPage==0 && builder.pageSize==0){
                throw new Exception("uri 为空") ;
            }
        }

        public Builder putParams(String Key , Object value){
            builder.params.put(Key , value+"") ;
            return builder ;
        }


    }

    /**
     * 继承loadingSpotsDialog<Page<T>>实现对网络加载拦截处理。
     * @param <T>
     */
    class PageCallBack<T> extends loadingSpotsDialog<Page<T>> {

        public PageCallBack(Context context){
            super(context);
            this.type = builder.type ;
        }

        @Override
        public void onErroe(Response response, int responseCode, Exception e) throws IOException {
            this.closeSpotsDialog();
        }

        @Override
        public void callBackSucces(Response response, Page<T> page) throws IOException {

            builder.curPage = page.getCurrentPage() ;
            builder.totalCount = page.getTotalCount() ;

            showData(page.getList() , page.getTotalPage() , page.getPageSize());
            this.closeSpotsDialog();
        }
    }

}

简单陈述:

  • 定义了一个静态内部类Builder,Builder类用来盛放分页刷新处理所需要的所有对象。

  • 定义了一个内部类继承了loadingSpotsDialog<Page<T>>实现了对网络请求的拦截处理,在这部分完成将从服务器获得的新的数据赋值给Builder,更新curPage , totalCount 。并将得到的商品信息展示到页面。

  • 定义了一个接口,将具体处理展示数据的过程交给调用者实现。

  • 给MaterialRefreshLayout添加事件监听。

  • 上述为Page所做的事情。我们来大概理一遍执行顺序,首先,调用静态方法得到Builder类的对象,然后向对象添加curPage,pageSize, uri,MaterialRefreshLayout实例,OnPageListener接口实例等所需要的初始值,最后调用build方法。在build方法中调用了new page()方法,间接调用了setMaterialRefreshLayoutListener方法,对MaterialRefreshLayout添加监听事件,一旦监听到有上滑或者下拉动作时,将会立即执行相关方法,即更新uri(如果还有数据可供加载),调用getdata方法,通过okhttp对服务器发出请求,获得新的数据,在我们新定义的类PageCallBack中处理了,请求结果。如果成功则调用showData()方法,对得到的数据进行展示。而showData()调用了,我们自定义接口的方法。这些方法需要调用者自行实现。

  • 上面已经讲述了封装的page执行的大概过程,下面给出使用封装的代码

  public void initMaterialRefreshLayout() throws Exception {

        String uri = Contents.API.HOT ;

        Pager.Builder builder = Pager.getBuilder()
                .setMaterialRefreshLayout(materialRefreshLayout)
                .putParams("curPage" , 1)
                .putParams("pageSize" , 10)
                .setUri(uri)
                .setOnPageListener(new Pager.OnPageListener<Ware>() {
                    @Override
                    public void loadNormal(List<Ware> mData, int totalPage, int pageSize) {
                        mHotAdapter =new HotAdapter(mContext , mData ) ;
//                        setItemlistenler() ;
                        recyclerView.setAdapter(mHotAdapter);
                        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
                        recyclerView.addItemDecoration(new MyDivider());
                    }

                    @Override
                    public void loadMoreData(List<Ware> mData, int totalPage, int pageSize) {
                        mHotAdapter.addData(mData);
                    }

                    @Override
                    public void refData(List<Ware> mData, int totalPage, int pageSize) {
                        mHotAdapter.cleanData();
                        mHotAdapter.addData(mData);
                        recyclerView.scrollToPosition(0);
                    }
                }) ;

        pager = builder.build(mContext , Page.class) ;

    }

我们的封装已经结束,大家快去使用吧。

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

推荐阅读更多精彩内容