其實你不懂:Drawable著色(tint)的兼容方案 源碼解析

前兩天寫一個自定義控件,使用Drawable變色來展示EditText的不同狀態(tài)鳍征,涉及到了DrawableCompat這個類,今天著重分析一下它。

1:Drawable變色的通用代碼

//1:通過圖片資源文件生成Drawable實例
Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher).mutate();
//2:先調(diào)用DrawableCompat的wrap方法
drawable = DrawableCompat.wrap(drawable);
//3:再調(diào)用DrawableCompat的setTint方法绰疤,為Drawable實例進行著色
DrawableCompat.setTint(drawable, Color.RED);

這里涉及幾個方法:

  • Drawable.mutate()
  • DrawableCompat.wrap(@NonNull Drawable drawable)
  • DrawableCompat.setTint(@NonNull Drawable drawable, @ColorInt int tint)

下面看一下這幾個方法的源碼,Drawable.mutate()稍后分析舞终,先看一下DrawableCompat中的wrap和setTint這兩個方法轻庆。

2:DrawableCompat.wrap(@NonNull Drawable drawable)

public static Drawable wrap(@NonNull Drawable drawable) {
  return IMPL.wrap(drawable);
}

從源碼可見,wrap方法內(nèi)部是return IMPL.wrap(drawable)敛劝,那這個IMPL是余爆?

    static final DrawableImpl IMPL;
    /**
     * Interface for the full API.
     */
    interface DrawableImpl {
        void jumpToCurrentState(Drawable drawable);
        void setAutoMirrored(Drawable drawable, boolean mirrored);
        boolean isAutoMirrored(Drawable drawable);
        void setHotspot(Drawable drawable, float x, float y);
        void setHotspotBounds(Drawable drawable, int left, int top, int right, int bottom);
        void setTint(Drawable drawable, int tint);
        void setTintList(Drawable drawable, ColorStateList tint);
        void setTintMode(Drawable drawable, PorterDuff.Mode tintMode);
        Drawable wrap(Drawable drawable);
        boolean setLayoutDirection(Drawable drawable, int layoutDirection);
        int getLayoutDirection(Drawable drawable);
        int getAlpha(Drawable drawable);
        void applyTheme(Drawable drawable, Resources.Theme t);
        boolean canApplyTheme(Drawable drawable);
        ColorFilter getColorFilter(Drawable drawable);
        void clearColorFilter(Drawable drawable);
        void inflate(Drawable drawable, Resources res, XmlPullParser parser, AttributeSet attrs,
                     Resources.Theme t) throws IOException, XmlPullParserException;
    }
    static {
        final int version = android.os.Build.VERSION.SDK_INT;
        if (version >= 23) {
            IMPL = new MDrawableImpl();
        } else if (version >= 21) {
            IMPL = new LollipopDrawableImpl();
        } else if (version >= 19) {
            IMPL = new KitKatDrawableImpl();
        } else if (version >= 17) {
            IMPL = new JellybeanMr1DrawableImpl();
        } else if (version >= 11) {
            IMPL = new HoneycombDrawableImpl();
        } else {
            IMPL = new BaseDrawableImpl();
        }
    }

可見,在不同的SDK版本下夸盟,IMPL對應DrawableImpl的不同子類實例蛾方。下面分別看一下這幾個實現(xiàn)類對wrap方法的實質(zhì)執(zhí)行代碼。

  • MDrawableImpl
    public Drawable wrap(Drawable drawable) {
    // No need to wrap on M+
    //未對Drawable實例做任何處理上陕,直接返回
    return drawable;
    }
    可見在SDK版本>= 23(MDrawableImpl)情況下:DrawableCompat.wrap(@NonNull Drawable drawable)直接返回了原始的Drawable實例
  • LollipopDrawableImpl
    public Drawable wrap(Drawable drawable) {
    return DrawableCompatLollipop.wrapForTinting(drawable);
    }
    繼續(xù)跟蹤DrawableCompatLollipop.wrapForTinting:
    public static Drawable wrapForTinting(final Drawable drawable) {
    if (!(drawable instanceof TintAwareDrawable)) {
    //當前傳入的Drawable實例并不屬于TintAwareDrawable
    return new DrawableWrapperLollipop(drawable);
    }
    return drawable;
    }
    繼續(xù)跟蹤DrawableWrapperLollipop:
    class DrawableWrapperLollipop extends DrawableWrapperKitKat {
    DrawableWrapperLollipop(Drawable drawable) {
    super(drawable);
    }
    ***
    }
    繼續(xù)跟蹤DrawableWrapperKitKat:
    class DrawableWrapperKitKat extends DrawableWrapperHoneycomb {
    DrawableWrapperKitKat(Drawable drawable) {
    super(drawable);
    }
    ***
    }
    繼續(xù)跟蹤DrawableWrapperHoneycomb:
    class DrawableWrapperHoneycomb extends DrawableWrapperGingerbread {
    DrawableWrapperHoneycomb(Drawable drawable) {
    super(drawable);
    }
    ***
    }
    繼續(xù)跟蹤DrawableWrapperGingerbread:
    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
    private int mCurrentColor;
    private PorterDuff.Mode mCurrentMode;
    private boolean mColorFilterSet;
    DrawableWrapperState mState;  //mState默認是null
    private boolean mMutated;
    Drawable mDrawable;

    DrawableWrapperGingerbread(@Nullable Drawable dr) {
        mState = mutateConstantState();
        // Now set the drawable...
        setWrappedDrawable(dr);
    }
  • mState = mutateConstantState();
mutateConstantState()一路追蹤到底:
    DrawableWrapperState mutateConstantState() {
        //返回一個DrawableWrapperStateBase實例
        //mState默認是null
        return new DrawableWrapperStateBase(mState, null);
    }
    private static class DrawableWrapperStateBase extends DrawableWrapperState {
        //調(diào)用父類 DrawableWrapperState 的構造函數(shù)
        //orig就是DrawableWrapperGingerbread中的mState桩砰,默認是null
        DrawableWrapperStateBase(
                @Nullable DrawableWrapperState orig, @Nullable Resources res) {
            super(orig, res);
        }
        @Override
        public Drawable newDrawable(@Nullable Resources res) {
            return new DrawableWrapperGingerbread(this, res);
        }
    }
    protected static abstract class DrawableWrapperState extends Drawable.ConstantState {
        int mChangingConfigurations;
        Drawable.ConstantState mDrawableState;
        ColorStateList mTint = null;
        PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
        //orig就是DrawableWrapperGingerbread中的mState,默認是null
        DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
            //因為orig是null,所以mChangingConfigurations释簿,mDrawableState亚隅,
            //mTint,mTintMode都是DrawableWrapperState中的默認值
            if (orig != null) {
                mChangingConfigurations = orig.mChangingConfigurations;
                mDrawableState = orig.mDrawableState;
                mTint = orig.mTint;
                mTintMode = orig.mTintMode;
            }
        }
}

代碼一路跟下來可見:mState = mutateConstantState(),mState被賦值為一個新的DrawableWrapperState實例庶溶,
其中:mState(DrawableWrapperState)中煮纵,下面成員變量的值都是默認值:
int mChangingConfigurations;
Drawable.ConstantState mDrawableState;
ColorStateList mTint = null;
PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;

  • setWrappedDrawable(dr);
setWrappedDrawable(Drawable dr)一路追蹤到底:
    public final void setWrappedDrawable(Drawable dr) {
        if (mDrawable != null) {
            mDrawable.setCallback(null);
        }
        mDrawable = dr;
        if (dr != null) {
            dr.setCallback(this);
            // Only call setters for data that's stored in the base Drawable.
            setVisible(dr.isVisible(), true);
            setState(dr.getState());
            setLevel(dr.getLevel());
            setBounds(dr.getBounds());
            //mState不為null:為一個新的DrawableWrapperState實例
            if (mState != null) {
                //為mState的mDrawableState賦值為Drawable原始實例
                //關聯(lián)的ConstantState
                mState.mDrawableState = dr.getConstantState();
            }
        }
        invalidateSelf();
    }

這里涉及到DrawableWrapperGingerbread中的幾個方法:
setVisible(boolean visible, boolean restart)
@Override
public boolean setVisible(boolean visible, boolean restart) {
//Drawable中的setVisible沉删,用于控制Drawable實例是否執(zhí)行動畫,對于AnimationDrawable實例會產(chǎn)生效果,控制是否執(zhí)行動畫
return super.setVisible(visible, restart) || mDrawable.setVisible(visible, restart);
}
setState(final int[] stateSet)

    @Override
    public boolean setState(final int[] stateSet) {
        boolean handled = mDrawable.setState(stateSet);
        handled = updateTint(stateSet) || handled;
        return handled;
    }
    private boolean updateTint(int[] state) {
        //isCompatTintEnabled()這里直接返回了true
        if (!isCompatTintEnabled()) {
            // If compat tinting is not enabled, fail fast
            return false;
        }
        //mState.mTint是默認值:null
        final ColorStateList tintList = mState.mTint;
        //mState.mTintMode是默認值:DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN
        final PorterDuff.Mode tintMode = mState.mTintMode;
        if (tintList != null && tintMode != null) {
            //tintList為null,所以不會執(zhí)行下面代碼
            final int color = tintList.getColorForState(state, tintList.getDefaultColor());
            if (!mColorFilterSet || color != mCurrentColor || tintMode != mCurrentMode) {
                setColorFilter(color, tintMode);
                mCurrentColor = color;
                mCurrentMode = tintMode;
                mColorFilterSet = true;
                return true;
            }
        } else {
            //tintList為null
            mColorFilterSet = false;
            //執(zhí)行的是其父類Drawable的clearColorFilter()
            clearColorFilter();
        }
        return false;
    }
    protected boolean isCompatTintEnabled() {
        // It's enabled by default on Gingerbread
        //這里直接返回了true
        return true;
    }
Drawable的clearColorFilter方法:移除了當前Drawable實例關聯(lián)的ColorFilter
    public void clearColorFilter() {
        setColorFilter(null);
    }

可見:setState(dr.getState())這一步直接移除了Drawable實例關聯(lián)的ColorFilter.
**setLevel直接使用的是其父類Drawable中的方法setLevel(@IntRange(from=0,to=10000) int level) **

Drawable的setLevel方法:
    public final boolean setLevel(@IntRange(from=0,to=10000) int level) {
        //在這里醉途,因為mLevel就是之前DrawableWrapperGingerbread構造函數(shù)中的Drawable dr的level值矾瑰,
        //而level=dr.getLevel()返回的也是Drawable dr的level值,mLevel == level隘擎,
        //所以下面的代碼并不會執(zhí)行
        if (mLevel != level) {
            mLevel = level;
            return onLevelChange(level);
        }
        return false;
    }

可見:setLevel(dr.getLevel())這一步并未產(chǎn)生實質(zhì)影響殴穴,未執(zhí)行處理邏輯。
setBounds直接使用的是其父類Drawable中的方法setBounds(@NonNull Rect bounds)

    /**
     * Specify a bounding rectangle for the Drawable. This is where the drawable
     * will draw when its draw() method is called.
     */
    public void setBounds(@NonNull Rect bounds) {
        setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
    }

可見:setBounds(dr.getBounds())這一步為新產(chǎn)生的DrawableWrapperGingerbread實例設置其繪制范圍與原始Drawable實例一致货葬。

可見在SDK版本>= 21(LollipopDrawableImpl)情況下:DrawableCompat.wrap(@NonNull Drawable drawable)返回了Drawable的子類DrawableWrapperGingerbread的一個新實例采幌。
且在updateTint方法中移除了該新實例關聯(lián)過的ColorFilter,設置了該新實例的繪制范圍和原始Drawable實例相同

  • KitKatDrawableImpl
    跟蹤終點同LollipopDrawableImpl
在KitKatDrawableImpl情況下,wrap(Drawable drawable)一路跟蹤到底:
@Override
public Drawable wrap(Drawable drawable) {
    return DrawableCompatKitKat.wrapForTinting(drawable);
}
**
繼承關系跟蹤到最后還是DrawableWrapperGingerbread震桶,和LollipopDrawableImpl相同:
DrawableWrapperGingerbread(@Nullable Drawable dr) {
    mState = mutateConstantState();
    // Now set the drawable...
    setWrappedDrawable(dr);
}
  • JellybeanMr1DrawableImpl
    跟蹤終點同LollipopDrawableImpl
  • HoneycombDrawableImpl
    跟蹤終點同LollipopDrawableImpl
  • BaseDrawableImpl
    跟蹤終點同LollipopDrawableImpl

綜上可見:
1:在SDK版本>= 23(MDrawableImpl)情況下:DrawableCompat.wrap(@NonNull Drawable drawable)直接返回了原始的Drawable實例休傍;
2:其余情況下,DrawableCompat.wrap(@NonNull Drawable drawable)返回了Drawable的子類DrawableWrapperGingerbread的一個新實例蹲姐,且在updateTint方法中移除了該新實例關聯(lián)過的ColorFilter,設置了該新實例的繪制范圍和原始Drawable實例相同磨取;

3:DrawableCompat.setTint(@NonNull Drawable drawable, @ColorInt int tint)

    public static void setTint(@NonNull Drawable drawable, @ColorInt int tint) {
        IMPL.setTint(drawable, tint);
    }

之前分析wrap方法時候已經(jīng)看到IMPL在不同SDK版本下有不同的實現(xiàn),還是逐一查看:

  • MDrawableImpl
setTint一路跟蹤:
@Override
public void setTint(Drawable drawable, int tint) {
    DrawableCompatLollipop.setTint(drawable, tint);
}
public static void setTint(Drawable drawable, int tint) {
    //執(zhí)行的是Drawable原生的setTint方法
    drawable.setTint(tint);
}
  • LollipopDrawableImpl
setTint一路跟蹤:
@Override
public void setTint(Drawable drawable, int tint) {
    //同MDrawableImpl
    DrawableCompatLollipop.setTint(drawable, tint);
}
  • KitKatDrawableImpl
setTint一路跟蹤:
@Override
public void setTint(Drawable drawable, int tint) {
    DrawableCompatBase.setTint(drawable, tint);
}
public static void setTint(Drawable drawable, int tint) {
    if (drawable instanceof TintAwareDrawable) {
        ((TintAwareDrawable) drawable).setTint(tint);
    }
}
public interface TintAwareDrawable {
    void setTint(@ColorInt int tint);
    void setTintList(ColorStateList tint);
    void setTintMode(PorterDuff.Mode tintMode);
}

在上面分析DrawableCompat.wrap方法時候柴墩,已知其返回結果為DrawableWrapperGingerbread新實例忙厌,看一下DrawableWrapperGingerbread類的聲明:class DrawableWrapperGingerbread extends Drawable implements Drawable.Callback, DrawableWrapper, TintAwareDrawable
由此可見,setTint實質(zhì)執(zhí)行的還是DrawableWrapperGingerbread的setTint方法江咳,繼續(xù)跟蹤:

    setTint一路追蹤:
    @Override
    public void setTint(int tint) {
        setTintList(ColorStateList.valueOf(tint));
    }
    @Override
    public void setTintList(ColorStateList tint) {
        //1:在上面分析DrawableCompat.wrap時候逢净,mState的值如下:
        //mState(DrawableWrapperState)中,下面成員變量的值都是默認值:
        //int mChangingConfigurations;
        //Drawable.ConstantState mDrawableState;
        //ColorStateList mTint = null;
        //PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;

        //2:在DrawableCompat.setTint時候歼指,mState.mTint不再為空值
        mState.mTint = tint;
        updateTint(getState());
    }

    //之前DrawableCompat.wrap已經(jīng)執(zhí)行過一次updateTint爹土,
    //現(xiàn)在DrawableCompat.setTint第二次執(zhí)行!踩身!
    private boolean updateTint(int[] state) {
        //isCompatTintEnabled()返回true
        if (!isCompatTintEnabled()) {
            // If compat tinting is not enabled, fail fast
            return false;
        }
        //此時mState.mTint已經(jīng)在setTintList中賦值不為null
        final ColorStateList tintList = mState.mTint;
        //mState.mTintMode依然為默認值不為null
        final PorterDuff.Mode tintMode = mState.mTintMode;
        if (tintList != null && tintMode != null) {
            //兩者都不為空胀茵,因而執(zhí)行if條件下代碼

            //獲取當前狀態(tài)下對應的顏色
            final int color = tintList.getColorForState(state, tintList.getDefaultColor());
            //mColorFilterSet默認是false
            //color即為setTint時候傳入的顏色
            //mCurrentColor默認值是0
            //tintMode是mState中的mTintMode=DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN
            //mCurrentMode默認值是null
            if (!mColorFilterSet || color != mCurrentColor || tintMode != mCurrentMode) {
                //對Drawable實例產(chǎn)生著色的,本質(zhì)上還是執(zhí)行了Drawable中的setColorFilter方法惰赋。
                setColorFilter(color, tintMode);
                mCurrentColor = color;
                mCurrentMode = tintMode;
                mColorFilterSet = true;
                return true;
            }
        } else {
            mColorFilterSet = false;
            clearColorFilter();
        }
        return false;
    }
  • JellybeanMr1DrawableImpl
    跟蹤終點同KitKatDrawableImpl
  • HoneycombDrawableImpl
    跟蹤終點同KitKatDrawableImpl
  • BaseDrawableImpl
    跟蹤終點同KitKatDrawableImpl

綜上可見:
1:在SDK版本>= 21(MDrawableImpl和LollipopDrawableImpl)情況下:DrawableCompat.setTint(@NonNull Drawable drawable, @ColorInt int tint)執(zhí)行的是Drawable原生的setTint方法宰掉;
2:其余情況下,DrawableCompat.setTint(@NonNull Drawable drawable, @ColorInt int tint)本質(zhì)上還是執(zhí)行了Drawable中的setColorFilter方法赁濒;

4:原生Drawable.setTint(@ColorInt int tintColor)

public void setTint(@ColorInt int tintColor) {
    setTintList(ColorStateList.valueOf(tintColor));
}
public void setTintList(@Nullable ColorStateList tint) {
    //你沒有看錯,竟然是個空方法C虾Α>苎住!挨务!
}

剛看到這兒時候也有些納悶击你,后來一想肯定是我們在獲取Drawable原始實例的時玉组,獲取的其實是Drawable的子類實例,在Drawable子類里對setTintList做了重寫丁侄,有圖有真相:


setTintList重寫.png

5:Drawable.mutate()的作用

    /**
     * Make this drawable mutable. This operation cannot be reversed. A mutable
     * drawable is guaranteed to not share its state with any other drawable.
     * This is especially useful when you need to modify properties of drawables
     * loaded from resources. By default, all drawables instances loaded from
     * the same resource share a common state; if you modify the state of one
     * instance, all the other instances will receive the same modification.
     *
     * Calling this method on a mutable Drawable will have no effect.
     *
     * @return This drawable.
     * @see ConstantState
     * @see #getConstantState()
     */
    public @NonNull Drawable mutate() {
        return this;
    }

單純看源碼解釋可能比較抽象惯雳,說的通俗一點,我們通過Resource獲取mipmap文件夾下的一張資源圖片鸿摇,在獲取Drawable初始實例時候如果不使用mutate()石景,那么我們對這個Drawable進行著色,不僅改變了當前Drawable實例的顏色拙吉,以后任何通過這個圖片獲取到的Drawable實例潮孽,都會具有之前設置的顏色。所以如果我們對一張資源圖片的著色不是APP全局生效的筷黔,就需要使用mutate()往史。

具體原因:
Android為了優(yōu)化系統(tǒng)性能,同一張資源圖片生成的Drawable實例在內(nèi)存中只存在一份佛舱,在不使用mutate的情況下椎例,修改任意Drawable都會全局發(fā)生變化。
使用mutate请祖,Android系統(tǒng)也沒有把Drawable實例又單獨拷貝一份粟矿,僅僅是單獨存放了狀態(tài)值,很小的一部分數(shù)據(jù)损拢,Drawable實例在內(nèi)存中仍然保持1份陌粹,因而并不會影響系統(tǒng)的性能。
具體變化可以通過2張圖片說明:
1:不使用mutate:

共享狀態(tài).png

2:使用mutate:
不共享狀態(tài).png

以上就是個人分析的一點結果福压,若有錯誤掏秩,請各位同學留言告知!

That's all !

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荆姆,一起剝皮案震驚了整個濱河市蒙幻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胆筒,老刑警劉巖邮破,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仆救,居然都是意外死亡抒和,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門彤蔽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來摧莽,“玉大人,你說我怎么就攤上這事顿痪∧髟” “怎么了油够?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長征懈。 經(jīng)常有香客問我石咬,道長,這世上最難降的妖魔是什么卖哎? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任鬼悠,我火速辦了婚禮,結果婚禮上棉饶,老公的妹妹穿的比我還像新娘厦章。我一直安慰自己,他們只是感情好照藻,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布袜啃。 她就那樣靜靜地躺著,像睡著了一般幸缕。 火紅的嫁衣襯著肌膚如雪群发。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天发乔,我揣著相機與錄音熟妓,去河邊找鬼。 笑死栏尚,一個胖子當著我的面吹牛起愈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播译仗,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼抬虽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纵菌?” 一聲冷哼從身側響起阐污,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咱圆,沒想到半個月后笛辟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡序苏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年手幢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杠览。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡弯菊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出踱阿,到底是詐尸還是另有隱情管钳,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布软舌,位于F島的核電站才漆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏佛点。R本人自食惡果不足惜醇滥,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望超营。 院中可真熱鬧鸳玩,春花似錦、人聲如沸演闭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽米碰。三九已至窝革,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吕座,已是汗流浹背虐译。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吴趴,地道東北人漆诽。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像锣枝,于是被迫代替她去往敵國和親厢拭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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