Android-RecyclerView侧滑删除和拖拽操作,最底部条目不参与操作+上拉加载更多实现

===滑动拖拽===

Look, ItemTouchHelper | Android Developers

ItemTouchHelper
public class ItemTouchHelper 
extends RecyclerView.ItemDecoration implements RecyclerView.OnChildAttachStateChangeListener

java.lang.Object
   ↳    android.support.v7.widget.RecyclerView.ItemDecoration
       ↳    android.support.v7.widget.helper.ItemTouchHelper

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.

It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

Depending on which functionality you support, you should override onMove(RecyclerView, ViewHolder, ViewHolder) and / or onSwiped(ViewHolder, int).

This class is designed to work with any LayoutManager but for certain situations, it can be optimized for your custom LayoutManager by extending methods in the ItemTouchHelper.Callback class or implementing ItemTouchHelper.ViewDropHandler interface in your LayoutManager.

By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. You can customize these behaviors by overriding onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int, boolean) or onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean).

Most of the time you only need to override onChildDraw.

解释:就是说这是一个支持RecyclerView的滑动删除和拖拽的实体类。

它靠一个回调来实现,也就是ItemTouchHelper.Callback | Android Developers

基于这个支持,你还需要实现[onMove(RecyclerView, ViewHolder, ViewHolder)](https://link.zhihu.com/?target=https%3A//developer.android.google.cn/reference/android/support/v7/widget/helper/ItemTouchHelper.Callback.html%3Fhl%3Dzh-cn%23onMove%28android.support.v7.widget.RecyclerView%2C%2520android.support.v7.widget.RecyclerView.ViewHolder%2C%2520android.support.v7.widget.RecyclerView.ViewHolder%29)and / or[onSwiped(ViewHolder, int)](https://link.zhihu.com/?target=https%3A//developer.android.google.cn/reference/android/support/v7/widget/helper/ItemTouchHelper.Callback.html%3Fhl%3Dzh-cn%23onSwiped%28android.support.v7.widget.RecyclerView.ViewHolder%2C%2520int%29) 方法。

这个类还支持特定解决方案的所有LayoutManager,但是需要你继承ItemTouchHelper.Callback或者实现ItemTouchHelper.ViewDropHandler 接口。

另外你通过重写onChildDraw 方法可以实现移动属性的自定义。

1. 所以创建一个这样的实体对象就是像这样:

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
            @Override
            public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
                return 0;
            }

            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {

            }
        }); 

2. 调用就是如下的方法咯, attach一下RecyclerView就可以啦...

  void  attachToRecyclerView(RecyclerView recyclerView)
Attaches the ItemTouchHelper to the provided RecyclerView.

还有其他方法如下,先不管嘛,慢慢来

image

Look2, 所以重点来了,就是这个 ItemTouchHelper.Callback | Android Developers 如果不想自定义,其实有个官方简单版ItemTouchHelper.SimpleCallback | Android Developers<u style="text-decoration: none; border-bottom: 1px dashed grey;"> 这个可能就不能针对某个条目单独处理了咯....</u>

不过人家已经把移动操作弄好了。另外滑动效果也处理了,你只需要将滑动的条目从adapter中删除即可!参数就是滑动的方向和拖拽的方法处理(比如只能向上拖动,左滑右滑啥的),如下:

     ItemTouchHelper itemTouchHelper2 = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
                ItemTouchHelper.LEFT) {
            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                return true;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
                // remove from adapter
            }
        });

Then,如果我们希望列表类似如下:(底部的条目不能被滑动删除,也不能被移动,同时也不能被拖动的条目改变位置...)

image

1. 这个时候就需要我们自定义ItemTouchHelper.Callback | Android Developers

然后重点重写如下三个方法,基本上就可以进行相关控制了。当然有些也可以简单重写,比如isLongPressDragEnabled()这些,可以控制状态。

 abstract int   getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)

  abstract boolean  onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
Called when ItemTouchHelper wants to move the dragged item from its old position to the new position.

abstract void   onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
Called when a ViewHolder is swiped by the user.

2. 我们为了控制最后的条目不能操作的情况,我们重点关注下getMovementFlags方法

getMovementFlags
added in version 24.1.0
int getMovementFlags (RecyclerView recyclerView, 
                RecyclerView.ViewHolder viewHolder)
Should return a composite flag which defines the enabled move directions in each state (idle, swiping, dragging).

Instead of composing this flag manually, you can use makeMovementFlags(int, int) or makeFlag(int, int).

This flag is composed of 3 sets of 8 bits, where first 8 bits are for IDLE state, next 8 bits are for SWIPE state and third 8 bits are for DRAG state. Each 8 bit sections can be constructed by simply OR'ing direction flags defined in ItemTouchHelper.

For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to swipe by swiping RIGHT, you can return:

      makeFlag(ACTION_STATE_IDLE, RIGHT) | makeFlag(ACTION_STATE_SWIPE, LEFT | RIGHT);

This means, allow right movement while IDLE and allow right and left movement while swiping.

这个属性返回值就意味着你对该条目的操作状态,比如我们获取当前位置是最后一个条目,进行如下处理:(mList.size()需要你外部传入链表mList哟)

  @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        //int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        //int swipeFlags = ItemTouchHelper.LEFT;
        int[] flags;
        if (viewHolder.getLayoutPosition() == (mList.size() - 1)){
            flags =  new int[]{0, 0};
        }else{
            flags =  new int[]{ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT};
        }
        return makeMovementFlags(flags[0], flags[1]);
    }         

也就是说不管是滑动还是拖拽,只要是最后一个条目,那么标志都为0,也就是什么都不能干!其他情况,正常左滑,拖拽即可!

当然其实我们可以搞一个回调来返回想要的处理,如果说很多页面都是同样的操作,倒是可以再次封装一下,最后我们贴上我的自定义:

SimpleItemTouchHelperCallback.java

import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;

/*
 *@Description: 侧滑删除辅助类
 *@Author: hl
 *@Time: 2019/1/4 15:52
 */
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
    private Sthc_Movement sthc_movement;

    public SimpleItemTouchHelperCallback(Sthc_Movement sthc_movement){
        this.sthc_movement = sthc_movement;
    }

    /**
     * 控制每个条目的可操作状态 - 滑动,拖拽等  来源->getFlag
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        //int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        //int swipeFlags = ItemTouchHelper.LEFT;
        int[] flags = sthc_movement.getFlag(viewHolder.getLayoutPosition());
        return makeMovementFlags(flags[0], flags[1]);
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
        sthc_movement.onMove(viewHolder, target);
        return true;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
        sthc_movement.onSwiped(viewHolder, i);
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                            float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);
            viewHolder.itemView.setTranslationX(dX);
        }
    }

    public interface Sthc_Movement{
        public void onMove(RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target);
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction);
        public int[] getFlag(int postion);
    }
}

LookEnd,具体使用如下(注意移动条目交换的操作就行)

    ///< 侧滑删除和拖拽排序
        SimpleItemTouchHelper itemTouchHelper = new SimpleItemTouchHelper(new SimpleItemTouchHelperCallback(new SimpleItemTouchHelperCallback.Sthc_Movement() {
            @Override
            public void onMove(RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                int fromPosition = viewHolder.getAdapterPosition();
                int toPosition = target.getAdapterPosition();
                ///< 禁止拖动到新增菜单的底部
                if (toPosition >= (mList.size() - 1)){
                    return;
                }
                if (fromPosition < toPosition) {
                    for (int i = fromPosition; i < toPosition; i++) {
                        Collections.swap(mList, i, i + 1);
                    }
                } else {
                    for (int i = fromPosition; i > toPosition; i--) {
                        Collections.swap(mList, i, i - 1);
                    }
                }
                baseAdapter.notifyItemMoved(fromPosition, toPosition);
            }

            @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                int position = viewHolder.getAdapterPosition();
                mList.remove(position);
                baseAdapter.notifyItemRemoved(position);
            }

            /**
             * 控制每个条目的可操作状态 - 滑动,拖拽等  对应->getMovementFlags
             * @param position
             * @return
             */
            @Override
            public int[] getFlag(int position) {
                if (position == (mList.size() - 1)){
                    return new int[]{0, 0};
                }else{
                    return new int[]{ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT};
                }
            }
        }));
        itemTouchHelper.attachToRecyclerView(dailyItemsRv);

其中注意不允许其他条目在底部菜单下面,所以我增加了如下判断:

image

基本上简单需求是满足了。对了,1. onChildDrawif (actionState == ItemTouchHelper.ACTION_STATE_SWIPE**) 的处理,是滑动过程alpha透明度的变化。滑动慢点可以看到效果。。。

2. SimpleItemTouchHelper没什么东东,就简单继承了下ItemTouchHelper | Android Developers 后面或许还可以增加额外自定义处理。

这个先简单这样认识下,毕竟小萌新都没接触过,有时候就是需要项目多实战。把常用的都搞搞,然后深入,然后源码剖析,这样应该才能掌握的更好!

===上拉加载更多===

说起这个,一般小萌新都是搬砖的,只会用人家的框架 - 下拉刷新,上拉加载更多!我们来看看官方的下拉刷新SwipeRefreshLayout | Android Developers 这个简单入门使用还好,先不搞特别复杂的效果:

        // 设置颜色属性的时候一定要注意是引用了资源文件还是直接设置16进制的颜色,因为都是int值容易搞混
        // 设置下拉进度的背景颜色,默认就是白色的
        dailyHisItemsSwr.setProgressBackgroundColorSchemeResource(android.R.color.white);
        // 设置下拉进度的主题颜色
        dailyHisItemsSwr.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark);
        // 下拉时触发SwipeRefreshLayout的下拉动画,动画完毕之后就会回调这个方法
        dailyHisItemsSwr.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                // 开始刷新,设置当前为刷新状态
                //swipeRefreshLayout.setRefreshing(true);
                //                if (startload){
                //                    return;
                //                }

                // 模拟下: 
                //  这里是主线程
                // 一些比较耗时的操作,比如联网获取数据,需要放到子线程去执行
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //  加载数据
                        //  刷新数据 baseAdapter.notifyDataSetChanged();
                        //  加载完数据设置为不刷新状态,将下拉进度收起来
                        dailyHisItemsSwr.setRefreshing(false);
                    }
                }, 500);

                // 这个不能写在外边,不然会直接收起来
                //swipeRefreshLayout.setRefreshing(false);
            }
        });

基本就能看到下拉刷新的效果啦....

然后接着我们快速入门下上拉加载更多吧,这个有点点麻烦,另外如果SwipeRefreshLayout + RecyclerView实现下拉刷新/上拉加载更多 - 可能还会遇到很多问题需要处理(比如下拉时禁止上拉,上拉加载过程中禁止下拉刷新,还有就是上拉加载触发的条件-当前如果没有数据,可能就不能上拉加载了,可能就是全屏显示无数据,点击刷新获取了, 总之要实现一个效果体验好的上拉加载/下拉刷新框架不是这么容易的)。 从目前小萌新的感觉来看,实现上拉加载更多动画,上拉停止动画,回弹,这个过程是需要监听持续touch事件的,也就是说单纯的靠监听RecyclerView的addOnScrollListener貌似不太行,必须有持续监听。 后面要专门搞搞这个上拉加载更多的效果....

说这么多,开始吧...**上拉加载更多。 **目前的方案基本都是监听RecyclerView的滚动,然后判断是上拉加载更多的话,则Adapter底部条目对应显示加载更多,以及还可以扩展“没有更多了”等效果。

1. 先看下Adapter的扩展处理吧 - 看关键点就可以了!每个人的适配器不同!

1.1 首先就是如果是需要展示底部加载状态的情况下,返回的item个数多一个

image

1.2 然后就是getItemViewType返回不同的类型

image

1.3 接着就是布局加载以及数据绑定,小萌新是分别搞了多个不同状态的布局。绑定数据的时候就处理正常条目数据即可,加载状态的布局不做绑定操作!!

image

1.4 额外提供一些数据刷新以及状态判断的方法

/**
     * 添加更多数据
     * @param _baseMulDataModelList
     */
    public void addMoreItem(List<BaseDataModel> _baseMulDataModelList) {
        state = STATE.HIDE;
        baseMulDataModelList.addAll(_baseMulDataModelList);
        notifyDataSetChanged();
    }

    /**
     * 开始加载
     */
    public void startLoad() {
        state = STATE.START_LOAD;
        notifyDataSetChanged();
    }

    /**
     * 加载中
     */
    public void startLoading() {
        state = STATE.LOADING;
        notifyDataSetChanged();
    }

    /**
     * 加载结束,无更多数据了
     */
    public void finishNoMoreData() {
        state = STATE.NO_MOREDATA;
        notifyDataSetChanged();
    }

    /**
     * 是否开始加载了
     * @return
     */
    public boolean bIsStart(){
        return state == STATE.START_LOAD;
    }

    /**
     * 是否正在加载
     * @return
     */
    public boolean bIsLoading(){
        return state == STATE.LOADING || state == STATE.NO_MOREDATA;
    }

    public boolean bJustLoading(){
        return state == STATE.LOADING;
    }

2. 以上就基本构建好了。接下来就是我们适配器创建RecyclerView.OnScrollListener | Android Developers ,上拉加载处理了...

2.1 你网上搜有些“SwipeRefreshLayout和RecyclerView实现下拉刷新上拉加载更多”,基本方案都类似。就是细节处理不太一样。有些是在onScrollStateChanged中请求上拉加载,有些是在onScrolled处理上拉加载,大部分还是在onScrollStateChanged中进行了处理。

2.2 需要注意, 如果你的条目不满一屏的话,上拉加载不会触发onScrolled回调,所以你要注意在onScrolled处理的地方哟!

按小萌新想法,这种情况可以不用触发上拉加载更多,本来就那么多数据,正常的逻辑!哈哈。。。

2.3 所以小萌新的总结是:onScrolled中进行判断是否上拉加载更多,并且满足条件,开启加载;然后当滑动停止时, 在onScrollStateChanged中进行数据加载(需要判断是否开启了加载,这样可以避免不满一屏也上拉的操作;同时还要判断是否正在下拉刷新这些情况....)

看下两个方法和一些属性介绍...

onScrollStateChanged
added in version 22.1.0
void onScrollStateChanged (RecyclerView recyclerView, 
                int newState)
Callback method to be invoked when RecyclerView's scroll state changes.

Parameters
recyclerView    RecyclerView: The RecyclerView whose scroll state has changed.
newState    int: The updated scroll state. One of SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING or SCROLL_STATE_SETTLING.

onScrolled
added in version 22.1.0
void onScrolled (RecyclerView recyclerView, 
                int dx, 
                int dy)
Callback method to be invoked when the RecyclerView has been scrolled. This will be called after the scroll has completed.

This callback will also be called if visible item range changes after a layout calculation. In that case, dx and dy will be 0.

Parameters
recyclerView    RecyclerView: The RecyclerView which scrolled.
dx  int: The amount of horizontal scroll.
dy  int: The amount of vertical scroll.

dy可以用来判断是下滑还是上滑的,目前按照我的逻辑,暂时不需要这个处理,我只需要判断上拉加载更多时,是否达到了底部?- 靠获取底部条目的bottom位置,然后跟RecyclerView做差值,小于10基本就是了。此时就可以开始加载...

   /**初始化界面***/
   private void initView(){
        ///< 上拉加载更多监听
        adh_dailyHisItemsRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                Log.e("test", "newState=" + newState);

                ///< 正在刷新,则直接返回
                if (dailyHisItemsSwr.isRefreshing() || baseAdapter.bIsLoading()) return;

                ///< 加载更多 onScrolled中上拉加载条件满足时进行加载更多的操作??其他优化?
                if (baseAdapter.bIsStart() &&
                        !baseAdapter.bIsLoading() &&
                        newState == RecyclerView.SCROLL_STATE_IDLE) {
                    baseAdapter.startLoading();
                    loadMoreDate();
                }
                //Log.e("test", "startload=" + startload + " newState=" + newState);
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                //lastVisibleItem = layoutManager.findLastVisibleItemPosition();
                //bUpLoad = dy > 0;
                Log.e("test", "dy=" + dy);

                if (baseAdapter.bIsLoading()) return;

                View viewlast = layoutManager.findViewByPosition(baseAdapter.getItemCount() - 1);
                Log.e("test", "viewlast=" + viewlast);
                ///< 当滑动到底部item的时候(此时底部bottom基本就是高度),设置startload = true,展示加载中(可以修改为动画显示)
                if (null != viewlast && (recyclerView.getHeight() - viewlast.getBottom()) < 10) {
                    //Log.e("test", "getHeight=" + recyclerView.getHeight());
                    //Log.e("test", "getTop=" + viewlast.getTop());
                    //Log.e("test", "getBottom=" + viewlast.getBottom());
                    baseAdapter.startLoad();
                }
            }
        });
   }      

   /**
     * 加载更多
     */
    private void loadMoreDate() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                List<BaseDataModel> mListTemp = new ArrayList<>();
                for (int i = 0; i < 20; i++) {
                    DailyHisAdapterItemBean dailyHisAdapterItemBean = new DailyHisAdapterItemBean();
                    dailyHisAdapterItemBean.setDate("2019.11.11");
                    dailyHisAdapterItemBean.setNames("杀猪刀一把/青椒炒苹果/萝卜抄西瓜/萝卜抄西瓜");
                    dailyHisAdapterItemBean.setTotal_price(12.28);
                    dailyHisAdapterItemBean.setTotal_weight(0.5);
                    mListTemp.add(dailyHisAdapterItemBean);
                }
                baseAdapter.addMoreItem(mListTemp);
                baseAdapter.finishNoMoreData();
            }
        }, 2000);
    }

基本就ojbk了。。。

image
image

总之,小萌新又接触了一些知识。这块后面要加强,是需要看下第三方框架源码,然后学习下。 虽然不用总是造轮子,但是还是知道多点比较好吧....

心情好,放松,开心的学习就好 - 小萌新

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

推荐阅读更多精彩内容