自定義View進階《十七》——手勢檢測

Android 手勢檢測,主要是 GestureDetector 相關(guān)內(nèi)容的用法和注意事項须喂,本文依舊屬于事件處理這一體系言蛇,部分內(nèi)容會涉及到之前文章提及過的知識點,如果你沒看過之前的文章兽狭,可以到 自定義 View 系列 來查看這些內(nèi)容。

在開發(fā) Android 手機應(yīng)用過程中鹿蜀,可能需要對一些手勢作出響應(yīng)箕慧,如:單擊、雙擊耻姥、長按销钝、滑動有咨、縮放等琐簇。這些都是很常用的手勢。就拿最簡單的雙擊來說吧座享,假如我們需要判斷一個控件是否被雙擊(即在較短的時間內(nèi)快速的點擊兩次)婉商,似乎是一個很容易的任務(wù),但仔細考慮起來渣叛,要處理的細節(jié)問題也有不少丈秩,例如:

  1. 記錄點擊次數(shù),為了判斷是否被點擊超過 1 次淳衙,所以必須記錄點擊次數(shù)蘑秽。
  2. 記錄點擊時間饺著,由于雙擊事件是較快速的點擊兩次,像點擊一次后肠牲,過來幾分鐘再點擊一次肯定不能算是雙擊事件幼衰,所以在記錄點擊次數(shù)的同時也要記錄上一次的點擊時間,我們可以設(shè)置本次點擊距離上一次時間超過一定時間(例如:超過100ms)就不識別為雙擊事件缀雳。
  3. 點擊狀態(tài)重置渡嚣,在響應(yīng)雙擊事件,或者判斷不是雙擊事件的時候要重置計數(shù)器和上一次點擊時間肥印。重置既可以在點擊的時候判斷并進行重新設(shè)置识椰,也可以使用定時器等超過一定時間后重置狀態(tài)。

這樣看起來深碱,判斷一個雙擊事件就有這么多麻煩事情腹鹉,更別其他的手勢了,雖然這些看起來都很簡單敷硅,但設(shè)計起來需要考慮的細節(jié)情況實在是太多了种蘸。

那么有沒有一種更好的方法來方便的檢測手勢呢?當(dāng)然有啦竞膳,因為這些手勢很常用航瞭,系統(tǒng)早就封裝了一些方法給我們用,接下來我們就看看它們是如何使用的坦辟。

GestureDetector

GestureDetector 可以使用 MotionEvents 檢測各種手勢和事件刊侯。GestureDetector.OnGestureListener 是一個回調(diào)方法,在發(fā)生特定的事件時會調(diào)用 Listener 中對應(yīng)的方法回調(diào)锉走。這個類只能用于檢測觸摸事件的 MotionEvent滨彻,不能用于軌跡球事件。

  • 創(chuàng)建一個 GestureDetector 實例挪蹭。
  • 在onTouchEvent(MotionEvent)方法中亭饵,確保調(diào)用 GestureDetector 實例的 onTouchEvent(MotionEvent)×豪鳎回調(diào)中定義的方法將在事件發(fā)生時執(zhí)行辜羊。
  • 如果偵聽 onContextClick(MotionEvent),則必須在 View 的 onGenericMotionEvent(MotionEvent)中調(diào)用 GestureDetector OnGenericMotionEvent(MotionEvent)词顾。

GestureDetector 本身的方法比較少八秃,使用起來也非常簡單,下面讓我們先看一下它的簡單使用示例肉盹,分解開來大概需要三個步驟昔驱。

// 1.創(chuàng)建一個監(jiān)聽回調(diào)
SimpleOnGestureListener listener = new SimpleOnGestureListener() {
    @Override public boolean onDoubleTap(MotionEvent e) {
        Toast.makeText(MainActivity.this, "雙擊666", Toast.LENGTH_SHORT).show();
        return super.onDoubleTap(e);
    }
};

// 2.創(chuàng)建一個檢測器
final GestureDetector detector = new GestureDetector(this, listener);

// 3.給監(jiān)聽器設(shè)置數(shù)據(jù)源
view.setOnTouchListener(new View.OnTouchListener() {
    @Override public boolean onTouch(View v, MotionEvent event) {
        return detector.onTouchEvent(event);
    }
});

接下來我們先了解一下 GestureDetector 里面都有哪些內(nèi)容。

1. 構(gòu)造函數(shù)

GestureDetector 一共有 5 種構(gòu)造函數(shù)上忍,但有 2 種被廢棄了骤肛,1 種是重復(fù)的纳本,所以我們只需要關(guān)注其中的 2 種構(gòu)造函數(shù)即可,如下:


第 1 種構(gòu)造函數(shù)里面需要傳遞兩個參數(shù)腋颠,上下文(Context) 和 手勢監(jiān)聽器(OnGestureListener)饮醇,這個很容易理解,就不再過多敘述秕豫,上面的例子中使用的就是這一種朴艰。

第 2 種構(gòu)造函數(shù)則需要多傳遞一個 Handler 作為參數(shù),這個有什么作用呢混移?其實作用也非常簡單祠墅,這個 Handler 主要是為了給 GestureDetector 提供一個 Looper。
在通常情況下是不需這個 Handler 的歌径,因為它會在內(nèi)部自動創(chuàng)建一個 Handler 用于處理數(shù)據(jù)毁嗦,如果你在主線程中創(chuàng)建 GestureDetector,那么它內(nèi)部創(chuàng)建的 Handler 會自動獲得主線程的 Looper回铛,然而如果你在一個沒有創(chuàng)建 Looper 的子線程中創(chuàng)建 GestureDetector 則需要傳遞一個帶有 Looper 的 Handler 給它狗准,否則就會因為無法獲取到 Looper 導(dǎo)致創(chuàng)建失敗。

第 2 種構(gòu)造函數(shù)使用方式如下(下面是兩種在子線程中創(chuàng)建 GestureDetector 的方法):

// 方式一茵肃、在主線程創(chuàng)建 Handler
final Handler handler = new Handler();
new Thread(new Runnable() {
    @Override public void run() {
        final GestureDetector detector = new GestureDetector(MainActivity.this, new
                GestureDetector.SimpleOnGestureListener() , handler);
        // ... 省略其它代碼 ...
    }
}).start();

// 方式二腔长、在子線程創(chuàng)建 Handler,并且指定 Looper
new Thread(new Runnable() {
    @Override public void run() {
        final Handler handler = new Handler(Looper.getMainLooper());
        final GestureDetector detector = new GestureDetector(MainActivity.this, new
                GestureDetector.SimpleOnGestureListener() , handler);
        // ... 省略其它代碼 ...
    }
}).start();

當(dāng)然了验残,使用其它創(chuàng)建 Handler 的方式也是可以的捞附,重點傳遞的 Handler 一定要有 Looper,敲黑板您没,重點是 Handler 中的 Looper鸟召。假如子線程準(zhǔn)備了 Looper 那么可以直接使用第 1 種構(gòu)造函數(shù)進行創(chuàng)建,如下:

new Thread(new Runnable() {
    @Override public void run() {
        Looper.prepare(); // <- 重點在這里
        final GestureDetector detector = new GestureDetector(MainActivity.this, new
                GestureDetector.SimpleOnGestureListener());
        // ... 省略其它代碼 ...
    }
}).start();

2.手勢監(jiān)聽器

既然是手勢檢測氨鹏,自然要在對應(yīng)的手勢出現(xiàn)的時候通知調(diào)用者欧募,最合適的自然是事件監(jiān)聽器模式。目前 GestureDetecotr 有四種監(jiān)聽器仆抵。

2.1 OnContextClickListener

由于 OnContextClickListener 主要是用于檢測外部設(shè)備按鈕的跟继,關(guān)于它需要注意一點,如果偵聽 onContextClick(MotionEvent)肢础,則必須在 View 的 onGenericMotionEvent(MotionEvent)中調(diào)用 GestureDetector 的 OnGenericMotionEvent(MotionEvent)还栓。

由于目前我們用到這個監(jiān)聽器的場景并不多,所以也就不展開介紹了传轰,重點關(guān)注后面幾個監(jiān)聽器。

2.2 OnDoubleTapListener

這個很明顯就是用于檢測雙擊事件的谷婆,它有三個回調(diào)接口慨蛙,分別是 onDoubleTap辽聊、onDoubleTapEvent 和 onSingleTapConfirmed。

2.2.1 onDoubleTap 與 onSingleTapConfirmed

如果你只想監(jiān)聽雙擊事件期贫,那么只用關(guān)注 onDoubleTap 就行了跟匆,如果你同時要監(jiān)聽單擊事件則需要關(guān)注 onSingleTapConfirmed 這個回調(diào)函數(shù)。
有人可能會有疑問通砍,監(jiān)聽單擊事件為什么要使用 onSingleTapConfirmed玛臂,使用 OnClickListener 不行嗎?從理論上是可行的封孙,但是我并不推薦這樣使用迹冤,主要有兩個原因:

  1. 它們兩個是存在一定沖突的,如果你看過 事件分發(fā)機制詳解 就會知道虎忌,如果想要兩者同時被觸發(fā)泡徙,則 setOnTouchListener 不能消費事件,如果 onTouchListener 消費了事件膜蠢,就可能導(dǎo)致 OnClick 無法正常觸發(fā)堪藐。
  2. 需要同時監(jiān)聽單擊和雙擊,則說明單擊和雙擊后響應(yīng)邏輯不同挑围,然而使用 OnClickListener 會在雙擊事件發(fā)生時觸發(fā)兩次礁竞,這顯然不是我們想要的結(jié)果。而使用 onSingleTapConfirmed 就不用考慮那么多了杉辙,你完全可以把它當(dāng)成單擊事件來看待苏章,而且在雙擊事件發(fā)生時,onSingleTapConfirmed 不會被調(diào)用奏瞬,這樣就不會引發(fā)沖突枫绅。

如果你需要同時監(jiān)聽兩種點擊事件可以這樣寫:

GestureDetector detector = new GestureDetector(this, new GestureDetector
        .SimpleOnGestureListener() {
    @Override public boolean onSingleTapConfirmed(MotionEvent e) {
        Toast.makeText(MainActivity.this, "單擊", Toast.LENGTH_SHORT).show();
        return false;
    }
    @Override public boolean onDoubleTap(MotionEvent e) {
        Toast.makeText(MainActivity.this, "雙擊", Toast.LENGTH_SHORT).show();
        return false;
    }
});

關(guān)于 onSingleTapConfirmed 原理也非常簡單,這一個回調(diào)函數(shù)在單擊事件發(fā)生后300ms后觸發(fā)(注意硼端,不是立即觸發(fā)的)并淋,只有在確定不會有后續(xù)的事件后,既當(dāng)前事件肯定是單擊事件才觸發(fā) onSingleTapConfirmed珍昨,所以在進行點擊操作時县耽,onDoubleTap 和 onSingleTapConfirmed 只會有一個被觸發(fā),也就不存在沖突了镣典。

2.2.2 onDoubleTapEvent

有些細心的小伙伴可能注意到還有一個 onDoubleTapEvent 回調(diào)函數(shù)兔毙,它是干什么的呢?它在雙擊事件確定發(fā)生時會對第二次按下產(chǎn)生的 MotionEvent 信息進行回調(diào)兄春。
至于為什么要存在這樣的回調(diào)澎剥,就要涉及到另一個比較細致的問題了,那就是 onDoubleTap 的觸發(fā)時間赶舆,如果你在這些函數(shù)被調(diào)用時打印一條日志哑姚,那么你會看到這樣的信息:

GCS-LOG: onDoubleTap
GCS-LOG: onDoubleTapEvent - down
GCS-LOG: onDoubleTapEvent - move
GCS-LOG: onDoubleTapEvent - move
GCS-LOG: onDoubleTapEvent - up

通過觀察這些信息你會發(fā)現(xiàn)它們的調(diào)用順序非常有趣祭饭,首先是 onDoubleTap 被觸發(fā),之后依次觸發(fā) onDoubleTapEvent 的 down叙量、move倡蝙、up 等信息,為什么說它們有趣呢绞佩?是因為這樣的調(diào)用順序會引發(fā)兩種猜想寺鸥,第一種猜想是 onDoubleTap 是在第二次手指抬起(up)后觸發(fā)的,而 onDoubleTapEvent 是一種延時回調(diào)品山。第二種猜想則是 onDoubleTap 在第二次手指按下(dowm)時觸發(fā)胆建,onDoubleTapEvent 是一種實時回調(diào)。
通過測試和觀察源碼發(fā)現(xiàn)第二種猜想是正確的谆奥,因為第二次按下手指時眼坏,即便不抬起也會觸發(fā) onDoubleTap 和 onDoubleTapEvent 的 down,而且源碼中邏輯也表明 onDoubleTapEvent 是一種實時回調(diào)酸些。
這就引發(fā)了另一個問題宰译,雙擊的觸發(fā)時間,雖然這是一個細微到很難讓人注意到的問題魄懂,假如說我們想要在第二次按下抬起后才判定這是一個雙擊操作沿侈,觸發(fā)后續(xù)的內(nèi)容,則不能使用 onDoubleTap 了市栗,需要使用 onDoubleTapEvent 來進行更細微的控制缀拭,如下:

final GestureDetector detector = new GestureDetector(MainActivity.this, new GestureDetector.SimpleOnGestureListener() {
    @Override public boolean onDoubleTap(MotionEvent e) {
        Logger.e("第二次按下時觸發(fā)");
        return super.onDoubleTap(e);
    }

    @Override public boolean onDoubleTapEvent(MotionEvent e) {
        switch (e.getActionMasked()) {
            case MotionEvent.ACTION_UP:
                Logger.e("第二次抬起時觸發(fā)");
                break;
        }
        return super.onDoubleTapEvent(e);
    }
});

如果你不需要控制這么細微的話,忽略即可填帽。

2.3 OnGestureListener

這個是手勢檢測中較為核心的一個部分了蛛淋,主要檢測以下類型事件:按下(Down)、 一扔(Fling)篡腌、長按(LongPress)褐荷、滾動(Scroll)、觸摸反饋(ShowPress) 和 單擊抬起(SingleTapUp)嘹悼。

2.3.1 onDown
@Override public boolean onDown(MotionEvent e) {
    return true;
}

看過前面的文章應(yīng)該知道叛甫,down 在事件分發(fā)體系中是一個較為特殊的事件,為了保證事件被唯一的 View 消費杨伙,哪個 View 消費了 down 事件其监,后續(xù)的內(nèi)容就會傳遞給該 View。如果我們想讓一個 View 能夠接收到事件限匣,有兩種做法

  1. 讓該 View 可以點擊抖苦,因為可點擊狀態(tài)會默認消費 down 事件。
  2. 手動消費掉 down 事件。
    由于圖片睛约、文本等一些控件默認是不可點擊的鼎俘,所以我們要么聲明它們的 clickable 為 true哲身,要么在發(fā)生 down 事件是返回 true辩涝。所以 onDown 在這里的作用就很明顯了,就是為了保證讓該控件能擁有消費事件的能力勘天,以接受后續(xù)的事件怔揩。
2.3.2 onFling

Failing 中文直接翻譯過來就是一扔、拋脯丝、甩商膊,最常見的場景就是在 ListView 或者 RecyclerView 上快速滑動時手指抬起后它還會滾動一段時間才會停止。onFling 就是檢測這種手勢的宠进。

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float
        velocityY) {
    return super.onFling(e1, e2, velocityX, velocityY);
}

在 onFling 的回調(diào)中共有四個參數(shù)晕拆,分別是:


我們可以通過 e1 和 e2 獲取到手指按下和抬起時的坐標(biāo)、時間等相關(guān)信息材蹬,通過 velocityX 和 velocityY 獲取到在這段時間內(nèi)的運動速度实幕,單位是像素/秒(即 1 秒內(nèi)滑動的像素距離)。

這個我們自己用到的地方比較少堤器,但是也可以幫助我們簡單的做出一些有趣的效果昆庇,例如下面的這種彈球效果,會根據(jù)滑動的力度和方向產(chǎn)生不同的彈跳效果闸溃。



其實這種原理非常簡單整吆,簡化之后如下:

  1. 記錄 velocityX 和 velocityY 作為初始速度,之后不斷讓速度衰減辉川,直至為零表蝙。
  2. 根據(jù)速度和當(dāng)前小球的位置計算一段時間后的位置,并在該位置重新繪制小球乓旗。
  3. 判斷小球邊緣是否碰觸控件邊界府蛇,如果碰觸了邊界則讓速度反向。

根據(jù)這三條基本的邏輯就可以做出比較像的彈球效果寸齐,具體的Demo可以看這里欲诺。

2.3.3 onLongPress

這個是檢測長按事件的,即手指按下后不抬起渺鹦,在一段時間后會觸發(fā)該事件扰法。

@Override 
public void onLongPress(MotionEvent e) {
}
2.3.4 onScroll

onScroll 就是監(jiān)聽滾動事件的,它看起來和 onFaling 比較像毅厚,不同的是塞颁,onSrcoll 后兩個參數(shù)不是速度,而是滾動的距離。

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float 
        distanceY) {
    return super.onScroll(e1, e2, distanceX, distanceY);
}
2.3.5 onShowPress

它是用戶按下時的一種回調(diào)祠锣,主要作用是給用戶提供一種視覺反饋酷窥,可以在監(jiān)聽到這種事件時可以讓控件換一種顏色,或者產(chǎn)生一些變化伴网,告訴用戶他的動作已經(jīng)被識別蓬推。
不過這個消息和 onSingleTapConfirmed 類似,也是一種延時回調(diào)澡腾,延遲時間是 180 ms沸伏,假如用戶手指按下后立即抬起或者事件立即被攔截,時間沒有超過 180 ms的話动分,這條消息會被 remove 掉毅糟,也就不會觸發(fā)這個回調(diào)。

@Override 
public void onShowPress(MotionEvent e) {
}
2.3.6 onSingleTapUp
@Override 
public boolean onSingleTapUp(MotionEvent e) {
    return super.onSingleTapUp(e);
}

這個也很容易理解澜公,就是用戶單擊抬起時的回調(diào)姆另,但是它和上面的 onSingleTapConfirmed 之間有何不同呢?和 onClick 又有何不同呢坟乾?

單擊事件觸發(fā):

GCS: onSingleTapUp
GCS: onClick
GCS: onSingleTapConfirmed


雙擊事件觸發(fā):

GCS: onSingleTapUp
GCS: onClick
GCS: onDoubleTap // <- 雙擊
GCS: onClick

可以看出來這三個事件還是有所不同的迹辐,根據(jù)自己實際需要進行使用即可

2.4 SimpleOnGestureListener

這個里面并沒有什么內(nèi)容,只是對上面三種 Listener 的空實現(xiàn)糊渊,在上面的例子中使用的基本都是這監(jiān)聽器右核。因為它用起來更方便一點。
這主要是 GestureDetector 構(gòu)造函數(shù)的設(shè)計問題渺绒,以只監(jiān)聽 OnDoubleTapListener 為例贺喝,如果想要使用 OnDoubleTapListener 接口則需要這樣進行設(shè)置:

GestureDetector detector = new GestureDetector(this, new GestureDetector
        .SimpleOnGestureListener());
detector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
    @Override public boolean onSingleTapConfirmed(MotionEvent e) {
        Toast.makeText(MainActivity.this, "單擊確認", Toast.LENGTH_SHORT).show();
        return false;
    }

    @Override public boolean onDoubleTap(MotionEvent e) {
        Toast.makeText(MainActivity.this, "雙擊", Toast.LENGTH_SHORT).show();
        return false;
    }

    @Override public boolean onDoubleTapEvent(MotionEvent e) {
        // Toast.makeText(MainActivity.this,"",Toast.LENGTH_SHORT).show();
        return false;
    }
});

既然都已經(jīng)創(chuàng)建 SimpleOnGestureListener 了,再創(chuàng)建一個 OnDoubleTapListener 顯然十分浪費宗兼,如果構(gòu)造函數(shù)不使用 SimpleOnGestureListener躏鱼,而是使用 OnGestureListener 的話,會多出幾個無用的空實現(xiàn)殷绍,顯然很浪費染苛,所以在一般情況下,老老實實的使用 SimpleOnGestureListener 就好了主到。

3. 相關(guān)方法

除了各類監(jiān)聽器之外茶行,與 GestureDetector 相關(guān)的方法其實并不多,只有幾個登钥,下面來簡單介紹一下畔师。


結(jié)語

關(guān)于手勢檢測部分的 GestureDetector 相關(guān)內(nèi)容基本就這么多了,其實手勢檢測還有一個 ScaleGestureDetector 也是為手勢檢測服務(wù)的牧牢,限于篇幅看锉,本次就講這么多吧姿锭。

其實手勢檢測輔助類 GestureDetector 本身并不是很復(fù)雜,帶上注釋等內(nèi)容才不到1000行伯铣,感興趣的可以自己研究一下實現(xiàn)方式呻此。
參考資料
文檔 · GestureDetector
源碼 · GestureDetector

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腔寡,隨后出現(xiàn)的幾起案子焚鲜,更是在濱河造成了極大的恐慌,老刑警劉巖蹬蚁,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恃泪,死亡現(xiàn)場離奇詭異郑兴,居然都是意外死亡犀斋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門情连,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叽粹,“玉大人,你說我怎么就攤上這事却舀〕婕福” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵挽拔,是天一觀的道長辆脸。 經(jīng)常有香客問我,道長螃诅,這世上最難降的妖魔是什么啡氢? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮术裸,結(jié)果婚禮上倘是,老公的妹妹穿的比我還像新娘。我一直安慰自己袭艺,他們只是感情好搀崭,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猾编,像睡著了一般瘤睹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上答倡,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天轰传,我揣著相機與錄音,去河邊找鬼苇羡。 笑死绸吸,一個胖子當(dāng)著我的面吹牛鼻弧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锦茁,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼攘轩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了码俩?” 一聲冷哼從身側(cè)響起度帮,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稿存,沒想到半個月后笨篷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡瓣履,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年率翅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袖迎。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡冕臭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出燕锥,到底是詐尸還是另有隱情辜贵,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布归形,位于F島的核電站托慨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏暇榴。R本人自食惡果不足惜厚棵,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望跺撼。 院中可真熱鬧窟感,春花似錦、人聲如沸歉井。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哩至。三九已至躏嚎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間菩貌,已是汗流浹背卢佣。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像蛤铜,于是被迫代替她去往敵國和親嘹叫。 傳聞我的和親對象是個殘疾皇子婆殿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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