RecyclerView从入门到入神—为RecyclerView添加下拉刷新和上拉加载更多(三)

cover.png

上一篇 讲解了为RecylerView添加HeaderView, FooterView, EmptyView, 以及完成了对GloriousRecyclerView的初步封装

本篇我们将继续尝试为RecyclerView添加下拉刷新上拉加载更多

一、为RecyclerView添加下拉刷新

对于已经了解SwipeRefreshLayout的同学请直接跳至 "二、为RecyclerView添加上拉加载更多"

添加下拉刷新其实很简单,android.support.v4.widget.SwipeRefreshLayout已经很好用了,使用起来也比较方便

但是有个缺点,就是滑动到下面的时候,有时不能再滑上去,而是触发了下拉刷新

不过没关系,我们可以自己继承SwipeRefreshLayout,然后重写canChildScrollUp()方法即可


public class ScrollChildSwipeRefreshLayout extends SwipeRefreshLayout {

    private View mScrollUpChild;

    public ScrollChildSwipeRefreshLayout(Context context) {
        super(context);
    }

    public ScrollChildSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean canChildScrollUp() {
        if (mScrollUpChild != null) {
            return ViewCompat.canScrollVertically(mScrollUpChild, -1);
        }
        return super.canChildScrollUp();
    }

    public void setScrollUpChild(View view) {
        mScrollUpChild = view;
    }
}

集成

我们还是用GloriousRecyclerView为例

1.xml中加入


<com.xpc.gloriousrecyclerviewdemo.ScrollChildSwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.xpc.gloriousrecyclerview.GloriousRecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</com.xpc.gloriousrecyclerviewdemo.ScrollChildSwipeRefreshLayout>

2.Actiivty中加入

protected void onCreate(Bundle savedInstanceState) {
    //...
    mRecyclerView = (GloriousRecyclerView) findViewById(R.id.recycler_view);
    mSwipeRefreshLayout = (ScrollChildSwipeRefreshLayout) findViewById(R.id.refresh_layout);
    mSwipeRefreshLayout.setScrollUpChild(mRecyclerView);
    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            //这里做刷新的操作
            //然后调用下面这句话告诉SwipeRefreshLayout已经加载完毕
            mSwipeRefreshLayout.setRefreshing(false);
        }
    });
    //...
}

效果展示

Pull2Refresh.gif

二、为RecyclerView添加上拉加载更多

Step 1. 添加 "加载更多" 的布局

由上一篇为RecyclerView添加HeaderView, FooterView和EmptyView我们已经知道,要为RecyclerView添加额外的childView需要在Adapter中定义指定的viewType, 所以我们这里添加下拉加载更多的原理是一样的,如果没看过的话请查看前面一篇文章,这里我就不在赘述了

Step 2. 判断是否滑到了当前数据的最后一条

这里,我们只是在RecylerView中加了一个childView, 但是我们要得效果并没有达到,我们还需要滑动到最后一条数据的时候,触发加载更多的操作

查看RecyclerView的源码发现有个滑动的事件监听

/**
 * Add a listener that will be notified of any changes in scroll state or position.
 */
public void addOnScrollListener(OnScrollListener listener) {
    //...
}

/**
* An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
* has occurred on that RecyclerView.
*/
public abstract static class OnScrollListener {
    
    public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
    
    public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
    
}

所以我们可以继承OnScrollListener

然后调用RecyclerViewaddOnScrollListener(OnScrollListener listener)方法

public class GloriousRecyclerView extends RecyclerView {
    //...
    
    private void init(){
         this.addOnScrollListener(mOnScrollListener);
    }

    private OnScrollListener mOnScrollListener = new OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //在这里判段是否滑动到了最后一条数据
            /**
            * LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager都提供了一个叫做
            * public int findLastVisibleItemPositions()的方法
            * 这个方法会返回当前可见的最后一条Item的 position
            * 当 position == 我们的真实数据的长度-1时即表示滑到了最后
            * 这时我们就可以触发上拉加载更多了
            */
        }
    };
    
    private int findLastVisibleItemPosition() {
        int position;
        if (getLayoutManager() instanceof LinearLayoutManager) {
            position = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
        } else if (getLayoutManager() instanceof GridLayoutManager) {
            position = ((GridLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
        } else if (getLayoutManager() instanceof StaggeredGridLayoutManager) {
            StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) getLayoutManager();
            int[] lastPositions = layoutManager.findLastVisibleItemPositions(new int[layoutManager.getSpanCount()]);
            position = findMaxPosition(lastPositions);
        } else {
            position = getLayoutManager().getItemCount() - 1;
        }
        return position;
    }
    
    //...
}

效果展示

GloriousRecyclerView1.gif
GloriousRecyclerView2.gif

三、完整源码

我已将完整代码及Demo上传至GitHub,查看完整源码请移步至

https://github.com/titanchen2000/GloriousRecyclerView

四、GloriousRecyclerView主要功能点

  • addHeaderView //添加HeaderView
  • addFooterView //添加FooterView
  • setEmptyView //设置EmptyView
  • setLoadMoreListener //下拉加载更多
  • removeHeaderView //移除HeaderView
  • removeFooterView //移除FooterView
  • "Cumstomzie LoadMoreView" //客制化加载更多, 这里通过layout xml配置

五、快速集成GloriousRecyclerView

我已经将GloriousRecyclerView发布至jcenter仓库

Step 1. 添加依赖

Gradle

dependencies {
    compile 'com.xpc:gloriousrecyclerview:0.2.5@aar'
    compile 'com.android.support:recyclerview-v7:24.2.0'
}

Maven

<dependency>
  <groupId>com.xpc</groupId>
  <artifactId>gloriousrecyclerview</artifactId>
  <version>0.2.5</version>
  <type>pom</type>
</dependency>

Step 2. 布局中加入

<com.xpc.gloriousrecyclerview.GloriousRecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

或者像下面这样客制化加载更多

<com.xpc.gloriousrecyclerview.GloriousRecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:hideNoMoreData="true"
    app:loadAllCompleteText="Load Complete"
    app:loadMoreBackground="#ccc"
    app:loadMoreFailedText="@string/loading_more_failed"
    app:loadMoreIndeterminateDrawable="@drawable/loading_icon_drawable"
    app:loadMoreTextColor="#0080ff"
    app:loadMoreTextSize="14sp"
    app:loadingMoreText="Loading More, Pls wait"/>

hideNoMoreData: Hide the LoadMoreView When no more data, default is true
loadMoreIndeterminateDrawable: The ProgressBar Indeterminate Drawable of LoadMoreView

六、总结

本篇主要讲解了如何为RecyclerView添加添加下拉刷新和上拉加载更多,顺便告知了开源到GitHub的地址和jcenter的引用

世上无难事,只怕有心人,不要停留在只是想想的地步,要勇敢地迈出第一步,你会发现另一片天空

七、下篇预告

ExpandableRecylerView ,可分组显示和折叠的RecylerView

附录:GloriousRecyclerView.java完整代码

/**
 * A full function RecyclerView integration of Header, Footer,EmptyView and Up Swipe To Load More
 *
 * @author cxp
 * @version 0.2.5
 * @see <a href="https://github.com/titanchen2000/GloriousRecyclerView">Source code in GitHub</a>
 */
public class GloriousRecyclerView extends RecyclerView {

    private View mHeaderView;
    private View mFooterView;
    private View mEmptyView;
    private View mLoadMoreView;
    private TextView mTvLoadMore;
    private ProgressBar mPbLoadMore;
    private boolean mIsLoadMoreEnabled;
    private boolean mIsLoadingMore;
    private GloriousAdapter mGloriousAdapter;
    private AutoLoadMoreListener mLoadMoreListener;

    /**
     * Hide the mLoadMoreView When no more data
     */
    private boolean mIsHideNoMoreData;

    private float mLoadMoreTextSize;
    private int mLoadMoreTextColor;
    private int mLoadMoreBackgroundColor;
    private String mLoadingMoreText;
    private String mLoadMoreFailedText;
    private String mLoadAllCompleteText;
    private int mLastPbLoadMoreVisibility;
    private String mLastPbTvLoadMoreText;

    /**
     * The ProgressBar IndeterminateDrawable of mLoadMoreView
     */
    private Drawable mLoadMorePbIndeterminateDrawable;

    private OnScrollListener mOnScrollListener = new OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if (mIsLoadMoreEnabled && !mIsLoadingMore && dy > 0) {
                if (findLastVisibleItemPosition() == mGloriousAdapter.getItemCount() - 1) {
                    mIsLoadingMore = true;
                    mLoadMoreListener.onLoadMore();
                }
            }
        }
    };

    public GloriousRecyclerView(Context context) {
        this(context, null);
    }

    public GloriousRecyclerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GloriousRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        /*
         *  Add the customize of mLoadMoreView from layout
         */
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GloriousRecyclerView);
        mIsHideNoMoreData = a.getBoolean(R.styleable.GloriousRecyclerView_hideNoMoreData, false);
        mLoadMoreTextColor = a.getColor(R.styleable.GloriousRecyclerView_loadMoreTextColor, 0xff888888);
        mLoadMoreTextSize = a.getDimensionPixelSize(R.styleable.GloriousRecyclerView_loadMoreTextSize, getResources()
                .getDimensionPixelSize(R.dimen.load_more_text_size));
        mLoadMoreBackgroundColor = a.getColor(R.styleable.GloriousRecyclerView_loadMoreBackground, 0xffffffff);
        int indeterminateDrawableResId = a.getResourceId(R.styleable
                .GloriousRecyclerView_loadMoreIndeterminateDrawable, 0);
        if (indeterminateDrawableResId != 0) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mLoadMorePbIndeterminateDrawable = getResources().getDrawable(indeterminateDrawableResId, context
                        .getTheme());
            } else {
                //noinspection deprecation
                mLoadMorePbIndeterminateDrawable = getResources().getDrawable(indeterminateDrawableResId);
            }
        }
        mLoadingMoreText = a.getString(R.styleable.GloriousRecyclerView_loadingMoreText);
        mLoadMoreFailedText = a.getString(R.styleable.GloriousRecyclerView_loadMoreFailedText);
        mLoadAllCompleteText = a.getString(R.styleable.GloriousRecyclerView_loadAllCompleteText);
        if (TextUtils.isEmpty(mLoadingMoreText)) mLoadingMoreText = getResources().getString(R.string.glorious_recyclerview_loading_more);
        if (TextUtils.isEmpty(mLoadMoreFailedText)) mLoadMoreFailedText = getResources().getString(R.string.glorious_recyclerview_load_more_failed);
        if (TextUtils.isEmpty(mLoadAllCompleteText)) mLoadAllCompleteText = getResources().getString(R.string.glorious_recyclerview_no_more_data);
        a.recycle();
    }

    public interface AutoLoadMoreListener {
        void onLoadMore();
    }

    /**
     * Add the {@link GloriousRecyclerView} headerView
     *
     * @param view headerView
     */
    public void addHeaderView(View view) {
        if (view != null && mHeaderView == null) {
            mHeaderView = view;
            mGloriousAdapter.notifyItemInserted(0);
        }
    }

    /**
     * Add the {@link GloriousRecyclerView} footerView
     *
     * @param view footerView
     */
    public void addFooterView(View view) {
        if (view != null && mFooterView == null) {
            mFooterView = view;
            mGloriousAdapter.notifyItemInserted(mGloriousAdapter.getItemCount() - 1);
        }
    }

    /**
     * Remove the {@link GloriousRecyclerView} headerView
     */
    public void removeHeaderView() {
        if (mHeaderView != null) {
            mHeaderView = null;
            mGloriousAdapter.notifyItemRemoved(0);
        }
    }

    /**
     * Remove the {@link GloriousRecyclerView} footerView
     */
    public void removeFooterView() {
        if (mFooterView != null) {
            mFooterView = null;
            mGloriousAdapter.notifyItemRemoved(mGloriousAdapter.getItemCount() - 1);
        }
    }

    /**
     * Add the {@link GloriousRecyclerView} emptyView
     *
     * @param view emptyView
     */
    public void setEmptyView(View view) {
        mEmptyView = view;
        mGloriousAdapter.notifyDataSetChanged();
    }

    /**
     * Called this also means that loadMore enabled
     *
     * @param loadMoreListener loadMoreListener
     */
    public void setLoadMoreListener(final AutoLoadMoreListener loadMoreListener) {
        if (null != loadMoreListener) {
            mLoadMoreListener = loadMoreListener;
            mIsLoadMoreEnabled = true;
            this.addOnScrollListener(mOnScrollListener);
        }
    }

    /**
     * @param adapter the adapter as normal RecyclerView you used {@link RecyclerView#setAdapter(Adapter)}.
     * @see GloriousAdapter
     */
    @Override
    public void setAdapter(Adapter adapter) {
        if (adapter != null) {
            mGloriousAdapter = new GloriousAdapter(adapter);
        }
        super.setAdapter(mGloriousAdapter);
    }

    /**
     * Called when load more data failed
     *
     * @see #notifyLoadMoreFinish(boolean, boolean)
     */
    public void notifyLoadMoreFailed() {
        notifyLoadMoreFinish(false, true);
    }

    /**
     * Called when load more data successful
     *
     * @see #notifyLoadMoreFinish(boolean, boolean)
     */
    public void notifyLoadMoreSuccessful(boolean hasMore) {
        notifyLoadMoreFinish(true, hasMore);
    }

    /**
     * Notify mLoadMoreView do some UI change
     *
     * @param success Is load more data successful
     * @param hasMore Whether has more data to be loaded
     * @see #setLoadMoreListener(AutoLoadMoreListener)
     */
    private void notifyLoadMoreFinish(boolean success, boolean hasMore) {
        this.clearOnScrollListeners();
        mIsLoadingMore = false;
        if (success) {
            mGloriousAdapter.notifyDataSetChanged();
            if (hasMore) {
                mPbLoadMore.setVisibility(VISIBLE);
                mTvLoadMore.setText(mLoadingMoreText);
                this.addOnScrollListener(mOnScrollListener);
            } else {
                if (mIsHideNoMoreData) {
                    //the mLoadMoreView will GONE when no more data to be loaded
                    mIsLoadMoreEnabled = false;
                } else {
                    //the mLoadMoreView will display "No More Data" when no more data to be loaded
                    //and the progressBar will GONE
                    mLoadMoreView.setOnClickListener(null);
                    mPbLoadMore.setVisibility(GONE);
                    mTvLoadMore.setText(mLoadAllCompleteText);
                }
            }
        } else {
            //the mLoadMoreView will display "Loading Failed, Click To Reload" when load more data failed
            //and the progressBar will GONE
            mTvLoadMore.setText(mLoadMoreFailedText);
            mPbLoadMore.setVisibility(GONE);
        }
        mLastPbLoadMoreVisibility = mPbLoadMore.getVisibility();
        mLastPbTvLoadMoreText = mTvLoadMore.getText().toString();
    }

    /**
     * Find the last visible position depends on LayoutManger
     *
     * @return the last visible position
     * @see #setLoadMoreListener(AutoLoadMoreListener)
     */
    private int findLastVisibleItemPosition() {
        int position;
        if (getLayoutManager() instanceof LinearLayoutManager) {
            position = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
        } else if (getLayoutManager() instanceof GridLayoutManager) {
            position = ((GridLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
        } else if (getLayoutManager() instanceof StaggeredGridLayoutManager) {
            StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) getLayoutManager();
            int[] lastPositions = layoutManager.findLastVisibleItemPositions(new int[layoutManager.getSpanCount()]);
            position = findMaxPosition(lastPositions);
        } else {
            position = getLayoutManager().getItemCount() - 1;
        }
        return position;
    }

    /**
     * Find StaggeredGridLayoutManager the last visible position
     *
     * @see #findLastVisibleItemPosition()
     */
    private int findMaxPosition(int[] positions) {
        int maxPosition = 0;
        for (int position : positions) {
            maxPosition = Math.max(maxPosition, position);
        }
        return maxPosition;
    }

    /**
     * The decoration Adapter
     */
    private class GloriousAdapter extends RecyclerView.Adapter<ViewHolder> {

        private Adapter mOriginalAdapter;
        private int ITEM_TYPE_NORMAL = 0;
        private int ITEM_TYPE_HEADER = 1;
        private int ITEM_TYPE_FOOTER = 2;
        private int ITEM_TYPE_EMPTY = 3;
        private int ITEM_TYPE_LOAD_MORE = 4;

        public GloriousAdapter(Adapter originalAdapter) {
            mOriginalAdapter = originalAdapter;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (viewType == ITEM_TYPE_HEADER) {
                return new GloriousViewHolder(mHeaderView);
            } else if (viewType == ITEM_TYPE_EMPTY) {
                return new GloriousViewHolder(mEmptyView);
            } else if (viewType == ITEM_TYPE_FOOTER) {
                return new GloriousViewHolder(mFooterView);
            } else if (viewType == ITEM_TYPE_LOAD_MORE) {
                mLoadMoreView = LayoutInflater.from(getContext()).inflate(R.layout
                        .glorious_recyclerview_layout_load_more, parent, false);
                /*
                 *  Customize the mLoadMoreView
                 */
                mLoadMoreView.setBackgroundColor(mLoadMoreBackgroundColor);
                mTvLoadMore = (TextView) mLoadMoreView.findViewById(R.id.tv_loading_more);
                mPbLoadMore = (ProgressBar) mLoadMoreView.findViewById(R.id.pb_loading_more);
                if (null != mLoadMorePbIndeterminateDrawable) {
                    mPbLoadMore.setIndeterminateDrawable(mLoadMorePbIndeterminateDrawable);
                }
                mTvLoadMore.getPaint().setTextSize(mLoadMoreTextSize);
                mTvLoadMore.setTextColor(mLoadMoreTextColor);
                mTvLoadMore.setText(mLoadingMoreText);

                //Add reload strategy if load more failed
                mLoadMoreView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (!mIsLoadingMore) {
                            mIsLoadingMore = true;
                            mPbLoadMore.setVisibility(VISIBLE);
                            mTvLoadMore.setText(mLoadingMoreText);
                            mTvLoadMore.setVisibility(VISIBLE);
                            mLoadMoreListener.onLoadMore();
                        }
                    }
                });

                //when remove footerView will trigger onCreateViewHolder()
                if (!TextUtils.isEmpty(mLastPbTvLoadMoreText)) {
                    mTvLoadMore.setText(mLastPbTvLoadMoreText);
                    mPbLoadMore.setVisibility(mLastPbLoadMoreVisibility);
                }

                return new GloriousViewHolder(mLoadMoreView);
            } else {
                return mOriginalAdapter.onCreateViewHolder(parent, viewType);
            }
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            int type = getItemViewType(position);
            if (type == ITEM_TYPE_HEADER
                    || type == ITEM_TYPE_FOOTER
                    || type == ITEM_TYPE_EMPTY
                    || type == ITEM_TYPE_LOAD_MORE) {
                return;
            }
            int realPosition = getRealItemPosition(position);
            mOriginalAdapter.onBindViewHolder(holder, realPosition);
        }

        @Override
        public int getItemCount() {
            //Get the real data counts
            int itemCount = mOriginalAdapter.getItemCount();
            /*
             * Add the extra views counts
             */
            if (null != mHeaderView) itemCount++;
            if (null != mFooterView) itemCount++;
            if (null != mEmptyView && itemCount == 0) {
                //if the real data is empty, do not show loadMore any way
                itemCount++;
                return itemCount;
            }
            if (mIsLoadMoreEnabled) itemCount++;
            return itemCount;
        }

        @Override
        public int getItemViewType(int position) {
            /*
             * The sequence is very important, do not change or you will get wrong type
             */
            if (null != mHeaderView && position == 0) return ITEM_TYPE_HEADER;
            if (null != mFooterView && position == getItemCount() - 1) return ITEM_TYPE_FOOTER;
            //if the real data is empty, do not show loadMore any way
            if (null != mEmptyView && mOriginalAdapter.getItemCount() == 0) {
                return ITEM_TYPE_EMPTY;
            } else if (mIsLoadMoreEnabled && position == getLoadMorePosition()) {
                return ITEM_TYPE_LOAD_MORE;
            }
            return ITEM_TYPE_NORMAL;
        }

        private int getRealItemPosition(int position) {
            if (null != mHeaderView) {
                return position - 1;
            }
            return position;
        }

        /**
         * Get the loadMore position,
         * <p>
         * if mFooterView is null , loadMore position will display at the end,
         * or it will display at the last but one
         *
         * @return the loadMore position
         */
        private int getLoadMorePosition() {
            if (null == mFooterView) {
                return getItemCount() - 1;
            } else {
                return getItemCount() - 2;
            }
        }

        /**
         * GloriousViewHolder here is only let the extra views we add own a ViewHolder
         */
        class GloriousViewHolder extends ViewHolder {

            GloriousViewHolder(View itemView) {
                super(itemView);
            }
        }
    }

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

推荐阅读更多精彩内容