Android Drawable完全解析(一):Drawable源碼分析(下)

Android Drawable完全解析(一):Drawable源碼分析(上)
Android Drawable完全解析(一):Drawable源碼分析(中)
Android Drawable完全解析(一):Drawable源碼分析(下)

昨天下班前至会,分析了View實(shí)例將Drawable作為背景繪制到屏幕上面的流程奶是,今天繼續(xù)分析Drawable在ImageView中的繪制流程!

3:Drawable繪制流程

3.3:Drawable在ImageView中的繪制流程

ImageView使用Drawable的方式大體以下幾種:

  • 在xml中直接設(shè)置android:background="@mipmap/voice"
    <ImageView
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:background="@mipmap/voice"
    />
  • 在xml中直接設(shè)置android:src="@mipmap/voice"
    <ImageView
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:src="@mipmap/voice"
    />
  • Java代碼中調(diào)用 setImageResource(@DrawableRes int resId)
  • Java代碼中調(diào)用 setImageDrawable(@Nullable Drawable drawable)
  • Java代碼中調(diào)用 setBackgroundDrawable,實(shí)質(zhì)是調(diào)用View.setBackgroundDrawable,上篇文章已分析弦牡。

下面就這幾種方式逐一分析:
首先上原圖:


voice.png

3.3.1:android:background="@mipmap/voice"

        <ImageView
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:background="@mipmap/voice"
            />

實(shí)際效果:


background.png

可見直接使用android:background友驮,圖片作為背景完全鋪滿ImageView尺寸,會(huì)根據(jù)ImageView的范圍縮放驾锰。
既然在xml中布局ImageView卸留,那么肯定是調(diào)用ImageView(Context context, @Nullable AttributeSet attrs),看一下關(guān)鍵代碼:

public class ImageView extends View {
    public ImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
****
    public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        //調(diào)用View的構(gòu)造函數(shù)
        super(context, attrs, defStyleAttr, defStyleRes);
        ****
    }
}
一路追蹤下去:
public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {
    public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        this(context);
        final TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
        ****
        Drawable background = null;
        ****
        final int N = a.getIndexCount();
        for (int i = 0; i < N; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                //獲取在xml中設(shè)置的android:background="@mipmap/voice"
                case com.android.internal.R.styleable.View_background:
                    background = a.getDrawable(attr);
                    break; 
                *****
            }
        }
        ****
        if (background != null) {
            setBackground(background);
        }
        ****
    }
    public void setBackground(Drawable background) {
        //[Android Drawable完全解析(一):Drawable源碼分析(中)](http://www.reibang.com/p/2213c62e4738)
        setBackgroundDrawable(background);
    }
}

可見:
在xml中直接設(shè)置android:background="@mipmap/voice"實(shí)質(zhì)是通過調(diào)用View.setBackgroundDrawable(Drawable background)將圖片繪制到屏幕上椭豫!

View.setBackgroundDrawable(Drawable background)在上一篇文章:Android Drawable完全解析(一):Drawable源碼分析(中)有過分析耻瑟!
為什么背景圖會(huì)鋪滿整個(gè)ImageView,是因?yàn)樵赩iew繪制過程中赏酥,將背景Drawable的繪制范圍設(shè)置為和View的尺寸一致:

    void setBackgroundBounds() {
        if (mBackgroundSizeChanged && mBackground != null) {
            mBackground.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
            mBackgroundSizeChanged = false;
            rebuildOutline();
        }
    }

3.3.2:android:src="@mipmap/voice"

        <ImageView
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:src="@mipmap/voice"
            />

實(shí)際效果:


src.png

可見直接使用android:src喳整,默認(rèn)情況下圖片會(huì)根據(jù)ImageView的尺寸在保留自身寬高比例下進(jìn)行縮放,最后在ImageView的中心顯示裸扶。
既然在xml中布局ImageView框都,那么肯定是調(diào)用ImageView(Context context, @Nullable AttributeSet attrs),同樣看一下關(guān)鍵代碼:

public class ImageView extends View {
    public ImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        //super上面分析過了呵晨,繪制的是android:background="@mipmap/voice"
        super(context, attrs, defStyleAttr, defStyleRes);
        //主要設(shè)置了 ImageView實(shí)例中圖像邊界 與 ImageView邊界間的縮放關(guān)系
        initImageView();
        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
        //將xml中使用android:src="@mipmap/voice"設(shè)置的圖片生成Drawable實(shí)例
        final Drawable d = a.getDrawable(R.styleable.ImageView_src);
        if (d != null) {
            //將src生成的Drawable實(shí)例設(shè)置為ImageView的內(nèi)容
            setImageDrawable(d);
        }
        ****
        //在我們的例子中魏保,沒有設(shè)置scaleType屬性,則index = -1摸屠;
        final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
        if (index >= 0) {
            //在我們例子中谓罗,index = -1,下面代碼不執(zhí)行
            setScaleType(sScaleTypeArray[index]);
        }
        //解析在xml中設(shè)置的tint和tintMode屬性值
        if (a.hasValue(R.styleable.ImageView_tint)) {
            mDrawableTintList = a.getColorStateList(R.styleable.ImageView_tint);
            mHasDrawableTint = true;
            // Prior to L, this attribute would always set a color filter with
            // blending mode SRC_ATOP. Preserve that default behavior.
            mDrawableTintMode = PorterDuff.Mode.SRC_ATOP;
            mHasDrawableTintMode = true;
        }
        if (a.hasValue(R.styleable.ImageView_tintMode)) {
            mDrawableTintMode = Drawable.parseTintMode(a.getInt(
                    R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
            mHasDrawableTintMode = true;
        }
        //根據(jù)當(dāng)前ImageView的ColorStateList對(duì) 通過src生成的Drawable實(shí)例進(jìn)行著色
        applyImageTint();
        final int alpha = a.getInt(R.styleable.ImageView_drawableAlpha, 255);
        //設(shè)置透明度
        if (alpha != 255) {
            setImageAlpha(alpha);
        }
        mCropToPadding = a.getBoolean(
                R.styleable.ImageView_cropToPadding, false);
        a.recycle();
        //need inflate syntax/reader for matrix
    }
    private void initImageView() {
        ****
        //設(shè)置mScaleType = ScaleType.FIT_CENTER;可見ImageView中
        //mScaleType默認(rèn)就是ScaleType.FIT_CENTER
        mScaleType = ScaleType.FIT_CENTER;
        ****
    }
    public void setImageDrawable(@Nullable Drawable drawable) {
        if (mDrawable != drawable) {
            ****
            //對(duì)src生成的Drawable實(shí)例設(shè)置一系列屬性
            updateDrawable(drawable);
            ****
            //最后調(diào)用invalidate()觸發(fā)draw
            invalidate();
        }
    }
    private void updateDrawable(Drawable d) {
        ****
        //將ImageView實(shí)例之前關(guān)聯(lián)的Drawable實(shí)例的動(dòng)畫監(jiān)聽移除季二,
        //并停止其已經(jīng)在執(zhí)行的動(dòng)畫妥衣,解除其所有事件
        if (mDrawable != null) {
            mDrawable.setCallback(null);
            unscheduleDrawable(mDrawable);
            if (isAttachedToWindow()) {
                mDrawable.setVisible(false, false);
            }
        }
        //將mDrawable賦值為通過src屬性生成的Drawable實(shí)例
        mDrawable = d;
        if (d != null) {
            //為通過src生成的Drawable實(shí)例設(shè)置動(dòng)畫監(jiān)聽為ImageView實(shí)例自身;
            //并設(shè)置其布局方向戒傻,狀態(tài)數(shù)組,Drawable動(dòng)畫是否開啟蜂筹,Drawable的level值需纳。
            d.setCallback(this);
            d.setLayoutDirection(getLayoutDirection());
            if (d.isStateful()) {
                d.setState(getDrawableState());
            }
            if (isAttachedToWindow()) {
                d.setVisible(getWindowVisibility() == VISIBLE && isShown(), true);
            }
            d.setLevel(mLevel);
            mDrawableWidth = d.getIntrinsicWidth();
            mDrawableHeight = d.getIntrinsicHeight();
            //根據(jù)當(dāng)前ImageView實(shí)例的ColorStateList對(duì)其進(jìn)行著色
            applyImageTint();
            //未執(zhí)行實(shí)質(zhì)代碼
            applyColorMod();
            //設(shè)置Drawable實(shí)例的繪制范圍不變,并根據(jù)ImageView實(shí)例內(nèi)容區(qū)域和
            //Drawable實(shí)例原始繪制范圍艺挪,確定Drawable實(shí)例在實(shí)際繪制時(shí)候的縮放不翩。
            configureBounds();
        } else {
            mDrawableWidth = mDrawableHeight = -1;
        }
    }
    private void applyImageTint() {
        ****
                //根據(jù)當(dāng)前ImageView的ColorStateList對(duì) 通過src生成的Drawable實(shí)例進(jìn)行著色
                mDrawable.setTintList(mDrawableTintList);
        ****
    }
    private void applyColorMod() {
        //對(duì)應(yīng)通過 src生成的Drawble實(shí)例來說,ImageView并未調(diào)用setColorFilter
        //mColorMod也為默認(rèn)的false值麻裳,所以下面代碼實(shí)質(zhì)未執(zhí)行
        if (mDrawable != null && mColorMod) {
            mDrawable = mDrawable.mutate();
            //如果當(dāng)前ImageView實(shí)例調(diào)用過setColorFilter口蝠,
            //則對(duì) 通過src生成的Drawable實(shí)例設(shè)置相同的ColorFilter
            if (mHasColorFilter) {
                mDrawable.setColorFilter(mColorFilter);
            }
            mDrawable.setXfermode(mXfermode);
            mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
        }
    }
    private void configureBounds() {
        //通過src生成的Drawable實(shí)例 原始寬高
        final int dwidth = mDrawableWidth;
        final int dheight = mDrawableHeight;
        //ImageView實(shí)例的內(nèi)容區(qū)域?qū)捀?去除了padding值)
        final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
        final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
        ****
        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
            //當(dāng)ImageView設(shè)置過android:scaleType="fitXY" 或setScaleType(ScaleType.FIT_XY),
            //則將此Drawable實(shí)例的繪制范圍設(shè)定為ImageView實(shí)例的內(nèi)容區(qū)域
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            //對(duì)應(yīng)我們例子中津坑,未設(shè)置android:scaleType情況下妙蔗,
            //通過src生成的Drawable實(shí)例的繪制范圍就是其原始范圍
            mDrawable.setBounds(0, 0, dwidth, dheight);
            //下面代碼設(shè)置了mDrawMatrix的屬性
            //在initImageView()方法中已知:
            //ImageView中mScaleType默認(rèn)就是ScaleType.FIT_CENTER
            if (ScaleType.MATRIX == mScaleType) {
                ****
            } else if (fits) {
                ****
            } else if (ScaleType.CENTER == mScaleType) {
                ****
            } else if (ScaleType.CENTER_CROP == mScaleType) {
                ****
            } else if (ScaleType.CENTER_INSIDE == mScaleType) {
                ****
            } else {
                //ImageView中mScaleType默認(rèn)就是ScaleType.FIT_CENTER
                //則根據(jù)ImageView實(shí)例內(nèi)容區(qū)域的范圍和Drawable實(shí)例實(shí)際寬高來設(shè)置mDrawMatrix
                mTempSrc.set(0, 0, dwidth, dheight);
                mTempDst.set(0, 0, vwidth, vheight);
                mDrawMatrix = mMatrix;
                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
            }
        }
    }
}

ImageView實(shí)例生成后,肯定還是執(zhí)行onDraw方法將自身繪制到屏幕上疆瑰,繼續(xù)追蹤代碼:

public class ImageView extends View {
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        ****
        //在上面分析過 mDrawMatrix不為null
        //mDrawMatrix的屬性根據(jù)ImageView實(shí)例內(nèi)容區(qū)域的范圍和Drawable實(shí)例實(shí)際寬高來配置
        if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
            //如果矩陣mDrawMatrix為空眉反,且ImageView的上下padding值都為0
            //則直接將Drawable實(shí)例繪制到畫布上
            mDrawable.draw(canvas);
        } else {
            ****
            //我們例子中昙啄,矩陣mDrawMatrix不為空,則將其設(shè)置到ImageView的畫布上
            if (mDrawMatrix != null) {
                canvas.concat(mDrawMatrix);
            }
            //然后在畫布上面繪制Drawable實(shí)例
            mDrawable.draw(canvas);
            canvas.restoreToCount(saveCount);
        }
    }
}

至此寸五,
android:src="@mipmap/voice"整個(gè)流程就分析完了梳凛,流程總結(jié)如下:

在ImageView構(gòu)造函數(shù)中:

1:設(shè)置縮放類型默認(rèn)為 ScaleType.FIT_CENTER(圖像居中等比例縮放)
2:在ImageView構(gòu)造函數(shù)中,解析xml中android:src屬性獲取Drawable實(shí)例梳杏;
3:為生成的Drawable實(shí)例設(shè)置一系列屬性:

  • 設(shè)置動(dòng)畫監(jiān)聽為ImageView實(shí)例自身:d.setCallback(this);
  • 設(shè)置布局方向和ImageView實(shí)例一致:d.setLayoutDirection(getLayoutDirection())韧拒;
  • 設(shè)置狀態(tài)數(shù)組和ImageView實(shí)例一致:d.setState(getDrawableState());
  • 設(shè)置動(dòng)畫是否可見和 ImageView可見性一致:d.setVisible(getWindowVisibility() == VISIBLE && isShown(), true);
  • 設(shè)置動(dòng)畫當(dāng)前Level值和ImageView的mLevel值一致:d.setLevel(mLevel);
  • 根據(jù)當(dāng)前ImageView實(shí)例的ColorStateList對(duì)其進(jìn)行著色:applyImageTint();
  • 設(shè)置繪制范圍為原始繪制范圍setBounds 且 根據(jù)ImageView十性、Drawable實(shí)例的范圍 和 縮放類型 來設(shè)置Matrix mDrawMatrix(用于onDraw):configureBounds();

4:如果我們?cè)趚ml中還設(shè)置了縮放類型叛溢,著色,著色模式烁试,透明度雇初,
則為mScaleType重新賦值,并為生成的Drawable實(shí)例逐一設(shè)置著色减响,著色模式靖诗,透明度

在ImageView的onDraw方法中:

1:如果矩陣mDrawMatrix為空,且ImageView的上下padding值都為0支示,則直接將Drawable實(shí)例繪制到畫布上
2:其余情況下:

  • 如果矩陣mDrawMatrix不為空刊橘,則將其設(shè)置到ImageView的畫布上;
  • 然后在畫布上面繪制Drawable實(shí)例

本質(zhì)上還是執(zhí)行了Drawable.draw(@NonNull Canvas canvas)將src生成的Drawable實(shí)例繪制到ImageView實(shí)例所在的畫布

3.3.3:setImageDrawable(@Nullable Drawable drawable)

setImageDrawable在上面分析過程中出現(xiàn)過

    public void setImageDrawable(@Nullable Drawable drawable) {
        if (mDrawable != drawable) {
            mResource = 0;
            mUri = null;
            final int oldWidth = mDrawableWidth;
            final int oldHeight = mDrawableHeight;
            updateDrawable(drawable);
            if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
                requestLayout();
            }
            //invalidate會(huì)引發(fā)重繪颂鸿,調(diào)用onDraw方法促绵,直接看上面onDraw的流程分析即可
            invalidate();
        }
    }

3.3.4:setImageResource(@DrawableRes int resId)

    public void setImageResource(@DrawableRes int resId) {
        ****
        //在updateDrawable(Drawable d)中:mDrawable = d;
        //此處將mDrawable重置為null
        updateDrawable(null);
        //為mResource賦值為傳入的資源ID,mUri重置為null
        mResource = resId;
        mUri = null;
        resolveUri();
        ****
        //引發(fā)重繪
        invalidate();
    }
    private void resolveUri() {
        //updateDrawable(null)已經(jīng)將mDrawable重置為null
        if (mDrawable != null) {
            return;
        }
        if (getResources() == null) {
            return;
        }
        Drawable d = null;
        //在中setImageResource(@DrawableRes int resId)已知:mResource = resId;
        if (mResource != 0) {
            //通過setImageResource傳入的resId通常不為0嘴纺,執(zhí)行如下:
            try {
                //通過傳入的圖片資源ID獲取Drawable實(shí)例
                d = mContext.getDrawable(mResource);
            } catch (Exception e) {
                Log.w(LOG_TAG, "Unable to find resource: " + mResource, e);
                // Don't try again.
                mUri = null;
            }
        } else if (mUri != null) {
            d = getDrawableFromUri(mUri);
            if (d == null) {
                Log.w(LOG_TAG, "resolveUri failed on bad bitmap uri: " + mUri);
                // Don't try again.
                mUri = null;
            }
        } else {
            return;
        }
        //將通過傳入的圖片資源ID生成的Drawable實(shí)例作為參數(shù)败晴,
        //調(diào)用updateDrawable,上面已經(jīng)分析過此方法
        updateDrawable(d);
    }

setImageResource(@DrawableRes int resId)整個(gè)流程就分析完了栽渴,流程總結(jié)如下:

  • 1:首先執(zhí)行updateDrawable(null)尖坤,將已經(jīng)繪制完畢的mDrawable動(dòng)畫停止,移除所有事件及動(dòng)畫監(jiān)聽闲擦,并重置為null
  • 2:用Resource實(shí)例通過resId獲取Drawable實(shí)例慢味,作為參數(shù)執(zhí)行updateDrawable
  • 3:ImageView實(shí)例執(zhí)行重繪,詳見之前onDraw的分析

至此墅冷,Drawable在ImageView中的繪制流程就分析完畢了纯路!Drawable源碼分析也告一段落,如有錯(cuò)誤或者翻譯問題請(qǐng)各位大神留言寞忿!

'Android Drawable完全解析' 系列未完待續(xù)...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驰唬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌定嗓,老刑警劉巖蜕琴,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宵溅,居然都是意外死亡凌简,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門恃逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雏搂,“玉大人,你說我怎么就攤上這事寇损⊥怪#” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵矛市,是天一觀的道長(zhǎng)芙沥。 經(jīng)常有香客問我,道長(zhǎng)浊吏,這世上最難降的妖魔是什么而昨? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮找田,結(jié)果婚禮上歌憨,老公的妹妹穿的比我還像新娘。我一直安慰自己墩衙,他們只是感情好务嫡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著漆改,像睡著了一般心铃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挫剑,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天去扣,我揣著相機(jī)與錄音,去河邊找鬼暮顺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛秀存,可吹牛的內(nèi)容都是我干的捶码。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼或链,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼惫恼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起澳盐,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤祈纯,失蹤者是張志新(化名)和其女友劉穎令宿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腕窥,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粒没,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了簇爆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片癞松。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖入蛆,靈堂內(nèi)的尸體忽然破棺而出响蓉,到底是詐尸還是另有隱情,我是刑警寧澤哨毁,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響民傻,放射性物質(zhì)發(fā)生泄漏呀舔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一迎捺、第九天 我趴在偏房一處隱蔽的房頂上張望举畸。 院中可真熱鬧,春花似錦凳枝、人聲如沸抄沮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叛买。三九已至,卻和暖如春蹋订,著一層夾襖步出監(jiān)牢的瞬間率挣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工露戒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椒功,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓智什,卻偏偏與公主長(zhǎng)得像动漾,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荠锭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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