仿今日頭條頻道管理實現(xiàn)排序移動

寫在前面:參考YoKey,感謝颤陶。
附上他的鏈接:http://www.reibang.com/p/d30fd8da4eac

主頁

界面我們分成4個viewType,分別是我的頻道頭瘪板,我的頻道內(nèi)容肉迫,其他頻道頭逸吵,其他頻道內(nèi)容宛乃。

    // 我的頻道 標題部分
    public static final int TYPE_MY_CHANNEL_HEADER = 0;
    // 我的頻道
    public static final int TYPE_MY = 1;
    // 其他頻道 標題部分
    public static final int TYPE_OTHER_CHANNEL_HEADER = 2;
    // 其他頻道
    public static final int TYPE_OTHER = 3;
  • 填充假數(shù)據(jù)
       //我的頻道的數(shù)據(jù)
        final List<ChannelData> myItems = new ArrayList<>();
        for (int i = 0 ; i < 20 ; i++){
            ChannelData channelData = new ChannelData();
            channelData.setName("頻道"+i);
            myItems.add(channelData);
        }

        //其他頻道的數(shù)據(jù)
        final List<ChannelData> otherItems = new ArrayList<>();
        for (int i = 0 ; i < 20 ; i++){
            ChannelData channelData = new ChannelData();
            channelData.setName("其他"+i);
            otherItems.add(channelData);
        }
  • 首頁代碼
    開啟網(wǎng)格模式
        //網(wǎng)格模式 4列
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this,4);
        mRecy.setLayoutManager(gridLayoutManager);

使用ItemTouchHelper的大概模型,這里itemTouchHelper就是實現(xiàn)item拖拽和滑動的關(guān)鍵類磷账,new對象的時候里面要放itemTouchHelper.CallBak回調(diào)峭沦。最后通過attachToRecyclerView方法綁定RecyclerView。

        ItemDragHelperCallback callback = new ItemDragHelperCallback();
        final ItemTouchHelper helper = new ItemTouchHelper(callback);//實現(xiàn)item的拖拽和滑動
        helper.attachToRecyclerView(mRecy);//通過attachToRecyclerView方法綁定RecyclerView

new adapter對象

        //adapter對象
        final ChannelAdapter adapter = new ChannelAdapter(this,helper,myItems,otherItems);

我們看看里面的四個參數(shù)逃糟,先看看ChannelAdapter的構(gòu)造方法吼鱼。

public ChannelAdapter(Context context, ItemTouchHelper mItemTouchHelper, List<ChannelData> mMyChannelItems, List<ChannelData> mOtherChannelItems) {
        this.mInflater = LayoutInflater.from(context);
        this.mItemTouchHelper = mItemTouchHelper;
        this.mMyChannelItems = mMyChannelItems;
        this.mOtherChannelItems = mOtherChannelItems;
    }

第一個參數(shù)上下文環(huán)境,第二個參數(shù)就是ItemTouchHelper對象绰咽,第三個參數(shù)是我的頻道item列表菇肃,第四個參數(shù)是其他頻道item列表。
因為4種類型都寫在一個界面里了取募,但是頭要單獨占一行琐谤,非頭的item要幾個(具體數(shù)自己定)才占一行,這時通過網(wǎng)格布局管理器中setSpanSizeLookup方法進行操作了玩敏,這個方法會根據(jù)position來設(shè)置span size 斗忌,這個我們的span count是4 如果是頭我們就給span size 為4 ,內(nèi)容就給1旺聚。具體分析看代碼

 //根據(jù)position來設(shè)置span size 這個我們的span count是4 如果是頭我們就給span size 為4 內(nèi)容就給1
        gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                int viewType = adapter.getItemViewType(position);
                //三元運算 如果是內(nèi)容就給1 是頭我們就給span size 為4
                return viewType == ChannelAdapter.TYPE_MY || viewType == ChannelAdapter.TYPE_OTHER ? 1 : 4;
            }
        });
        mRecy.setAdapter(adapter);

我們看到里面有一個getItemViewType方法织阳,點進去看下

    @Override
    public int getItemViewType(int position) {
        //不同position對應的viewType
        if (position == 0) {
            return TYPE_MY_CHANNEL_HEADER;
        } else if (position == mMyChannelItems.size() + 1) {
            return TYPE_OTHER_CHANNEL_HEADER;
        } else if (position > 0 && position < mMyChannelItems.size() + 1) {
            return TYPE_MY;
        } else {
            return TYPE_OTHER;
        }
    }

哦,原來是一個復寫的方法砰粹,看看RecyclerView里對這個方法的描述

        /**
         * Return the view type of the item at <code>position</code> for the purposes
         * of view recycling.
         *
         * <p>The default implementation of this method returns 0, making the assumption of
         * a single view type for the adapter. Unlike ListView adapters, types need not
         * be contiguous. Consider using id resources to uniquely identify item view types.
         *
         * @param position position to query
         * @return integer value identifying the type of the view needed to represent the item at
         *                 <code>position</code>. Type codes need not be contiguous.
         */
        public int getItemViewType(int position) {
            return 0;
        }

原來是根據(jù)position的位置獲取這個item的viewtype唧躲,那繼續(xù)看我們復寫的那個方法,position==0是那就是我的頻道頭碱璃,我的頻道的item數(shù)量+1就是其他頻道頭弄痹,在這之間就是我的頻道,剩下的就是其他頻道了厘贼,這樣類型就是出來界酒,在往上看,三元運算符嘴秸,意思很明顯,就是當viewType是我的頻道或者其他頻道的時候返回值就為1,否則就為4岳掐,返回值就是span size凭疮。

ItemTouchHelper.Callback類

這里我們寫了一個ItemTouchHelper.Callback的繼承類〈觯看看這類里面一些必須的方法执解。
設(shè)置滑動類型

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags;
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager || manager instanceof StaggeredGridLayoutManager){
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT ;
        }else {
            dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        }

        // 如果想支持滑動(刪除)操作, swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END
        int swipeFlags = 0 ;

        //返回一個整數(shù)類型的標識,用于判斷Item那種移動行為是允許的
        return makeMovementFlags(dragFlags,swipeFlags);
    }

如上代碼纲酗,如果屬于網(wǎng)格類型衰腌,就可以上下左右的拖拽,不然就只能上下拖拽觅赊,滑動的話是禁止的右蕊。返回一個整數(shù)類型的標識,用于判斷Item那種移動行為是允許的吮螺。
拖拽切換Item的回調(diào)

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        if (viewHolder.getItemViewType() != target.getItemViewType()){
            return false;
        }
        //instanceof 這個對象是否是這個特定類或者是它的子類的一個實例饶囚。
        if (recyclerView.getAdapter() instanceof OnItemMoveListener){
            OnItemMoveListener listener = (OnItemMoveListener) recyclerView.getAdapter();
            listener.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
        }
        return true;
    }

如果Item切換了位置,返回true鸠补;反之萝风,返回false。onMove()是在拖動到新位置時候的回調(diào)方法紫岩,我們在這里做數(shù)組集合的交換操作规惰,在這里我們把它暴露出去,交給Adapter自己處理泉蝌。注意這里一個判斷recyclerView.getAdapter() instanceof OnItemMoveListener歇万,在寫adapter的時候會實現(xiàn)OnItemMoveListener這個接口。

public interface OnItemMoveListener {
    void  onItemMove(int fromPosition,int toPosition);
}

這個類里還復寫了item被選中和調(diào)用完畢后item的狀態(tài)方法

    /**
     * item被選中時改變item的背景
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        //不在閑置的狀態(tài)
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE){
            if (viewHolder instanceof OnDragVHListener){
                OnDragVHListener itemViewHolder = (OnDragVHListener) viewHolder;
                itemViewHolder.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    /**
     * 用戶操作完畢或者動畫完畢后調(diào)用梨与,恢復item的背景和透明度
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        if (viewHolder instanceof  OnDragVHListener){
            OnDragVHListener itemViewHolder = (OnDragVHListener) viewHolder;
            itemViewHolder.onItemFinished();
        }
        super.clearView(recyclerView, viewHolder);
    }

這里注意下OnDragVHListener

public interface OnDragVHListener {
    /**
     * Item被選中時觸發(fā)
     */
    void onItemSelected();

    /**
     * Item在拖拽結(jié)束/滑動結(jié)束后觸發(fā)
     */
    void onItemFinished();
}

后續(xù)中堕花,因為拖拽操作只被設(shè)定在我的頻道里操作,所以目前只有我的頻道ViewHolder實現(xiàn)了OnDragVHListener粥鞋,這里就改變了一下背景色缘挽。

    /**
     * 我的頻道
     */
    class MyViewHolder extends RecyclerView.ViewHolder implements OnDragVHListener {
        private TextView textView;
        private ImageView imgEdit;

        public MyViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.tv);
            imgEdit = (ImageView) itemView.findViewById(R.id.img_edit);
        }

        /**
         * item 被選中時
         */
        @Override
        public void onItemSelected() {
            textView.setBackgroundResource(R.drawable.bg_channel_p);
        }

        /**
         * item 取消選中時
         */
        @Override
        public void onItemFinished() {

            textView.setBackgroundResource(R.drawable.bg_channel);

        }
    }

長按拖拽,這里其他頻道不需要拖拽呻粹,所以返回false,對于我的頻道則手動調(diào)用ItemTouchHelper的startDrag方法啟動拖拽壕曼。

    /**
     * isLongPressDragEnabled()如果返回true,則支持長按拖拽等浊,
     * 這里“其他頻道”等不需要拖拽腮郊,所以返回false,手動調(diào)用ItemTouchHelper的startDrag方法啟動拖拽筹燕。
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return false;
    }

    /**
     *  不支持滑動
     * @return false
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return false;
    }

ChannelAdapter

對于這個類轧飞,吧代碼貼上看吧衅鹿,注釋已經(jīng)寫的很詳細了。

public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
switch (viewType) {
        case TYPE_MY_CHANNEL_HEADER:
        case TYPE_MY:
        case TYPE_OTHER_CHANNEL_HEADER:
        case TYPE_OTHER:
        }
    return null;
    }

拆開看吧
我的頻道頭,就是進入和取消編輯狀態(tài)

                case TYPE_MY_CHANNEL_HEADER:
                view = mInflater.inflate(R.layout.item_my_channel_header, parent, false);
                final MyChannelHeaderViewHolder holder = new MyChannelHeaderViewHolder(view);
                holder.tvBtnEdit.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (!isEditMode) {
                            startEditMode((RecyclerView) parent);
                            holder.tvBtnEdit.setText(R.string.finish);
                        } else {
                            cancelEditMode((RecyclerView) parent);
                            holder.tvBtnEdit.setText(R.string.edit);
                        }
                    }
                });
                return holder;

我的頻道过咬,這里有三種監(jiān)聽大渤,一種是點擊,一種是長按掸绞,還一種是觸摸

                case TYPE_MY:
                view = mInflater.inflate(R.layout.item_my, parent, false);
                final MyViewHolder myViewHolder = new MyViewHolder(view);

                //點擊
                myViewHolder.textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int position = myViewHolder.getAdapterPosition();
                        if (isEditMode) {
                            RecyclerView recyclerView = (RecyclerView) parent;
                            //目標view也就是即將要去其他頻道的那個item
                            // position是 mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER 也既是我的頻道頭+其他頻道頭+我的頻道內(nèi)容全部item
                            View targetView = recyclerView.getLayoutManager().findViewByPosition(mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER);
                            View currentView = recyclerView.getLayoutManager().findViewByPosition(position);
                            // 如果targetView不在屏幕內(nèi),則indexOfChild為-1  此時不需要添加動畫,因為此時notifyItemMoved自帶一個向目標移動的動畫
                            // 如果在屏幕內(nèi),則添加一個位移動畫
                            if (recyclerView.indexOfChild(targetView) >= 0) {
                                int targetX, targetY;
                                //獲取到網(wǎng)格布局的列數(shù)
                                RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
                                int spanCount = ((GridLayoutManager) manager).getSpanCount();

                                if ((mMyChannelItems.size() - 1) % spanCount == 0) {
                                    //這種情況 移動后高度發(fā)生變化 (少了一行)
                                    View preTargetView = recyclerView.getLayoutManager().findViewByPosition(mMyChannelItems.size() + COUNT_PRE_OTHER_HEADER - 1);
                                    targetX = preTargetView.getLeft();
                                    targetY = preTargetView.getTop();
                                } else {
                                    targetX = targetView.getLeft();
                                    targetY = targetView.getTop();
                                }

                                moveMyToOther(myViewHolder);
                                startAnimation(recyclerView, currentView, targetX, targetY);

                            } else {
                                //targetView不在屏幕內(nèi),則indexOfChild為-1  此時不需要添加動畫
                                moveMyToOther(myViewHolder);
                            }

                        } else {
                            //不是編輯狀態(tài)下點擊我的頻道item
                            mChannelItemClickListener.OnItemClick(v, position - 1);
                        }
                    }
                });

                //長按
                myViewHolder.textView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        if (!isEditMode) {
                            RecyclerView recyclerView = ((RecyclerView) parent);
                            startEditMode(recyclerView);

                            //header 按鈕文字 改成"完成"
                            View view = recyclerView.getChildAt(0);
                            if (view == recyclerView.getLayoutManager().findViewByPosition(0)) {
                                TextView tvBtnEdit = (TextView) view.findViewById(R.id.tv_btn_edit);
                                tvBtnEdit.setText(R.string.finish);
                            }
                        }
                        mItemTouchHelper.startDrag(myViewHolder);
                        return true;
                    }
                });

                //touch
                myViewHolder.textView.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        if (isEditMode) {
                            switch (MotionEventCompat.getActionMasked(event)) {
                                case MotionEvent.ACTION_DOWN:
                                    startTime = System.currentTimeMillis();
                                    break;
                                case MotionEvent.ACTION_MOVE:
                                    if (System.currentTimeMillis() - startTime > SPACE_TIME) {
                                        mItemTouchHelper.startDrag(myViewHolder);
                                    }
                                    break;
                                case MotionEvent.ACTION_CANCEL:
                                case MotionEvent.ACTION_UP:
                                    startTime = 0;
                                    break;
                            }
                        }
                        return false;
                    }
                });
                return myViewHolder;

這里我想說下判斷targetView在不在屏幕里的那個方法indexOfChild泵三,以及item移動后高度發(fā)生變化的情況。
先看indexOfChild

    /**
     * Returns the position in the group of the specified child view.
     *
     * @param child the view for which to get the position
     * @return a positive integer representing the position of the view in the
     *         group, or -1 if the view does not exist in the group
     */
    public int indexOfChild(View child) {
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            if (children[i] == child) {
                return i;
            }
        }
        return -1;
    }

看他里面實現(xiàn)是把所有的指view都輪詢一遍衔掸,看看我們傳入的view在不在輪詢的里面烫幕,不在就return -1。在看高度變化敞映,想想在什么時候高度會發(fā)生變化较曼,當我的頻道多出一個item的時候,移動后我們這行少了驱显。告訴發(fā)生變化诗芜,其他頻道的位置也跟著變化,所有添加一個(mMyChannelItems.size() - 1) % spanCount == 0的判斷埃疫。
其他頻道

                case TYPE_OTHER:
                view = mInflater.inflate(R.layout.item_other, parent, false);
                final OtherViewHolder otherHolder = new OtherViewHolder(view);
                otherHolder.textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        RecyclerView recyclerView = ((RecyclerView) parent);
                        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
                        int currentPiosition = otherHolder.getAdapterPosition();
                        // 如果RecyclerView滑動到底部,移動的目標位置的y軸 - height
                        View currentView = manager.findViewByPosition(currentPiosition);
                        // 目標位置的前一個item  即當前MyChannel的最后一個
                        View preTargetView = manager.findViewByPosition(mMyChannelItems.size() - 1 + COUNT_PRE_MY_HEADER);

                        // 如果targetView不在屏幕內(nèi),則為-1  此時不需要添加動畫,因為此時notifyItemMoved自帶一個向目標移動的動畫
                        // 如果在屏幕內(nèi),則添加一個位移動畫
                        if (recyclerView.indexOfChild(preTargetView) >= 0) {
                            int targetX = preTargetView.getLeft();
                            int targetY = preTargetView.getTop();

                            int targetPosition = mMyChannelItems.size() - 1 + COUNT_PRE_OTHER_HEADER;

                            GridLayoutManager gridLayoutManager = ((GridLayoutManager) manager);
                            int spanCount = gridLayoutManager.getSpanCount();
                            // target 在最后一行第一個
                            if ((targetPosition - COUNT_PRE_MY_HEADER) % spanCount == 0) {
                                View targetView = manager.findViewByPosition(targetPosition);
                                targetX = targetView.getLeft();
                                targetY = targetView.getTop();
                            } else {
                                targetX += preTargetView.getWidth();

                                // 最后一個item可見
                                if (gridLayoutManager.findLastVisibleItemPosition() == getItemCount() - 1) {
                                    // 最后的item在最后一行第一個位置
                                    if ((getItemCount() - 1 - mMyChannelItems.size() - COUNT_PRE_OTHER_HEADER) % spanCount == 0) {
                                        // RecyclerView實際高度 > 屏幕高度 && RecyclerView實際高度 < 屏幕高度 + item.height
                                        int firstVisiblePostion = gridLayoutManager.findFirstVisibleItemPosition();
                                        if (firstVisiblePostion == 0) {
                                            // FirstCompletelyVisibleItemPosition == 0 即 內(nèi)容不滿一屏幕 , targetY值不需要變化
                                            // // FirstCompletelyVisibleItemPosition != 0 即 內(nèi)容滿一屏幕 并且 可滑動 , targetY值 + firstItem.getTop
                                            if (gridLayoutManager.findFirstCompletelyVisibleItemPosition() != 0) {
                                                int offset = (-recyclerView.getChildAt(0).getTop()) - recyclerView.getPaddingTop();
                                                targetY += offset;
                                            }
                                        } else { // 在這種情況下 并且 RecyclerView高度變化時(即可見第一個item的 position != 0),
                                            // 移動后, targetY值  + 一個item的高度
                                            targetY += preTargetView.getHeight();
                                        }
                                    }
                                } else {
                                    System.out.println("呵呵噠");
                                }
                            }

                            // 如果當前位置是otherChannel可見的最后一個
                            // 并且 當前位置不在grid的第一個位置
                            // 并且 目標位置不在grid的第一個位置

                            // 則 需要延遲250秒 notifyItemMove , 這是因為這種情況 , 并不觸發(fā)ItemAnimator , 會直接刷新界面
                            // 導致我們的位移動畫剛開始,就已經(jīng)notify完畢,引起不同步問題
                            if (currentPiosition == gridLayoutManager.findLastVisibleItemPosition()
                                    && (currentPiosition - mMyChannelItems.size() - COUNT_PRE_OTHER_HEADER) % spanCount != 0
                                    && (targetPosition - COUNT_PRE_MY_HEADER) % spanCount != 0) {
                                moveOtherToMyWithDelay(otherHolder);
                            } else {
                                moveOtherToMy(otherHolder);
                            }
                            startAnimation(recyclerView, currentView, targetX, targetY);

                        } else {
                            moveOtherToMy(otherHolder);
                        }
                    }
                });
                return otherHolder;

動畫

rivate void startAnimation(RecyclerView recyclerView, final View currentView, int targetX, int targetY) {
        final ViewGroup viewGroup = (ViewGroup) recyclerView.getParent();
        final ImageView mirrorView = addMirrorView(viewGroup, recyclerView, currentView);

        Animation animation = getTranslateAnimator(
                targetX - currentView.getLeft(), targetY - currentView.getTop());
        currentView.setVisibility(View.INVISIBLE);
        mirrorView.startAnimation(animation);

        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                viewGroup.removeView(mirrorView);
                if (currentView.getVisibility() == View.INVISIBLE) {
                    currentView.setVisibility(View.VISIBLE);
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }

添加鏡像,鏡像ImageView啟動位移動畫的同時伏恐,調(diào)用notifyItemMove。

/**
     * 添加需要移動的 鏡像View
     */
    private ImageView addMirrorView(ViewGroup parent, RecyclerView recyclerView, View view) {
        view.destroyDrawingCache();
        view.setDrawingCacheEnabled(true);
        final ImageView mirrorView = new ImageView(recyclerView.getContext());
        Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
        mirrorView.setImageBitmap(bitmap);
        view.setDrawingCacheEnabled(false);
        int[] locations = new int[2];
        view.getLocationOnScreen(locations);
        int[] parenLocations = new int[2];
        recyclerView.getLocationOnScreen(parenLocations);
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(bitmap.getWidth(), bitmap.getHeight());
        params.setMargins(locations[0], locations[1] - parenLocations[1], 0, 0);
        parent.addView(mirrorView, params);

        return mirrorView;
    }

代碼下載

Demo代碼下載(AS導到Module里)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末栓霜,一起剝皮案震驚了整個濱河市翠桦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胳蛮,老刑警劉巖销凑,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仅炊,居然都是意外死亡斗幼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門抚垄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜕窿,“玉大人,你說我怎么就攤上這事呆馁⊥┚” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵浙滤,是天一觀的道長阴挣。 經(jīng)常有香客問我,道長纺腊,這世上最難降的妖魔是什么畔咧? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任茎芭,我火速辦了婚禮,結(jié)果婚禮上盒卸,老公的妹妹穿的比我還像新娘骗爆。我一直安慰自己次氨,他們只是感情好蔽介,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著煮寡,像睡著了一般虹蓄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上幸撕,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天薇组,我揣著相機與錄音,去河邊找鬼坐儿。 笑死律胀,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的貌矿。 我是一名探鬼主播炭菌,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逛漫!你這毒婦竟也來了黑低?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤酌毡,失蹤者是張志新(化名)和其女友劉穎克握,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枷踏,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡菩暗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了旭蠕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片员咽。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡搓扯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遣总,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布辜腺,位于F島的核電站宣鄙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏岗屏。R本人自食惡果不足惜辆琅,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一漱办、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧婉烟,春花似錦娩井、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昙衅,卻和暖如春扬霜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背而涉。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工著瓶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啼县。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓材原,卻偏偏與公主長得像,于是被迫代替她去往敵國和親季眷。 傳聞我的和親對象是個殘疾皇子余蟹,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容