自定義View之事件分發(fā)

本篇文章我們專(zhuān)門(mén)來(lái)研究一下view層的事件分發(fā)機(jī)制帝火,我們?cè)趯W(xué)習(xí)過(guò)程中總會(huì)碰到關(guān)于事件分發(fā)的各種問(wèn)題烹困,如onTouch和onTouchEvent的關(guān)系嘲更,setOnTouchListener和setOnClickListener的關(guān)系等等揪漩,類(lèi)似這樣的問(wèn)題很多畜眨,結(jié)論我們都知道鸣驱,有的時(shí)候是死記硬背的泛鸟,記不長(zhǎng)久,本篇文章我們來(lái)從源碼的角度來(lái)分析總結(jié)一下各種關(guān)系踊东,這樣才能理解北滥,便于記憶。

分析工具

//Android源碼環(huán)境
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"    
}

//分析工具
Android Studio 2.2.3
Build #AI-145.3537739, built on December 2, 2016
JRE: 1.8.0_112-release-b05 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o

接下來(lái)我們正式分析一下view層的事件分發(fā)的源碼闸翅。首先要知道一點(diǎn)再芋,對(duì)于view層次的,事件分發(fā)主要有兩個(gè)方法坚冀,dispatchTouchEve和onTouchEvent济赎,我們主要對(duì)這兩種方法進(jìn)行分析。

一记某、實(shí)例引入

我們先通過(guò)自定義一個(gè)button來(lái)進(jìn)行分析司训。自定義的button很簡(jiǎn)單,就是重寫(xiě)了一下dispatchTouchEve和onTouchEvent兩個(gè)方法液南。

public class MyButton extends Button {

    protected static final String TAG = "liji-view-test";

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

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        int action = event.getAction();

        switch (action)
        {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onTouchEvent ACTION_UP");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event)
    {
        int action = event.getAction();

        switch (action)
        {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "dispatchTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "dispatchTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "dispatchTouchEvent ACTION_UP");
                break;

            default:
                break;
        }
        return super.dispatchTouchEvent(event);
    }

}

自定義的MyButton很簡(jiǎn)單壳猜,就是重寫(xiě)了view的兩個(gè)方法,我們?cè)谶@兩個(gè)方法中只進(jìn)行一些log操作滑凉,其他不改變统扳。接著我們?cè)赼ctivity中使用這個(gè)自定義的MyButton喘帚。

        mMyButton = (MyButton) findViewById(R.id.myButton);
        mMyButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG,"onClick button click");
            }
        });

        mMyButton.setOnTouchListener(new View.OnTouchListener()
        {
            @Override
            public boolean onTouch(View v, MotionEvent event)
            {
                int action = event.getAction();

                switch (action)
                {
                    case MotionEvent.ACTION_DOWN:
                        Log.d(TAG, "onTouch ACTION_DOWN");
                        break;
                    case MotionEvent.ACTION_MOVE:
                        Log.d(TAG, "onTouch ACTION_MOVE");
                        break;
                    case MotionEvent.ACTION_UP:
                        Log.d(TAG, "onTouch ACTION_UP");
                        break;
                    default:
                        break;
                }

                return false;
            }
        });

可以看到,在activity中我們也處理了兩個(gè)方法咒钟,一個(gè)是setOnTouchListener吹由、一個(gè)是setOnClickListener,然后運(yùn)行一下朱嘴,我們可以看看log結(jié)果是什么倾鲫。

D/liji-view-test: dispatchTouchEvent ACTION_DOWN
D/liji-view-test: onTouch ACTION_DOWN
D/liji-view-test: onTouchEvent ACTION_DOWN
D/liji-view-test: dispatchTouchEvent ACTION_MOVE
D/liji-view-test: onTouch ACTION_MOVE
D/liji-view-test: onTouchEvent ACTION_MOVE
D/liji-view-test: dispatchTouchEvent ACTION_MOVE
D/liji-view-test: onTouch ACTION_MOVE
D/liji-view-test: onTouchEvent ACTION_MOVE
D/liji-view-test: dispatchTouchEvent ACTION_MOVE
D/liji-view-test: onTouch ACTION_MOVE
D/liji-view-test: onTouchEvent ACTION_MOVE
D/liji-view-test: dispatchTouchEvent ACTION_MOVE
D/liji-view-test: onTouch ACTION_MOVE
D/liji-view-test: onTouchEvent ACTION_MOVE
D/liji-view-test: dispatchTouchEvent ACTION_UP
D/liji-view-test: onTouch ACTION_UP
D/liji-view-test: onTouchEvent ACTION_UP
D/liji-view-test: onClick button click

可以大概看出來(lái)事件響應(yīng)的順序是:

dispatchTouchEvent -> onTouch -> onTouchEvent -> onClick

從上面的log可以看出來(lái),onTouch是優(yōu)先于onClick執(zhí)行的腕够,并且onTouch執(zhí)行了多次级乍,一次是ACTION_DOWN舌劳,一次是ACTION_UP帚湘,還有幾次是ACTION_MOVE。因此事件傳遞的順序是先經(jīng)過(guò)onTouch甚淡,再傳遞到onClick大诸。

onTouch方法是有返回值的,如果我們嘗試把onTouch方法里的返回值改成true,再運(yùn)行一次就會(huì)發(fā)現(xiàn)onClick方法不再執(zhí)行了,這是因?yàn)閛nTouch方法返回true就認(rèn)為這個(gè)事件被onTouch消費(fèi)掉了贯卦,因而不會(huì)再繼續(xù)向下傳遞资柔。

這其中的緣由究竟是怎么樣的?我們通過(guò)源碼來(lái)一探究竟撵割。view事件分發(fā)的順序是從dispatchTouchEvent開(kāi)始的贿堰,所以我們就從它開(kāi)始分析:

二、源碼探究

首先我們進(jìn)入view的dispatchTouchEvent方法中查看啡彬。

    //view.java
    public boolean dispatchTouchEvent(MotionEvent event) {

        //...

        boolean result = false;

        //...

        if (onFilterTouchEventForSecurity(event)) {

            //...

            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

       //...

        return result;
    }

我們省略了其中無(wú)關(guān)的代碼羹与,只看對(duì)分析有用的代碼,我們進(jìn)入到if中去庶灿,首先看到一個(gè)對(duì)象ListenerInfo的li對(duì)象指的是什么纵搁,

    static class ListenerInfo {    
        protected OnFocusChangeListener mOnFocusChangeListener;
        protected OnScrollChangeListener mOnScrollChangeListener;
        public OnClickListener mOnClickListener;
        protected OnLongClickListener mOnLongClickListener;
        private OnKeyListener mOnKeyListener;
        private OnTouchListener mOnTouchListener;
        //...     
    }

看到?jīng)]有往踢,其實(shí)這個(gè)li指的就是我們?cè)O(shè)置的一些監(jiān)聽(tīng)器腾誉,包括onTouchListener、onClickListener等等峻呕,我們接著分析if中的條件

            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

可以確認(rèn)這里面的li!=null利职,所以第一個(gè)條件為true,第二個(gè)條件我們因?yàn)樵O(shè)置了onTouchListener事件監(jiān)聽(tīng)瘦癌,所以這里面的li.mOnTouchListener != null也是為true猪贪,再看第三個(gè)條件(mViewFlags & ENABLED_MASK) == ENABLED,因?yàn)槲覀兊腷utton是可以點(diǎn)擊的佩憾,所以這里面也是為true哮伟,如果碰到不可點(diǎn)擊的干花,如ImageView,這里面就是false了楞黄,我們到時(shí)候另外再談池凄,我們接著看下面一句代碼。

li.mOnTouchListener.onTouch(this, event))

這句代碼說(shuō)明什么鬼廓?如果我們?cè)趕etOnTouchListener里面返回true的話肿仑,那么我們將直接返回result=true了,如果返回了false的話碎税,那么這個(gè)if條件就不成立尤慰,所以它將會(huì)執(zhí)行下一行代碼if語(yǔ)句端判斷-即它將會(huì)執(zhí)行onTouchEvent事件

            if (!result && onTouchEvent(event)) {
                result = true;
            }

因?yàn)槲覀兌际窃O(shè)置的默認(rèn)返回值,所以在一開(kāi)始的時(shí)候我們的log日志顯示的順序是:

dispatchTouchEvent -> onTouch -> onTouchEvent -> onClick

這個(gè)時(shí)候就看看onTouch返回結(jié)果了雷蹂,返回的結(jié)果不同導(dǎo)致的順序也不同伟端。我們接著看看onTouchEvent的源碼,分析一下里面藏了什么東西匪煌。

    public boolean onTouchEvent(MotionEvent event) {
        //...

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:

                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        boolean focusTaken = false;

                      //...

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {

                            if (!focusTaken) {

                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }
                    //...

                    break;

                case MotionEvent.ACTION_DOWN:
                    //...
                    break;

                case MotionEvent.ACTION_CANCEL:
                       //...
                    break;

                case MotionEvent.ACTION_MOVE:
                   //...

                    break;
            }

            return true;
        }

        return false;
    }

我們?cè)趏nTouchEvent方法中查看一下责蝠,省略一些無(wú)關(guān)的代碼,我們發(fā)現(xiàn)了其中有一個(gè)方法就是在手指松開(kāi)的時(shí)候action=MotionEvent.ACTION_UP的時(shí)候萎庭,會(huì)調(diào)用這個(gè)performClick方法霜医。我們進(jìn)入performClick方法中繼續(xù)查看

    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

看到?jīng)]?這里面就涉及到了onClick事件了驳规,這也間接的證明了肴敛,onTouch的事件優(yōu)先級(jí)高于onClick的優(yōu)先級(jí)。

到了這里吗购,我們就可以總結(jié)一下關(guān)于一開(kāi)始提出來(lái)的幾個(gè)問(wèn)題:

1医男、onTouch和onTouchEvent有什么區(qū)別,又該如何使用巩搏?

從源碼中可以看出昨登,這兩個(gè)方法都是在View的dispatchTouchEvent中調(diào)用的,onTouch優(yōu)先于onTouchEvent執(zhí)行贯底。如果在onTouch方法中通過(guò)返回true將事件消費(fèi)掉丰辣,onTouchEvent將不會(huì)再執(zhí)行。

另外需要注意的是禽捆,onTouch能夠得到執(zhí)行需要兩個(gè)前提條件笙什,第一mOnTouchListener的值不能為空,第二當(dāng)前點(diǎn)擊的控件必須是enable的胚想。因此如果你有一個(gè)控件是非enable的琐凭,那么給它注冊(cè)onTouch事件將永遠(yuǎn)得不到執(zhí)行(&&操作符,如果前面的判斷為false的話浊服,后面就不判斷了)统屈。對(duì)于這一類(lèi)控件胚吁,如果我們想要監(jiān)聽(tīng)它的touch事件,就必須通過(guò)在該控件中重寫(xiě)onTouchEvent方法來(lái)實(shí)現(xiàn)愁憔。

2腕扶、onTouch和onClick優(yōu)先級(jí)

我們從源碼中也可以分析得到:onTouch的優(yōu)先級(jí)高于onClick的優(yōu)先級(jí),其中onClick的事件是在onTouchEvent中產(chǎn)生的吨掌。

判斷是否發(fā)生onTouchEvent事件的條件有三個(gè)半抱。(1)設(shè)置OnTouchListener監(jiān)聽(tīng),(2)該view是否是enable的膜宋,(3)在onTouch方法中返回true

如果上述三個(gè)條件有一個(gè)沒(méi)有滿足即為FALSE的話窿侈,那么它將執(zhí)行onTouchEvent事件同時(shí)將產(chǎn)生onClick事件。

3秋茫、touch事件的層級(jí)傳遞

我們都知道如果給一個(gè)控件注冊(cè)了touch事件史简,每次點(diǎn)擊它的時(shí)候都會(huì)觸發(fā)一系列的ACTION_DOWN,ACTION_MOVE学辱,ACTION_UP等事件乘瓤。這里需要注意环形,如果你在執(zhí)行ACTION_DOWN的時(shí)候返回了false策泣,后面一系列其它的action就不會(huì)再得到執(zhí)行了。簡(jiǎn)單的說(shuō)抬吟,就是當(dāng)dispatchTouchEvent在進(jìn)行事件分發(fā)的時(shí)候萨咕,只有前一個(gè)action返回true,才會(huì)觸發(fā)后一個(gè)action火本。

說(shuō)到這里危队,很多的朋友肯定要有巨大的疑問(wèn)了。這不是在自相矛盾嗎钙畔?前面的例子中茫陆,明明在onTouch事件里面返回了false,ACTION_DOWN和ACTION_UP不是都得到執(zhí)行了嗎擎析?其實(shí)你只是被假象所迷惑了簿盅,讓我們仔細(xì)分析一下,在前面的例子當(dāng)中揍魂,我們到底返回的是什么桨醋。參考著我們前面分析的源碼,首先在onTouch事件里返回了false现斋,就一定會(huì)進(jìn)入到onTouchEvent方法中喜最,然后我們來(lái)看一下onTouchEvent方法的細(xì)節(jié)。由于我們點(diǎn)擊了按鈕庄蹋,就會(huì)進(jìn)入到第14行這個(gè)if判斷的內(nèi)部瞬内,然后你會(huì)發(fā)現(xiàn)迷雪,不管當(dāng)前的action是什么,最終都一定會(huì)走到第89行虫蝶,返回一個(gè)true振乏。是不是有一種被欺騙的感覺(jué)?明明在onTouch事件里返回了false秉扑,系統(tǒng)還是在onTouchEvent方法中幫你返回了true慧邮。就因?yàn)檫@個(gè)原因,才使得前面的例子中ACTION_UP可以得到執(zhí)行舟陆。

那我們可以換一個(gè)控件误澳,將按鈕替換成ImageView,然后給它也注冊(cè)一個(gè)touch事件秦躯,并返回false忆谓。在ACTION_DOWN執(zhí)行完后,后面的一系列action都不會(huì)得到執(zhí)行了踱承。這又是為什么呢倡缠?因?yàn)镮mageView和按鈕不同,它是默認(rèn)不可點(diǎn)擊的茎活,因此在onTouchEvent的內(nèi)部判斷時(shí)無(wú)法進(jìn)入到if的內(nèi)部昙沦,直接跳到第最后面返回了false,也就導(dǎo)致后面其它的action都無(wú)法執(zhí)行了载荔。

三盾饮、總結(jié)

接下來(lái)我們來(lái)總結(jié)一下各個(gè)事件發(fā)生的流程。

針對(duì)于view來(lái)說(shuō)懒熙,當(dāng)發(fā)生一個(gè)事件時(shí)(譬如:onTouch事件)丘损,這個(gè)時(shí)候就會(huì)調(diào)用view的dispatchTouchEvent事件,它擁有boolean類(lèi)型的返回值工扎,當(dāng)返回為true時(shí)徘钥,順序下發(fā)會(huì)中斷,也就是說(shuō)肢娘,這個(gè)onTouch事件是不會(huì)繼續(xù)執(zhí)行下去了呈础,就執(zhí)行完一個(gè)dispatchTouchEvent事件,當(dāng)它返回false時(shí)事件繼續(xù)傳遞到onTouchListener中蔬浙,這個(gè)onTouchListener(onTouch事件)也是一個(gè)擁有boolean類(lèi)型的返回值的方法猪落,默認(rèn)返回false,這個(gè)時(shí)候就可以繼續(xù)執(zhí)行onClick(在onTouchEvent事件中)事件了畴博,如果onTouch事件返回了true笨忌,那么就代表這個(gè)事件被它自己給消耗掉了,不會(huì)再繼續(xù)傳遞俱病。

用一張圖來(lái)表示下:

image.png

對(duì)于View中的dispatchTouchEvent方法官疲,在這個(gè)方法內(nèi)袱结,首先是進(jìn)行了一個(gè)判斷,里面有三個(gè)條件途凫,如果這三個(gè)條件都滿足垢夹,就返回true,否則就返回onTouchEvent方法執(zhí)行的結(jié)果维费。對(duì)于第一個(gè)條件是一個(gè)mOnTouchListener變量果元,這個(gè)變量是在View中的setOnTouchListener方法里賦值的,也就是說(shuō)只要我們給控件注冊(cè)了touch事件犀盟,mOnTouchListener就一定被賦值了而晒。第二個(gè)條件是判斷當(dāng)前點(diǎn)擊的控件是否是enable的,按鈕默認(rèn)都是enable的阅畴,因此這個(gè)條件恒定為true倡怎。第三個(gè)條件最為關(guān)鍵,mOnTouchListener.onTouch(this, event)贱枣,其實(shí)也就是去回調(diào)控件注冊(cè)touch事件時(shí)的onTouch方法监署。也就是說(shuō)如果我們?cè)趏nTouch方法里返回true,就會(huì)讓這三個(gè)條件全部成立纽哥,從而整個(gè)方法直接返回true钠乏。如果我們?cè)趏nTouch方法里返回false,就會(huì)再去執(zhí)行onTouchEvent(event)方法昵仅。

到這里缓熟,整個(gè)view的事件分發(fā)就比較清楚了,接下來(lái)我們分析關(guān)于viewGroup的事件分發(fā)了摔笤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垦写,隨后出現(xiàn)的幾起案子吕世,更是在濱河造成了極大的恐慌,老刑警劉巖梯投,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件命辖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡分蓖,警方通過(guò)查閱死者的電腦和手機(jī)尔艇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)么鹤,“玉大人终娃,你說(shuō)我怎么就攤上這事≌籼穑” “怎么了棠耕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵余佛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我窍荧,道長(zhǎng)辉巡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任蕊退,我火速辦了婚禮郊楣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓤荔。我一直安慰自己痢甘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布茉贡。 她就那樣靜靜地躺著塞栅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腔丧。 梳的紋絲不亂的頭發(fā)上放椰,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音愉粤,去河邊找鬼砾医。 笑死,一個(gè)胖子當(dāng)著我的面吹牛衣厘,可吹牛的內(nèi)容都是我干的如蚜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼影暴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼错邦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起型宙,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤撬呢,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后妆兑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體魂拦,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年搁嗓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芯勘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腺逛,死狀恐怖荷愕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤路翻,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布狈癞,位于F島的核電站,受9級(jí)特大地震影響茂契,放射性物質(zhì)發(fā)生泄漏蝶桶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一掉冶、第九天 我趴在偏房一處隱蔽的房頂上張望真竖。 院中可真熱鬧,春花似錦厌小、人聲如沸恢共。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)讨韭。三九已至,卻和暖如春癣蟋,著一層夾襖步出監(jiān)牢的瞬間透硝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工疯搅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留濒生,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓幔欧,卻偏偏與公主長(zhǎng)得像罪治,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子礁蔗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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