小豬淺談Android屏幕適配

tags: Tutorial


引言

??????國慶前在微信群里看到有人在問Android屏幕適配的問題,湊巧自己最近時間
略有閑暇,索性來談談Android中屏幕適配相關的一些內容吧,鄙人才疏學淺,
所說的都是自己認知范圍以內的,不可能面面俱到,如有疏漏不妥之處,還請
不吝指出,謝謝!配上最近被玩壞的Gif~


Android中的單位與名詞


1.相對單位與絕對單位

對于計量單位,人們日常習慣性分為「相對單位」與「絕對單位」兩類,前者
根據不同的情景表現出不同的大小,比如Android里的dp,px與sp等;而后者則是
制定了一個標準,cm(厘米),寫死了,就那么多,1cm什么情況下都是一樣大。

說到這個絕對單位,順帶提下兩個單位:

  • in(英寸)1 in = 2.54cm
  • pt(磅,印刷行業常用單位):1 pt = 1 / 72 in

2.px (pixel,像素)

就是一個個的像素點圖像的最小組成單元


3.dp (dip,密度無關像素)

Density-independent pixel,抽象意義上的像素與設備的實際物理像素點無關
可以保證在不同的像素密度的設備上顯示相同的效果,也是Android獨有的長度單位

1dp表示在屏幕像素密度為160dpi的屏幕上1dp = 1px
類推,在320dpi上,1dp = 2px,不難看出這樣的轉換公式:px = dp * (dpi / 160)


4.sp (sip,獨立比例像素)

scale-independent pixel,字體大小專用單位會根據系統設置的字體大小進行
縮放
,推薦使用12sp以上的字體(12sp以下太小),不推薦用奇數和小數,容易造成
精度丟失問題。

Tip

盡管官方建議我們字體都用sp作為單位,但是sp字體會根據系統設置的字體大小進行
縮放,假如用戶修改了系統字體大小,可能會導致我們APP的UI受到影響,比如字體
大了,然后各種顯示不全,個人還是建議使用dp來做為字體單位


5.手機外觀尺寸

整個手機的尺寸不止屏幕,一般在中關村或者其他評測網站都可以查到。
一般是用mm為單位,依次為長 x 寬 x 厚度,比如我的老掉牙的moto x 的尺寸就是:
159.3mm × 83mm × 10.1mm。


6.屏幕尺寸

屏幕對角線的長度,單位是英寸,計算公式如下:

其實就是勾股定理求對角線長度,比如長4寸,寬3寸的手機,他的屏幕

尺寸計算:

7.分辨率(Resolution)

屏幕豎直方向與水平方向的像素個數,比如:1280*720 就說明屏幕的
豎直方向上有1280個像素點,而水平方向上有720個像素點,單位px。


8.dpi與ppi

  • dpi:dot per inch,點密度每英寸多少個點,一般用作表示印刷品點密度;
  • ppi:pixels per inch,像素密度每英寸所包含的像素數目
    一般用作表示顯示設備的點密度。

兩者的值近乎相等,不用過于糾結!這個值的計算公式如下:


9.density(屏幕密度)

這個單位感覺是用來表示不同dpi的倍數關系,計算公式:density = dpi/160


10.常用單位轉換工具類

public class DensityUtil {
    public static int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public static int px2dp(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }

    public static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }
}


11.獲取屏幕尺寸與密度的三種方法

public class GetScreenParameter {  
    //方法一:已過時,可使用,但不建議使用  
    public static void getResolution1(Context mContext) {  
        Display mDisplay = ((Activity) mContext).getWindowManager()  
                .getDefaultDisplay();  
        int W = mDisplay.getWidth();  
        int H = mDisplay.getHeight();  
    }  
    
    //方法二:通過getWindowManager來獲取屏幕尺寸的  
    public static void getResolution2(Context mContext) {  
        DisplayMetrics mDisplayMetrics = new DisplayMetrics();  
        ((Activity) mContext).getWindowManager().getDefaultDisplay()  
                .getMetrics(mDisplayMetrics);  
        int W = mDisplayMetrics.widthPixels;  
        int H = mDisplayMetrics.heightPixels;  
        // 屏幕密度(0.75 / 1.0 / 1.5)        
        float density = mDisplayMetrics.density;
        // 就是屏幕密度 * 160而已,屏幕密度DPI(120 / 160 / 240)
        int densityDpi = mDisplayMetrics.densityDpi; 
    }  
    
    //方法三:通過getResources來獲取屏幕尺寸的,大部分用這個
    public static void getResolution3(Context mContext) {  
        DisplayMetrics mDisplayMetrics = new DisplayMetrics();  
        mDisplayMetrics = mContext.getResources().getDisplayMetrics();  
        int W = mDisplayMetrics.widthPixels;  
        int H = mDisplayMetrics.heightPixels;  
        float density = mDisplayMetrics.density;   
        int densityDpi = mDisplayMetrics.densityDpi; 
    }  
}  

Tip(可以不看,現在基本不會用這么小屏的手機了):

對于屏幕密度很低的小屏手機,比如240320,計算出來的尺寸,可能為:320427
原因是:沒有設置多分辨率支持的話,Android系統會將240x320的低密度(120)尺寸
轉換為中等密度160dpi對應的尺寸,如果你想獲得正確的物理尺寸,需要在
AndroidManifest.xml里添加下述代碼:

<supports-screens  
    android:smallScreens="true"  
    android:normalScreens="true"  
    android:largeScreens="true"  
    android:resizeable="true"  
    android:anyDensity="true"/>

Android適配準備


1.什么是Android適配

答:因為Android系統的開放性,很多廠商都喜歡對Android系統和硬件進行個性化定制,
以達到他們想要的樣子,這種結果帶來的「Android系統」和「手機屏幕」的碎片化問題
只對市場占用率較高的720和1080進行適配顯然是不夠的,為了讓我們的Android應用在各種
各樣的手機上保證界面效果一致,各種手機屏幕的適配顯得非常重要,也是每個開發者
為之頭痛的問題。


2.Android中的六種通用屏幕密度

屏幕密度 范圍(dpi) 標準分辨率 dp與px 圖標尺寸
ldpi(QVGA) ~ 120 240 * 320 1dp=0.75px 36 * 36
mdpi(HVGA) 120 ~ 160 320 * 480 1dp=1px 48 * 48
hdpi(WVGA) 160 ~ 240 480 * 800 1dp=1.5px 72 * 72
xhdpi(720P) 240 ~ 320 720 * 1280 1dp=2px 96 * 96
xxhdpi(1080p) 320 ~ 480 1080 * 1920 1dp=3px 144 * 144
xxxhdpi(2K) 480 ~ 640 1440 × 2560 1dp=4px 192 * 192

Tip:圖標大小 = px數 * 4 * 12


3.關于UI設計稿的適配

我們可以通過友盟的 全域羅盤 知道當前國內的移動設備使用情況,
以此了解需要適配的趨勢,以八月份的統計為例,截取屏幕尺寸與分辨率占比
拍醒前五的數據如下:

??

更多詳細信息可自行查看,從上面兩個表格不難看出這樣的趨勢:

5.5寸5寸 屏幕尺寸市場占比最高,而分辨率:1920x10801280x720
依舊是當前主流

也就是說讓設計師按照720p和1080p出兩套設計稿就可以適配大部分的
設備了,但是不同的公司因為人員或者時間等外部因素,可能有這幾
種情況:

  • 三套:720p,1080p,1440p,這是最理想的情況;
  • 兩套:720p,1080p,主流,大部分公司都或走兩套;
  • 一套:720p,又或者和iOS共用一套 750x1334,可以當做720p的設計稿
    來編寫UI,盡管比例和接近16:9,但是還是有些偏差,需要Android對UI進行微調!

UI能做的基本就到這里了,至于其他分辨率的手機則需要我們開通過
相關的手段來適配了。


Android適配開始


1.最簡單一些適配技巧

先來說幾個爛大街的適配技巧吧

1) 使用dp而非px

dp是像素無關的,而在實際使用中1dp大約等于1/160 in,比如一個160dp * 160dp
的控件,在大多數的屏幕上都能保持1 in * 1 in 的大小。

但是,并不是能解決所有問題的,以下兩點要注意:

  • 1.實際效果還是會有些差距的,僅僅是相近而已;
  • 2.當設備的物理尺寸存在差異的時候,dp就顯得無能為力了。
    比如,為4.3寸屏幕準備的UI,運行在5.0寸的屏幕上,很可能在
    右側和下側存在大量的空白;而5.0寸的UI運行到4.3寸的設備上,很可能顯示不下。

2) 少寫固定尺寸

少寫固定尺寸,而使用 wrap_content, match_parentweight 權重 」


3) 使用相對布局,不要使用絕對布局

常識,而且絕對布局基本退出歷史舞臺了,可以忽略...


4) 自動拉伸的.9圖

常識,.9圖的作用是:拉伸的時候特定的區域不會發生圖片失真,而不失真的區域
可以由我們自己繪制,從而實現圖片適配。


5) 使用shape代替純色圖片

常識,一些純色的矩形,圓角,圓都可以通過編寫shape文件來替換,比起png,
xml文件小太多。


6)使用SVG矢量圖替換位圖

可能有些朋友對SVG矢量圖有些陌生,其實和普通的位圖最大的區別就是:
SVG是通過「XML文件」來定義一個圖形,通過一些特定的語法和規則來繪制
出我們所需的圖像,而不是位圖那樣通過存儲圖像中每一點的像素值來保存
與使用圖形。

SVG是已經定義好怎么畫這個圖,需要的時候再去畫,因為是按照特定的語法
和規則,理論上支持任何級別的縮放,而且不會失真,相比起多套同樣的位圖
文件,方便太多。

矢量圖雖好,但是有幾點要注意的:

  • 1.適用于Android 5.0以上,盡管官方有兼容包,低版本還是會有些問題的!
  • 2.不適合細節過于復雜的圖片
  • 3.因為是用到的時候才畫,所以加載圖片所消耗的時間和資源可能會增加。

至于怎么用這個矢量圖,你可以讓美工在PS里把圖片導出為SVG/PSD格式,然后
AS里右鍵drawable文件夾:

選中本地文件,確定

然后AS會幫你自動生成一個矢量xml文件,比如我的一個tab圖標

右側可以看到預覽圖:

然后要用到圖片的地方直接引用即可。
至于SVG的規則以及如何自行編寫不在本節重點,有興趣的可自行搜索用法~


2.多套資源文件的套路

常用的套路依舊不能解決大部分的屏幕適配問題,Android給我們提供了
備用資源這個東西,詳細可見官網:提供備用資源
簡單說就是按照規范,創建屏幕對應的資源文件夾和資源文件,
Android會自動去加載對應文件夾里的資源的值,可能表述得有些不清晰,
舉個簡單的例子:

比如我們xxhdpi,padding為3dp,而在xxxhdpi下的padding為5dp
我們就可以創建一個對應的values文件夾和dimens.xml文件

相信你看到這里就明了,官網寫的這個也就清晰了

而這個qualifier限定符怎么拼接可以看官網,或者看下面的簡表:

看到這里,你可能會有問題,我要寫多少套?
講真,我也不知道,只能說看需要,但是想跟你說限定符里一個很常用的:smallestWidth
屏幕區域的最小尺寸,比如 values-sw320dp,只有在屏幕寬度不小于
320dp的才會使用這個文件夾里的dimens.xml文件,我們還可以另外建立一個
values-sw360dp,然后320dp那個文件夾只有在屏幕寬度在320dp到360dp
內才回去調用了,drawable,layout等資源文件也是同樣的套路。


3.使用腳本對長度按照不同分辨率進行比例轉換

就是拿屏幕寬度,去除以360,得出比例去乘以對應的dp值
比如:sw720dp里一個單位的1 dp = 2dp,而sw800dp里的1 dp = 2.22dp:

Github地址:https://github.com/mengzhinan/PhoneScreenMatch

具體的自己看文檔和代碼吧,可能有的疑問就是不是win電腦執行不了bat文件
的問題,比如mac,直接命令行,cd到 PhoneScreenMatch/app/src/main
目錄下,鍵入下述命令即可:

java -jar screenMatchPX.jar

項目中還提供了一個px適配的,分開寬高做等值縮放的,對于長寬比很奇葩
的機子可以試試。當然這種適配方式帶來的最明顯的問題就是資源文件增加
的問題,這需要自行權衡。

類似的方案還有:https://github.com/paulyung541/EasyScreen


4.[過時] 百分比布局庫支持庫

這個庫API 26.0.0 后的版本已經棄用了,在官方倉庫可以看到

[DEPRECATED in support lib v26] you should now use ConstraintLayout widget

如果你想使用百分比來編寫建議使用約束布局 ConstraintLayout
所以android-percent-support怎么用并不講,想通過看看源碼的方式
來了解百分比的實現套路,沒興趣的可以跳過了,或者看其他關于
百分比布局支持庫的其他使用擴展文章:

源碼過一過 (流程參照CSDN張鴻洋的博文)

lib庫里的東西不多,就三個核心的類而已

如果讓我們來做套路可能是:

  • 通過LayoutParams獲取布局中設置的與百分比有關的屬性
  • 父布局拿到這些屬性值后進行動態計算,比如寬*寬占百分比
  • 子控件調用measure(計算后值)進行繪制。

看下代碼具體是怎么做的,首先是獲取Layout里子控件的屬性,打開
PercentFrameLayout,重寫了generateLayoutParams方法,
然后新建了一個LayoutParams對象

看看這個LayoutParams里賣的什么藥:

通過PercentLayoutHelpergetPercentLayoutInfo方法構造一個
PercentLayoutInfo對象,而getPercentLayoutInfo里做的事則
是把attrs里的東東都拿出來,然后塞到PercentLayoutInfo對象用于返回:

而這個PercentLayoutInfo里定義了一堆百分比相關的屬性,
還有一些方法,字面上大概可以知道是設置LayoutParams相關
的方法,就是把onMeasure里的邏輯操作抽取到了這里。

好的,屬性拿到了,接著就是跟onMeasure方法了,調用了
PercentLayoutHelper對象里的adjustChildren方法

方法里做的東西也比較簡單,拿到布局的寬高,然后
循環遍歷布局里的子控件,如果params是百分比類型的
取出PercentLayoutInfo里info,然后去設置寬,高和margin。

再跟下fillLayoutParams方法,保存原本寬高后(后面用來重置),重新設置寬高,
fillMarginLayoutParams也是類似,百分比的基本套路就已經完了。

不過項目中還多了兩個細節,計算值過小的情況以及重置原布局尺寸
onMeasure最后還調用handleMeasuredStateTooSmall方法,而方法做的
事是出去容器中所有的子控件,如果params是百分比類型的,則調用
shouldHandleMeasuredWidthTooSmall方法判斷值是不是太小,太小則
把寬或高設置為ViewGroup.LayoutParams.WRAP_CONTENT(圖中 = -2那里)
needsSecondMeasure設置為true,代表需要二次重繪。

重置布局尺寸的代碼則是寫在onLayout方法里(onMeasure后會回調)

一樣是遍歷子控件,然后把參數都重置為之前存mPreservedParams里的值,
而這個值是布局里設置的0dp。(感覺沒什么用處...)

因為onMeasure的關鍵代碼都寫到Helper類里,擴展一個百分比的線性
布局也很簡單,拷貝下PercentFrameLayout改點東西就好。
另外,因為依賴于父容器,導致ScrollView,ListView等容器內高度無法使用百分比
而鴻洋還另外對官方的百分比進行了擴展,寫了一個android-percent-support-extend
有興趣可以移步到他的博客閱讀:Android 增強版百分比布局庫 為了適配而擴展


5.[作者停止維護] AndroidAutoLayout

又是鴻洋大神的庫,不過作者已經不維護了,使用之前還是三思!!!
博客鏈接:http://blog.csdn.net/lmj623565791/article/details/49990941

大概套路是

定好設計稿尺寸,直接選擇對應分辨率的預覽,AndroidAutoLayout 庫會根據
占屏比例,自動計算轉換當前屏幕下適配的大小。加載布局其實是把各自的
Layout都轉換為對應的AutoLayout,從而不用在所有的xml中進行更改。

思路很gay,但是也有一些弊端,在onMeasure的時候進行數值計算,存在性能問題
擴展性差,用到的ViewGroup都需要自行對AutoLayout進行擴展。
最重要的是:

issues較多,而且作者不維護,應用到實際項目中還需自行權衡...


6.ConstraintLayout 約束布局

使用約束的方式來指定各個控件的位置和關系的,可以看做更加強大的
RelativeLayout,通過圖形化界面拖拽的方式來編寫界面,當然也可以
直接用XML進行編寫,畢竟拖拽的背后也是XML來實現的,不過屬性比較
多,直接編寫顯得有些繁瑣,建議還是拖拽后自己在手動修改參數

ConstraintLayout約束布局除了支持百分比外,相比傳統布局一層
套一層的嵌套布局結構,編寫的XML元素層次結構更簡單(一個外層
ConstraintLayout包全部),而且性能更優。
至于問題的話,可能就是布局中的元素過于復雜時,拖拽可能會有少許問題;

其他的話暫時沒發現,關于ConstraintLayout與傳統布局方式的性能對比可見:
解析ConstraintLayout的性能優勢

而關于圖形化拖拽的,郭霖大神有篇詳細講解的博文:
Android新特性介紹,ConstraintLayout完全解析

而關于屬性講解和具體實例的,可以翻看鴻洋的博文:
ConstraintLayout 完全解析 快來優化你的布局吧

附:記下關鍵點,方便自己日后回憶

  • Inspector:右側Properties區域的上半部分,豎直和水平的軸用于確定
    位置;圖中的四個16確定的是間距;中間的四個 >>> 的箭頭確定的是大小
    有三種可選的模式:
    wrap_content、
    固定值、
    any size
    用于填充滿當前控件的約束規則,和match_parent是不同的!

?

  • Guidelines:參照線
    ,就是弄個線給別的控件提供約束,有垂直和水平
    兩種,用來實現一些百分比的效果很贊,比如下圖這種兩個控件并排居中的布局:

    ?
  • 自動添加約束:拖拽控件后,一個個去添加約束非常繁瑣,ConstraintLayout中
    支持自動添加約束,有兩種自動添加約束的方式:
    AutoConnect:默認關閉,啟用后拖拉控件到布局里會自動生成約束

    Infer Constraints:控件拖拉得差不多后,點擊后會為所有控件生成約束

    當然,這兩種自動生成的約束可能有些問題,我們可以自己另外調整下~

7.等比例縮放的另一種實現方案

同樣是等比例縮放的套路,原文可見:一種粗暴快速的Android全屏幕適配方案

上面也說了AndroidAutoLayout在onMeasure中進行數值運算可能會有性能問題。
而這個方案則是規避了這個問題,直接在Android進行長度計算的時候就進行換算;

系統長度計算的入口是TypedValue里的applyDimension方法:

public static float applyDimension(int unit, float value, DisplayMetrics metrics){
    switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP:
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }
}

而作者選擇比較冷門的pt單位,從而避免對正常的px,dp,sp造成影響,
PT的計算為:value * metrics.xdpi * (1.0f/72),我們可以動前面
metrics.xdpi,這個值應改為:縮放比例7272的原因是想轉px,
metrics可以通過context.getResources().getDisplayMetrics()
拿到。作者另外寫了一個Helper類,通過構造方法傳入設計稿的寬度除以
當前設備的分辨率得出比例,然后乘以72即可得出對應分辨率下的尺寸。

調用也很簡單,直接在Application類啟用即可:

new RudenessScreenHelper(this, 750).activate();

等比縮放套路換湯不換藥,不過很機智的規避了復雜的計算問題,想法是挺贊的,
有興趣的可以試試。


小結


??????在本節中,筆者對于自己所知的Android原生適配套路都一一進行了復述,相信會對
你在開發中的屏幕適配有所幫助,當然具體要怎么適配還是看需求吧,比如我在的
小作坊,也是只是適配720p,2333;當然有其他的套路也歡迎告知,萬分感激~
(PS:說來慚愧,本來想著國慶能發的,后面因為各種瑣事拖到今天...)


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容