笔记20170420--用PullRefreshLayout+RecyclerView实现上拉加载下拉刷新(Android)

需求:

达到像LoadMoreListView那样的效果,实现分页时上拉加载等待时间有个footerView

要达到的效果

上拉加载.jpg
下拉刷新.jpg

PullRefreshLayout+RecyclerView相关代码:

xml--Activity的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="xxx.xxx.xxx.xxx.activity.XXXActivity">

    <com.baoyz.widget.PullRefreshLayout
        android:id="@+id/pullRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:overScrollMode="never" />
    </com.baoyz.widget.PullRefreshLayout>
</LinearLayout>

xml--上拉加载的footerView的布局(R.layout.load_more_footer):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="46dp"
    android:background="@color/globalBackGround"
    android:gravity="center"
    android:id="@+id/loadmore"
    android:orientation="horizontal">

    <ProgressBar
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="35dp"
        android:layout_height="35dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="46dp"
        android:gravity="center"
        android:text="正在加载..." />
</LinearLayout>
xml--adapter用到的item布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/touch_bg"
    android:clickable="true"
    android:id="@+id/msg_layout"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dip">

        <ImageView
            android:id="@+id/bell_logo"
            android:layout_width="50dp"
            android:src="@drawable/xtxx_icon"
            android:layout_height="50dp"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_gravity="center"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="15dp"
            android:layout_marginRight="15dp"
            android:layout_marginTop="10dp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@id/bell_logo"
            android:layout_toRightOf="@id/bell_logo"
            android:ellipsize="end"
            android:gravity="left|center_vertical"
            android:lines="1"
            android:text="系统消息"
            android:textColor="#1e1e1e"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/msg_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/bell_logo"
            android:layout_toLeftOf="@+id/red_point"
            android:layout_toRightOf="@id/bell_logo"
            android:ellipsize="end"
            android:gravity="left|center_vertical"
            android:lines="1"
            android:text="content content......." />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_alignTop="@id/bell_logo"
            android:layout_marginRight="10dp"
            android:text="2017-04-20"
            android:id="@+id/msg_time"
            android:textColor="@color/color_aaaaaa_content_text"
            android:textSize="10sp" />

</LinearLayout>
网上找的HeaderViewRecyclerAdapter:
package xxx.xxx.xxx.xxx.adapter;

/*
 * Copyright (C) 2014 darnmason
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * RecyclerView adapter designed to wrap an existing adapter allowing the addition of
 * header views and footer views.
 * </p>
 * <p>
 * I implemented it to aid with the transition from ListView to RecyclerView where the ListView's
 * addHeaderView and addFooterView methods were used. Using this class you may initialize your
 * header views in the Fragment/Activity and add them to the adapter in the same way you used to
 * add them to a ListView.
 * </p>
 * <p>
 * I also required to be able to swap out multiple adapters with different content, therefore
 * setAdapter may be called multiple times.
 * </p>
 * Created by darnmason on 07/11/2014.
 */
public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int HEADERS_START = Integer.MIN_VALUE;
    private static final int FOOTERS_START = Integer.MIN_VALUE + 10;
    private static final int ITEMS_START = Integer.MIN_VALUE + 20;
    private static final int ADAPTER_MAX_TYPES = 100;

    private RecyclerView.Adapter mWrappedAdapter;
    private List<View> mHeaderViews, mFooterViews;
    private Map<Class, Integer> mItemTypesOffset;

    /**
     * Construct a new header view recycler adapter
     * @param adapter The underlying adapter to wrap
     */
    public HeaderViewRecyclerAdapter(RecyclerView.Adapter adapter) {
        mHeaderViews = new ArrayList<View>();
        mFooterViews = new ArrayList<View>();
        mItemTypesOffset = new HashMap<Class, Integer>();
        setWrappedAdapter(adapter);
    }

    /**
     * Replaces the underlying adapter, notifying RecyclerView of changes
     * @param adapter The new adapter to wrap
     */
    public void setAdapter(RecyclerView.Adapter adapter) {
        if(mWrappedAdapter != null && mWrappedAdapter.getItemCount() > 0) {
            notifyItemRangeRemoved(getHeaderCount(), mWrappedAdapter.getItemCount());
        }
        setWrappedAdapter(adapter);
        notifyItemRangeInserted(getHeaderCount(), mWrappedAdapter.getItemCount());
    }

    @Override
    public int getItemViewType(int position) {
        int hCount = getHeaderCount();
        if (position < hCount) return HEADERS_START + position;
        else {
            int itemCount = mWrappedAdapter.getItemCount();
            if (position < hCount + itemCount) {
                return getAdapterTypeOffset() + mWrappedAdapter.getItemViewType(position - hCount);
            }
            else return FOOTERS_START  + position - hCount - itemCount;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        if (viewType < HEADERS_START + getHeaderCount())
            return new StaticViewHolder(mHeaderViews.get(viewType - HEADERS_START));
        else if (viewType < FOOTERS_START + getFooterCount())
            return new StaticViewHolder(mFooterViews.get(viewType - FOOTERS_START));
        else {
            return mWrappedAdapter.onCreateViewHolder(viewGroup, viewType - getAdapterTypeOffset());
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        int hCount = getHeaderCount();
        if (position >= hCount && position < hCount + mWrappedAdapter.getItemCount())
            mWrappedAdapter.onBindViewHolder(viewHolder, position - hCount);
    }

    /**
     * Add a static view to appear at the start of the RecyclerView. Headers are displayed in the
     * order they were added.
     * @param view The header view to add
     */
    public void addHeaderView(View view) {
        mHeaderViews.add(view);
    }

    /**
     * Add a static view to appear at the end of the RecyclerView. Footers are displayed in the
     * order they were added.
     * @param view The footer view to add
     */
    public void addFooterView(View view) {
        mFooterViews.add(view);
    }

    @Override
    public int getItemCount() {
        return getHeaderCount() + getFooterCount() + getWrappedItemCount();
    }

    /**
     * @return The item count in the underlying adapter
     */
    public int getWrappedItemCount() {
        return mWrappedAdapter.getItemCount();
    }

    /**
     * @return The number of header views added
     */
    public int getHeaderCount() {
        return mHeaderViews.size();
    }

    /**
     * @return The number of footer views added
     */
    public int getFooterCount() {
        return mFooterViews.size();
    }

    private void setWrappedAdapter(RecyclerView.Adapter adapter) {
        if (mWrappedAdapter != null) mWrappedAdapter.unregisterAdapterDataObserver(mDataObserver);
        mWrappedAdapter = adapter;
        Class adapterClass = mWrappedAdapter.getClass();
        if(!mItemTypesOffset.containsKey(adapterClass)) putAdapterTypeOffset(adapterClass);
        mWrappedAdapter.registerAdapterDataObserver(mDataObserver);
    }

    private void putAdapterTypeOffset(Class adapterClass) {
        mItemTypesOffset.put(adapterClass, ITEMS_START + mItemTypesOffset.size() * ADAPTER_MAX_TYPES);
    }

    private int getAdapterTypeOffset() {
        return mItemTypesOffset.get(mWrappedAdapter.getClass());
    }

    private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            super.onChanged();
            notifyDataSetChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            super.onItemRangeChanged(positionStart, itemCount);
            notifyItemRangeChanged(positionStart + getHeaderCount(), itemCount);
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            super.onItemRangeInserted(positionStart, itemCount);
            notifyItemRangeInserted(positionStart + getHeaderCount(), itemCount);
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            super.onItemRangeRemoved(positionStart, itemCount);
            notifyItemRangeRemoved(positionStart + getHeaderCount(), itemCount);
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount);
            int hCount = getHeaderCount();
            // TODO: No notifyItemRangeMoved method?
            notifyItemRangeChanged(fromPosition + hCount, toPosition + hCount + itemCount);
        }
    };

    private static class StaticViewHolder extends RecyclerView.ViewHolder {

        public StaticViewHolder(View itemView) {
            super(itemView);
        }
    }
    //下边这个方法是我自己拷贝另一个网上的adpter的,作用是获得footerview,好在加载完的时候直接隐藏掉footerview
    public View getFooterView() {
        return  getFooterCount()>0 ? mFooterViews.get(0) : null;
    }

}

网上找的自定义onScrollListener:
package xxx.xxx.xxx.common.listener;

import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;


public abstract class SysMsgListScrollListener  extends RecyclerView.OnScrollListener{

    private LinearLayoutManager mLinearLayoutManager;
    int lastVisibleItemPosition;

    public SysMsgListScrollListener(LinearLayoutManager linearLayoutManager) {
        this.mLinearLayoutManager = linearLayoutManager;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        //获取最后一个显示 的item的位置
        lastVisibleItemPosition = mLinearLayoutManager.findLastVisibleItemPosition();
    }

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        //这一段是关键,如果显示的item总数大于0,并且滑动状态符合,并且最后一个位置大于等于item总数量-1的话(代表最后一个) 就执行加载方法onLoadMore();
        int currentScrollState = newState;
        int visibleItemCount = mLinearLayoutManager.getChildCount();
        int totalItemCount = mLinearLayoutManager.getItemCount();
        if ((visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE && (lastVisibleItemPosition) >= totalItemCount - 1)) {
            onLoadMore();
        }
    }

    public abstract void onLoadMore();

}

自己写的一个接口,用作item点击监听:
package xxx.xxx.xxx.common.listener;

/**
 * Created by Hello我的World on 2017/4/20.
 */

public interface SysMsgOnItemClickListener {
     void sysMsgItemClick(long id);
}

ViewHolder:
package xxx.xxx.xxx.xxx.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import xxx.xxx.xxx.R;
import xxx.xxx.xxx.common.bean.SysMsgInfo;
import xxx.xxx.xxx.common.listener.SysMsgOnItemClickListener;

import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

/**
 * Created by Hello我的World on 2017/4/20.
 */

public class ViewHolder extends RecyclerView.ViewHolder  {

    @BindView(R.id.msg_layout)
    LinearLayout msg_layout;
    @BindView(R.id.msg_title)
    TextView msg_title;
    @BindView(R.id.msg_time)
    TextView msg_time;
    
    SysMsgOnItemClickListener listener;
    List<SysMsgInfo> data;

    public ViewHolder(View itemView, SysMsgOnItemClickListener listener, List<SysMsgInfo>data) {
        super(itemView);
        ButterKnife.bind(this,itemView);
        this.listener = listener;
        this.data = data;
    }

    @OnClick(R.id.msg_layout)
    public void onClick(){
        listener.sysMsgItemClick(data.get(getLayoutPosition()).getId());
    }

    public void setContent(SysMsgInfo info){
        msg_title.setText(info.getTitle());
        msg_time.setText(info.getCreateTime());
    }

}
真正加载数据的Adapter(用来放到上边那个“网上找的adapter”里):
package xxx.xxx.xxx.common.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import xxx.xxx.xxx.R;
import xxx.xxx.xxx.common.bean.SysMsgInfo;
import xxx.xxx.xxx.common.listener.SysMsgOnItemClickListener;

import java.util.List;

/**
 * Created by Hello我的World on 2017/4/20.
 */

public class SysMsgListAdapter extends RecyclerView.Adapter<ViewHolder> {

    Context context;
    List<SysMsgInfo> data;//这个list里的对象,是自己写的数据databean,按自己实际需求实现
    SysMsgOnItemClickListener onItemClickListener;//这个监听器是自己写的接口,就一个方法,让item收到click事件时可以回调

    public SysMsgListAdapter(Context context,List<SysMsgInfo>data,SysMsgOnItemClickListener listener){
        this.context = context;
        this.data = data;
        this.onItemClickListener = listener;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.sysmsg_item, parent, false);
        return new ViewHolder(view,onItemClickListener,data);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.setContent(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    public List<SysMsgInfo> getData() {
        return data;
    }
}
使用:
package xxx.xxx.xxx.common.activity;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import com.baoyz.widget.PullRefreshLayout;
import xxx.xxx.xxx.R;
import xxx.xxx.xxx.base.BaseActivity;
import xxx.xxx.xxx.base.utils.CommonUtil;
import xxx.xxx.xxx.common.adapter.HeaderViewRecyclerAdapter;
import xxx.xxx.xxx.common.adapter.SysMsgListAdapter;
import xxx.xxx.xxx.common.bean.GetSysMessageListReturnInfo;
import xxx.xxx.xxx.common.contract.SysMsgListContract;
import xxx.xxx.xxx.common.listener.SysMsgListScrollListener;
import xxx.xxx.xxx.common.presenter.SysMsgListPresenter;

import butterknife.BindView;
import butterknife.ButterKnife;

public class SysMsgListActivity extends BaseActivity implements SysMsgListContract.View {
    @BindView(R.id.pullRefreshLayout)
    PullRefreshLayout pullRefreshLayout;
    @BindView(R.id.tv_title)
    TextView tv_title;
    @BindView(R.id.listView)
    RecyclerView listView;

    SysMsgListContract.Presenter presenter;
    HeaderViewRecyclerAdapter hAdapter;
    SysMsgListAdapter adapter;
    View footer;
    int pageIndex = 1;
    int pageSize = 20;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sys_msg_list);
        ButterKnife.bind(this);
        tv_title.setText("系统消息中心");
        //这是这个activity的presenter,loadSysMsgList方法是加载数据的方法
        presenter = new SysMsgListPresenter(this);
        presenter.loadSysMsgList(pageIndex, pageSize);
        //布局管理器
        LinearLayoutManager l = new LinearLayoutManager(this);
        listView.setLayoutManager(l);
        listView.addOnScrollListener(new SysMsgListScrollListener(l) {
            @Override
            public void onLoadMore() {
                //这个是RecyclerView的滑动监听,如果滑动到最后  就重新加载,先把footerView显示出来
                if(footer!=null) footer.setVisibility(View.VISIBLE);
                //执行加载
                presenter.loadSysMsgList(pageIndex, pageSize);
            }
        });
        pullRefreshLayout.setOnRefreshListener(() -> {
            //下拉刷新操作,直接将pageIndex还原
            pageIndex = 1;
            //如果adapter有数据,直接清掉,并且将“网上找来的adapter”更新
            if (adapter != null) {
                adapter.getData().clear();
                if (hAdapter != null) {
                    hAdapter.notifyDataSetChanged();
                }
            }
            //加载数据
            presenter.loadSysMsgList(pageIndex, pageSize);
        });
    }

    @Override
    public void showProgress(String message) {

    }

    @Override
    public void dismissProgress() {

    }
    //下边这个方法是Adapter的关键代码段
    @Override
    public void showMsgList(GetSysMessageListReturnInfo data, int pageIndex) {
        pullRefreshLayout.setRefreshing(false);//数据返回后,将下拉刷新状态设置为false
        if (data.getMessageList().size() == 0) return;//如果数据为0就不用显示了
        this.pageIndex = pageIndex;//分页加载的页码
        //如果adapter为空,就新建adapter,如果不为空代表之前有过加载,直接在adapter的数组上addAll好了
        if (adapter == null) {
            adapter = new SysMsgListAdapter(this, data.getMessageList(), id -> {
                //这个就是SysMsgOnItemClickListener的回调,然后做出自己需要的逻辑处理即可 
                Intent intent = new Intent(SysMsgListActivity.this,SysMsgDetailActivity.class);
                intent.putExtra("MsgId",id);
                startActivity(intent);
            });
        } else {
            adapter.getData().addAll(data.getMessageList());
            if (hAdapter != null) hAdapter.notifyDataSetChanged();
        }
        //如果“在网上找的Adapter”为空,就新创建一个,新建对象时将我们自己展示数据的adapter传进去
        if (hAdapter == null) {
            hAdapter = new HeaderViewRecyclerAdapter(adapter);
            listView.setAdapter(hAdapter);
            //isLastPage==0代表不是最后一页,isLastPage==1代表最后一页
            if (data.getIsLastPage() != 1) {
                //如果加载返回的数据中isLastPage表示不是最后一页,就要添加footerView
                footer = LayoutInflater.from(this).inflate(R.layout.load_more_footer, listView, false);
                hAdapter.addFooterView(footer);
            }
        }
        //数据加载完成时将footerView隐藏
        if (footer != null) footer.setVisibility(View.GONE);
    }

    @Override
    public void loadDone() {
        //加载到最后数据,当服务器返回已经没有数据了,presenter中会回调这个方法,在这里将footerView隐藏掉
        CommonUtil.showToast("数据已加载到最后...");
        if (footer != null) footer.setVisibility(View.GONE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.onDestroy();
    }


}

实现思路:

  • Activity的布局(要包含PullRefreshLayout和RecyclerView)
    这里用的是com.baoyz.widget.PullRefreshLayout,然后这个布局中包一个RecyclerView
  • 创建footerView的布局
  • 创建adapter加载数据要用到的item
  • 要有一个可以加footerView的adapter,网上找的HeaderViewRecyclerAdapter 点击这里看这个adapter的源码
  • 实现onItemClick的监听器(其实就是个接口,在item监听到点击着自己了就回调这个接口的方法)
  • 要知道RecyclerView滑动了没呀,要根据滑动状态判断加载不加载啊(onScrollListener RecyclerView滑动监听,ps.这个监听的源码地址找不到了)
  • 要有一个ViewHolder
  • 要有一个放数据的Adapter
  • Activity里的一系列使用

以上是笔记,其实去年已经写过类似需求,也找到一套方法,没有记笔记,现在用到时发现全忘了,重新找资料看的,所以这次一定要记录好备用!
BTW:方法千千万,还有很多更简洁更快的方法----->不信就去看啊
再看啊

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

推荐阅读更多精彩内容