RecyclerView進(jìn)階:使用ItemTouchHelper實(shí)現(xiàn)拖拽和側(cè)滑刪除

前言

現(xiàn)在RecyclerView的應(yīng)用越來(lái)越廣泛了,不同的應(yīng)用場(chǎng)景需要其作出不同的改變。有時(shí)候我們可能需要實(shí)現(xiàn)側(cè)滑刪除的功能函似,比如知乎首頁(yè)的側(cè)滑刪除阴绢,又或者長(zhǎng)按Item進(jìn)行拖動(dòng)與其他Item進(jìn)行位置的交換店乐,但RecyclerView沒(méi)有提供現(xiàn)成的API供我們操作,所幸SDK提供了ItemTouchHelper這樣一個(gè)工具類(lèi)幫助我們快速實(shí)現(xiàn)以上功能呻袭。不多說(shuō)別的眨八,我們來(lái)介紹一下ItemTouchHelper。

什么是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).

以上是官方文檔的介紹左电,ItemTouchHelper是一個(gè)工具類(lèi)廉侧,可實(shí)現(xiàn)側(cè)滑刪除和拖拽移動(dòng),使用這個(gè)工具類(lèi)需要RecyclerView和Callback篓足。同時(shí)根據(jù)需要重寫(xiě)onMove和onSwiped方法段誊。接下來(lái)就來(lái)講述ItemTouchHelper的使用方法。

ItemTouchHelper基本使用方法

step.1新建一個(gè)接口栈拖,讓Adapter實(shí)現(xiàn)之

從解耦的角度考慮连舍,我們需要一個(gè)接口來(lái)實(shí)現(xiàn)Adapter和ItemTouchHelper之間涉及數(shù)據(jù)的操作,因?yàn)镮temTouchHelper在完成觸摸的各種動(dòng)畫(huà)后涩哟,就要對(duì)Adapter的數(shù)據(jù)進(jìn)行操作索赏,比如側(cè)滑刪除操作,最后需要調(diào)用Adapter的notifyItemRemove()方法來(lái)移除該數(shù)據(jù)贴彼。因此我們可以把數(shù)據(jù)操作的部分抽象成一個(gè)接口方法潜腻,讓ItemTouchHelper.Callback調(diào)用該方法即可。具體如下:
新建ItemTouchHelperAdapter:

public interface ItemTouchHelperAdapter {
    //數(shù)據(jù)交換
    void onItemMove(int fromPosition,int toPosition);
    //數(shù)據(jù)刪除
    void onItemDissmiss(int position);
}

讓我們的Adapter實(shí)現(xiàn)該接口:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ItemTouchHelperAdapter {
    //數(shù)據(jù)
    private List<String> mData;
    ...
     @Override
    public void onItemMove(int fromPosition, int toPosition) {
        //交換位置
        Collections.swap(mData,fromPosition,toPosition);
        notifyItemMoved(fromPosition,toPosition);
    }

    @Override
    public void onItemDissmiss(int position) {
        //移除數(shù)據(jù)
        mData.remove(position);
        notifyItemRemoved(position);
    }

}

那么我們?cè)贗temTouchHelper.Callback內(nèi)直接調(diào)用接口的方法即可器仗。

step.2新建類(lèi)繼承自ItemTouchHelper.Callback

從官方文檔我們知道融涣,使用ItemTouchHelper需要一個(gè)Callback,該Callback是ItemTouchHelper.Callback的子類(lèi)精钮,所以我們需要新建一個(gè)類(lèi)比如SimpleItemTouchHelperCallback繼承自ItemTouchHelper.Callback威鹿。我們可以重寫(xiě)其數(shù)個(gè)方法來(lái)實(shí)現(xiàn)我們的需求。我們先來(lái)看看ItemTouchHelper.Callback需要重寫(xiě)的幾個(gè)常用的方法轨香。
1专普、public int getMovementFlags(RecyclerView, RecyclerView.ViewHolder):該方法用于返回可以滑動(dòng)的方向,比如說(shuō)允許從右到左側(cè)滑弹沽,允許上下拖動(dòng)等。我們一般使用makeMovementFlags(int,int)或makeFlag(int, int)來(lái)構(gòu)造我們的返回值筋粗。
例如:要使RecyclerView的Item可以上下拖動(dòng)策橘,同時(shí)允許從右到左側(cè)滑,但不許允許從左到右的側(cè)滑娜亿,我們可以這樣寫(xiě):

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;        //允許上下的拖動(dòng)
        int swipeFlags = ItemTouchHelper.LEFT;   //只允許從右向左側(cè)滑
        return makeMovementFlags(dragFlags,swipeFlags);
    }

2丽已、public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
當(dāng)用戶拖動(dòng)一個(gè)Item進(jìn)行上下移動(dòng)從舊的位置到新的位置的時(shí)候會(huì)調(diào)用該方法,在該方法內(nèi)买决,我們可以調(diào)用Adapter的notifyItemMoved方法來(lái)交換兩個(gè)ViewHolder的位置沛婴,最后返回true吼畏,表示被拖動(dòng)的ViewHolder已經(jīng)移動(dòng)到了目的位置。所以嘁灯,如果要實(shí)現(xiàn)拖動(dòng)交換位置泻蚊,可以重寫(xiě)該方法(前提是支持上下拖動(dòng)):

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        //onItemMove是接口方法
        mAdapter.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());  
        return true;
    }

3、public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
當(dāng)用戶左右滑動(dòng)Item達(dá)到刪除條件時(shí)丑婿,會(huì)調(diào)用該方法性雄,一般手指觸摸滑動(dòng)的距離達(dá)到RecyclerView寬度的一半時(shí),再松開(kāi)手指羹奉,此時(shí)該Item會(huì)繼續(xù)向原先滑動(dòng)方向滑過(guò)去并且調(diào)用onSwiped方法進(jìn)行刪除秒旋,否則會(huì)反向滑回原來(lái)的位置。在該方法內(nèi)部我們可以這樣寫(xiě):

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //onItemDissmiss是接口方法
        mAdapter.onItemDissmiss(viewHolder.getAdapterPosition());
    }

如果在onSwiped方法內(nèi)我們沒(méi)有進(jìn)行任何操作诀拭,即不刪除已經(jīng)滑過(guò)去的Item迁筛,那么就會(huì)留下空白的地方,因?yàn)閷?shí)際上該ItemView還占據(jù)著該位置耕挨,只是移出了我們的可視范圍內(nèi)罷了细卧。

4、public boolean isLongPressDragEnabled():該方法返回true時(shí)俗孝,表示支持長(zhǎng)按拖動(dòng)酒甸,即長(zhǎng)按ItemView后才可以拖動(dòng),我們遇到的場(chǎng)景一般也是這樣的赋铝。默認(rèn)是返回true插勤。

5、public boolean boolean isItemViewSwipeEnabled():該方法返回true時(shí)革骨,表示如果用戶觸摸并左右滑動(dòng)了View农尖,那么可以執(zhí)行滑動(dòng)刪除操作,即可以調(diào)用到onSwiped()方法良哲。默認(rèn)是返回true盛卡。

6、public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState):從靜止?fàn)顟B(tài)變?yōu)橥献Щ蛘呋瑒?dòng)的時(shí)候會(huì)回調(diào)該方法筑凫,參數(shù)actionState表示當(dāng)前的狀態(tài)滑沧。

7、public void clearView(RecyclerView recyclerView, ViewHolder viewHolder):當(dāng)用戶操作完畢某個(gè)item并且其動(dòng)畫(huà)也結(jié)束后會(huì)調(diào)用該方法巍实,一般我們?cè)谠摲椒▋?nèi)恢復(fù)ItemView的初始狀態(tài)滓技,防止由于復(fù)用而產(chǎn)生的顯示錯(cuò)亂問(wèn)題。

8棚潦、public void onChildDraw(...):我們可以在這個(gè)方法內(nèi)實(shí)現(xiàn)我們自定義的交互規(guī)則或者自定義的動(dòng)畫(huà)效果令漂。
那么完整的SimpleItemTouchHelperCallback文件是這樣的:

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback{

    private ItemTouchHelperAdapter mAdapter;

    public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter){
        mAdapter = adapter;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.LEFT;
        return makeMovementFlags(dragFlags,swipeFlags);
    }

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

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

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        mAdapter.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        mAdapter.onItemDissmiss(viewHolder.getAdapterPosition());
    }
}

step.3為RecycleView添加ItemTouchHelper

上面我們修改了Adapter和新建了ItemTouchHelper.Callback的子類(lèi),接下來(lái)我們要為RecyclerView添加ItemTouchHelper:

    //先實(shí)例化Callback
    ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(myAdapter);
    //用Callback構(gòu)造ItemtouchHelper
    ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
    //調(diào)用ItemTouchHelper的attachToRecyclerView方法建立聯(lián)系
    touchHelper.attachToRecyclerView(mRecyclerView);

經(jīng)過(guò)以上步驟,我們已經(jīng)實(shí)現(xiàn)了Item的拖拽和側(cè)滑刪除功能了叠必,看一下效果:


拖拽和側(cè)滑.gif

自定義側(cè)滑動(dòng)畫(huà)

有時(shí)候我們對(duì)默認(rèn)的動(dòng)畫(huà)效果可能不滿意荚孵,需要自己實(shí)現(xiàn)想要的動(dòng)畫(huà)效果,ItemTouchHelper.Callback提供的onChildDraw方法可以讓我們很方便地實(shí)現(xiàn)想要的效果纬朝。以下帶來(lái)一種自定義的實(shí)現(xiàn)效果收叶,當(dāng)做拋磚引玉,讓大家熟悉自定義效果的運(yùn)用玄组。先來(lái)看看要實(shí)現(xiàn)的效果:


自定義側(cè)滑.gif

該效果是比較常見(jiàn)的滔驾,用戶向左滑動(dòng)Item的時(shí)候,一開(kāi)始提示的是“左滑刪除”俄讹,滑動(dòng)到一定距離后哆致,顯示刪除的圖標(biāo),并且隨著滑動(dòng)距離的增加該圖標(biāo)不斷變大患膛,達(dá)到最大后用戶松開(kāi)手指摊阀,該Item被刪除。
接下來(lái)我們來(lái)分析一下怎樣實(shí)現(xiàn)以上的效果:
首先踪蹬,要想左滑出現(xiàn)一個(gè)刪除的方塊胞此,可以在LinearLayout放一個(gè)這樣的“方塊”,讓它與Item水平并排排列跃捣,以下是布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:orientation="horizontal">

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="#ffffff"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:layout_marginBottom="4dp"
        app:cardCornerRadius="1dp"
        app:elevation="1dp"
        app:contentPadding="1dp">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#ffffff">
            <TextView
                android:id="@+id/item"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="22sp"
                android:padding="4dp"
                android:layout_centerInParent="true"/>

        </RelativeLayout>
    </android.support.v7.widget.CardView>

    <FrameLayout
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:layout_marginRight="4dp"
        android:layout_marginBottom="4dp"
        android:background="#f33213">

        <ImageView
            android:id="@+id/iv_img"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:src="@mipmap/ic_eye_72"
            android:visibility="invisible"/>
        <TextView
            android:id="@+id/tv_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="左滑刪除"
            android:textSize="18sp"
            android:textColor="#ffffff"
            android:layout_gravity="center"/>
    </FrameLayout>
</LinearLayout>

布局文件修改后漱牵,我們嘗試來(lái)滑動(dòng)一下,發(fā)現(xiàn)后面的刪除方塊并不會(huì)出現(xiàn)疚漆,這是因?yàn)槟J(rèn)的滑動(dòng)方式是setTranslationX(int)酣胀,即是對(duì)整個(gè)View的滑動(dòng),所以無(wú)論我們?cè)鯓踊瑒?dòng)娶聘,都不會(huì)出現(xiàn)刪除方塊闻镶。因此,我們要改變一個(gè)種滑動(dòng)方式丸升,比如使用scrollTo(int,int)铆农,這種是對(duì)View的內(nèi)容的滑動(dòng),所以隨著左滑狡耻,item會(huì)向左滑去墩剖,而位于右方的方塊自然也就出現(xiàn)了。
接著夷狰,我們考慮該“刪除眼睛”的圖標(biāo)是怎樣從小變大的涛碑,這個(gè)實(shí)現(xiàn)也比較簡(jiǎn)單,只要根據(jù)滑動(dòng)的距離對(duì)該ImageView的LayoutParams.width進(jìn)行改變就行了孵淘,不過(guò)要注意限制大小,否則過(guò)大會(huì)造成圖片的失真歹篓。當(dāng)滑動(dòng)距離等于RecyclerView寬度的一半時(shí)瘫证,此時(shí)松開(kāi)手會(huì)使Item刪除揉阎,那么我們可以在該滑動(dòng)距離達(dá)到該值時(shí)時(shí)“眼睛”變得最大,此時(shí)可以達(dá)到良好的交互效果背捌,提示了用戶無(wú)需繼續(xù)滑動(dòng)即可刪除該Item了毙籽。
最后我們要考慮的是:在刪除了Item或者沒(méi)刪除而滑回原來(lái)的位置后,我們要把所做的改變重置一下毡庆,否則坑赡,會(huì)由于RecyclerView的復(fù)用而導(dǎo)致其他位置的ViewHolder與當(dāng)前的ViewHolder所做的改變一樣,即造成顯示的錯(cuò)誤么抗。我們可以在clearView()方法內(nèi)重置改變毅否,這樣就能解決因復(fù)用而導(dǎo)致的顯示問(wèn)題了。
最后我們來(lái)看看SimpleItemTouchHelperCallback的代碼:

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback{

    //省略上面的代碼....

    //限制ImageView長(zhǎng)度所能增加的最大值
    private double ICON_MAX_SIZE = 50;
    //ImageView的初始長(zhǎng)寬
    private int fixedWidth = 150;

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        //重置改變蝇刀,防止由于復(fù)用而導(dǎo)致的顯示問(wèn)題
        viewHolder.itemView.setScrollX(0);
        ((MyAdapter.NormalItem)viewHolder).tv.setText("左滑刪除");
        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) ((MyAdapter.NormalItem) viewHolder).iv.getLayoutParams();
        params.width = 150;
        params.height = 150;
        ((MyAdapter.NormalItem) viewHolder).iv.setLayoutParams(params);
        ((MyAdapter.NormalItem) viewHolder).iv.setVisibility(View.INVISIBLE);
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        //僅對(duì)側(cè)滑狀態(tài)下的效果做出改變
        if (actionState ==ItemTouchHelper.ACTION_STATE_SWIPE){
            //如果dX小于等于刪除方塊的寬度螟加,那么我們把該方塊滑出來(lái)
            if (Math.abs(dX) <= getSlideLimitation(viewHolder)){
                viewHolder.itemView.scrollTo(-(int) dX,0);
            }
            //如果dX還未達(dá)到能刪除的距離,此時(shí)慢慢增加“眼睛”的大小吞琐,增加的最大值為ICON_MAX_SIZE
            else if (Math.abs(dX) <= recyclerView.getWidth() / 2){
                double distance = (recyclerView.getWidth() / 2 -getSlideLimitation(viewHolder));
                double factor = ICON_MAX_SIZE / distance;
                double diff =  (Math.abs(dX) - getSlideLimitation(viewHolder)) * factor;
                if (diff >= ICON_MAX_SIZE)
                    diff = ICON_MAX_SIZE;
                ((MyAdapter.NormalItem)viewHolder).tv.setText("");   //把文字去掉
                ((MyAdapter.NormalItem) viewHolder).iv.setVisibility(View.VISIBLE);  //顯示眼睛
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) ((MyAdapter.NormalItem) viewHolder).iv.getLayoutParams();
                params.width = (int) (fixWidth + diff);
                params.height = (int) (fixWidth + diff);
                ((MyAdapter.NormalItem) viewHolder).iv.setLayoutParams(params);
            }
        }else {
            //拖拽狀態(tài)下不做改變捆探,需要調(diào)用父類(lèi)的方法
            super.onChildDraw(c,recyclerView,viewHolder,dX,dY,actionState,isCurrentlyActive);
        }
    }

    /**
     * 獲取刪除方塊的寬度
     */
    public int getSlideLimitation(RecyclerView.ViewHolder viewHolder){
        ViewGroup viewGroup = (ViewGroup) viewHolder.itemView;
        return viewGroup.getChildAt(1).getLayoutParams().width;
    }
}

好了,到目前為止站粟,自定義效果介紹完畢黍图,讀者可以根據(jù)需求來(lái)實(shí)現(xiàn)多樣化的效果。最后奴烙,感謝你的閱讀助被,如有錯(cuò)誤歡迎指出~

參考文章
RecyclerView的拖動(dòng)和滑動(dòng) 第一部分 :基本的ItemTouchHelper示例

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缸沃,隨后出現(xiàn)的幾起案子恰起,更是在濱河造成了極大的恐慌,老刑警劉巖趾牧,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件检盼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡翘单,警方通過(guò)查閱死者的電腦和手機(jī)吨枉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哄芜,“玉大人貌亭,你說(shuō)我怎么就攤上這事∪想” “怎么了圃庭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我剧腻,道長(zhǎng)拘央,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任书在,我火速辦了婚禮灰伟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘儒旬。我一直安慰自己栏账,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布栈源。 她就那樣靜靜地躺著挡爵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凉翻。 梳的紋絲不亂的頭發(fā)上了讨,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音制轰,去河邊找鬼前计。 笑死,一個(gè)胖子當(dāng)著我的面吹牛垃杖,可吹牛的內(nèi)容都是我干的男杈。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼调俘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼伶棒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起彩库,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肤无,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后骇钦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體宛渐,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年眯搭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秩伞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片道媚。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖湖蜕,靈堂內(nèi)的尸體忽然破棺而出空入,到底是詐尸還是另有隱情锁孟,我是刑警寧澤斤寂,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站允耿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏爹梁。R本人自食惡果不足惜右犹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姚垃。 院中可真熱鬧盼忌,春花似錦积糯、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至跨嘉,卻和暖如春川慌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祠乃。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工梦重, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亮瓷。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓琴拧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嘱支。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚓胸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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