Android開源:手把手教你做一款含一鍵刪除&自定義樣式的SuperEditText


前言

  • Android開發(fā)中,EditText的使用 非常常見
  • 本文將手把手教你做一款 附帶一鍵刪除功能 & 自定義樣式豐富SuperEditText控件饿凛,希望你們會喜歡。
效果圖

已在Github開源:Super_EditText软驰,歡迎 Star 涧窒!


目錄

示意圖

1. 簡介

一款 附帶一鍵刪除功能 & 自定義樣式豐富SuperEditText控件

已在Github開源:Super_EditText,歡迎 Star 锭亏!

效果圖

2. 功能介紹

2.1 需求場景

對于 EditText來說纠吴,一般的需求有:

  • 方便用戶因出現(xiàn)輸入錯誤而進行2次輸入
  • 標(biāo)識用戶正在填寫項
  • 根據(jù)具體場景增加一定的UI元素

2.2 功能需求

根據(jù)需求場景,得出EditText需要具備的功能如下:

  • 一鍵刪除
  • 豐富的自定義樣式:左側(cè)圖標(biāo)贰镣、刪除功能圖標(biāo)呜象、分割線 & 光標(biāo) 樣式變化。具體如下圖:
示意圖

注:該樣式的設(shè)置是系統(tǒng)自帶的 API 所不具備的

  • 功能列表
示意圖

2.3 功能示意

效果圖

3. 特點

對比市面上EditText控件碑隆,該控件Super_EditText 的特點是:

3.1 功能實用

  • 一鍵刪除功能 在需求中非常常見恭陡,現(xiàn)將其封裝后更加方便使用
  • 可自定義樣式程度高(比自帶的強大 & 方便),不復(fù)雜卻能滿足一般的EditText使用需求

可自定義樣式如下:(注:該樣式的設(shè)置是系統(tǒng)自帶的 API 所不具備的)

示意圖

3.2 使用簡單

3.3 二次開發(fā)成本低

  • 本項目已在 Github上開源:Super_EditText
  • 具備詳細(xì)的源碼分析文檔(即本文)

所以上煤,在其上做二次開發(fā) & 定制化成本非常低休玩。


4. 功能詳細(xì)設(shè)計

下面將給出詳細(xì)的功能邏輯

4.1 一鍵清空輸入字段

  • 描述:將當(dāng)前用戶輸入的字段清空
  • 需求場景:方便用戶因出現(xiàn)輸入錯誤而進行2次輸入
  • 原型圖
示意圖
  • 源碼分析
   /*
    * 步驟1:定義屬性
    * */

    private int  ic_deleteResID; // 刪除圖標(biāo) 資源ID
    private Drawable  ic_delete; // 刪除圖標(biāo)
    private int delete_x,delete_y,delete_width,delete_height; // 刪除圖標(biāo)起點(x,y)、刪除圖標(biāo)寬劫狠、高(px)

    /*
    * 步驟2:初始化屬性
    * */
   private void init(Context context, AttributeSet attrs) {

        // 獲取控件資源
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SuperEditText);
       /**
         * 初始化刪除圖標(biāo)
         */
         // 1. 獲取資源ID
         ic_deleteResID = typedArray.getResourceId(R.styleable.SuperEditText_ic_delete,R.drawable.delete);
         // 2. 根據(jù)資源ID獲取圖標(biāo)資源(轉(zhuǎn)化成Drawable對象)
         ic_delete =  getResources().getDrawable(ic_deleteResID);
         // 3. 設(shè)置圖標(biāo)大小
         // 起點(x拴疤,y)、寬= left_width独泞、高 = left_height
         delete_x = typedArray.getInteger(R.styleable.SuperEditText_delete_x, 0);
         delete_y = typedArray.getInteger(R.styleable.SuperEditText_delete_y, 0);
         delete_width = typedArray.getInteger(R.styleable.SuperEditText_delete_width, 60);
         delete_height = typedArray.getInteger(R.styleable.SuperEditText_delete_height, 60);
         ic_delete.setBounds(delete_x, delete_y, delete_width, delete_height);

   /**
     * 步驟3:通過監(jiān)聽復(fù)寫EditText本身的方法來確定是否顯示刪除圖標(biāo)
     * 監(jiān)聽方法:onTextChanged() & onFocusChanged()
     * 調(diào)用時刻:當(dāng)輸入框內(nèi)容變化時 & 焦點發(fā)生變化時
     */
    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        setDeleteIconVisible(hasFocus() && text.length() > 0,hasFocus());
        // hasFocus()返回是否獲得EditTEXT的焦點呐矾,即是否選中
        // setDeleteIconVisible() = 根據(jù)傳入的是否選中 & 是否有輸入來判斷是否顯示刪除圖標(biāo)->>關(guān)注1
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        setDeleteIconVisible(focused && length() > 0,focused);
        // focused = 是否獲得焦點
        // 同樣根據(jù)setDeleteIconVisible()判斷是否要顯示刪除圖標(biāo)->>關(guān)注1
    }

   /**
     * 關(guān)注1
     * 作用:判斷是否顯示刪除圖標(biāo)
     */
    private void setDeleteIconVisible(boolean deleteVisible,boolean leftVisible) {
        setCompoundDrawables(leftVisible ?  ic_left_click :  ic_left_unclick, null,
                deleteVisible ?  ic_delete: null, null);

    // setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)介紹
        // 作用:在EditText上、下懦砂、左蜒犯、右設(shè)置圖標(biāo)(相當(dāng)于android:drawableLeft=""  android:drawableRight="")
        // 備注:傳入的Drawable對象必須已經(jīng)setBounds(x,y,width,height)组橄,即必須設(shè)置過初始位置、寬和高等信息
        // x:組件在容器X軸上的起點 y:組件在容器Y軸上的起點 width:組件的長度 height:組件的高度
        // 若不想在某個地方顯示罚随,則設(shè)置為null

        // 另外一個相似的方法:setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)
        // 作用:在EditText上玉工、下、左淘菩、右設(shè)置圖標(biāo)
        // 與setCompoundDrawables的區(qū)別:setCompoundDrawablesWithIntrinsicBounds()傳入的Drawable的寬高=固有寬高(自動通過getIntrinsicWidth()& getIntrinsicHeight()獲茸癜唷)
        // 不需要設(shè)置setBounds(x,y,width,height)
    }


   /**
     * 步驟4:對刪除圖標(biāo)區(qū)域設(shè)置點擊事件,即"點擊 = 清空搜索框內(nèi)容"
     * 原理:當(dāng)手指抬起的位置在刪除圖標(biāo)的區(qū)域潮改,即視為點擊了刪除圖標(biāo) = 清空搜索框內(nèi)容
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // 原理:當(dāng)手指抬起的位置在刪除圖標(biāo)的區(qū)域狭郑,即視為點擊了刪除圖標(biāo) = 清空搜索框內(nèi)容
        switch (event.getAction()) {
            // 判斷動作 = 手指抬起時
            case MotionEvent.ACTION_UP:
                Drawable drawable =  ic_delete;

                if (drawable != null && event.getX() <= (getWidth() - getPaddingRight())
                        && event.getX() >= (getWidth() - getPaddingRight() - drawable.getBounds().width())) {

                    // 判斷條件說明
                    // event.getX() :抬起時的位置坐標(biāo)
                    // getWidth():控件的寬度
                    // getPaddingRight():刪除圖標(biāo)圖標(biāo)右邊緣至EditText控件右邊緣的距離
                    // 即:getWidth() - getPaddingRight() = 刪除圖標(biāo)的右邊緣坐標(biāo) = X1
                        // getWidth() - getPaddingRight() - drawable.getBounds().width() = 刪除圖標(biāo)左邊緣的坐標(biāo) = X2
                    // 所以X1與X2之間的區(qū)域 = 刪除圖標(biāo)的區(qū)域
                    // 當(dāng)手指抬起的位置在刪除圖標(biāo)的區(qū)域(X2=<event.getX() <=X1),即視為點擊了刪除圖標(biāo) = 清空搜索框內(nèi)容
                    setText("");

                }
                break;
        }
        return super.onTouchEvent(event);
    }

示意圖

4.2 選中樣式

  • 描述:通過增加UI元素 & 交互樣式表示用戶正在填寫的項目
  • 需求場景:標(biāo)識用戶正在填寫項
  • 樣式說明
示意圖
  • 原型圖
示意圖
  • 屬性說明
示意圖
示意圖
  • 源碼分析
   /*
    * 步驟1:定義屬性
    * */
    private Paint mPaint; // 畫筆
    private int  ic_left_clickResID,ic_left_unclickResID;    // 左側(cè)圖標(biāo) 資源ID(點擊 & 無點擊)
    private Drawable  ic_left_click,ic_left_unclick; // 左側(cè)圖標(biāo)(點擊 & 未點擊)
    private int left_x,left_y,left_width,left_height; // 左側(cè)圖標(biāo)起點(x,y)进陡、左側(cè)圖標(biāo)寬愿阐、高(px)

    private int cursor; // 光標(biāo)

    // 分割線變量
    private int lineColor_click,lineColor_unclick;// 點擊時 & 未點擊顏色
    private int color; 
    private int linePosition; // 分割線位置

   /*
    * 步驟2:初始化屬性
    * */
private void init(Context context, AttributeSet attrs) {

        // 獲取控件資源
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SuperEditText);

        /**
         * 初始化左側(cè)圖標(biāo)(點擊 & 未點擊)
         */

        // a. 點擊狀態(tài)的左側(cè)圖標(biāo)
         // 1. 獲取資源ID
         ic_left_clickResID = typedArray.getResourceId(R.styleable.SuperEditText_ic_left_click, R.drawable.ic_left_click);
         // 2. 根據(jù)資源ID獲取圖標(biāo)資源(轉(zhuǎn)化成Drawable對象)
         ic_left_click =  getResources().getDrawable(ic_left_clickResID);
         // 3. 設(shè)置圖標(biāo)大小
         // 起點(x微服,y)趾疚、寬= left_width、高 = left_height
         left_x = typedArray.getInteger(R.styleable.SuperEditText_left_x, 0);
         left_y = typedArray.getInteger(R.styleable.SuperEditText_left_y, 0);
         left_width = typedArray.getInteger(R.styleable.SuperEditText_left_width, 60);
         left_height = typedArray.getInteger(R.styleable.SuperEditText_left_height, 60);

         ic_left_click.setBounds(left_x, left_y,left_width, left_height);
         // Drawable.setBounds(x,y,width,height) = 設(shè)置Drawable的初始位置以蕴、寬和高等信息
         // x = 組件在容器X軸上的起點糙麦、y = 組件在容器Y軸上的起點、width=組件的長度丛肮、height = 組件的高度

        // b. 未點擊狀態(tài)的左側(cè)圖標(biāo)
         // 1. 獲取資源ID
         ic_left_unclickResID = typedArray.getResourceId(R.styleable.SuperEditText_ic_left_unclick, R.drawable.ic_left_unclick);
         // 2. 根據(jù)資源ID獲取圖標(biāo)資源(轉(zhuǎn)化成Drawable對象)
         // 3. 設(shè)置圖標(biāo)大猩陌酢(此處默認(rèn)左側(cè)圖標(biāo)點解 & 未點擊狀態(tài)的大小相同)
         ic_left_unclick =  getResources().getDrawable(ic_left_unclickResID);
         ic_left_unclick.setBounds(left_x, left_y,left_width, left_height);

        /**
         * 設(shè)置EditText左側(cè)圖片(初始狀態(tài)僅有左側(cè)圖片))
         */
        setCompoundDrawables( ic_left_unclick, null,
                null, null);

        // setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)介紹
        // 作用:在EditText上、下宝与、左焚廊、右設(shè)置圖標(biāo)(相當(dāng)于android:drawableLeft=""  android:drawableRight="")
        // 備注:傳入的Drawable對象必須已經(jīng)setBounds(x,y,width,height),即必須設(shè)置過初始位置习劫、寬和高等信息
        // x:組件在容器X軸上的起點 y:組件在容器Y軸上的起點 width:組件的長度 height:組件的高度
        // 若不想在某個地方顯示咆瘟,則設(shè)置為null

        // 另外一個相似的方法:setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)
        // 作用:在EditText上、下诽里、左袒餐、右設(shè)置圖標(biāo)
        // 與setCompoundDrawables的區(qū)別:setCompoundDrawablesWithIntrinsicBounds()傳入的Drawable的寬高=固有寬高(自動通過getIntrinsicWidth()& getIntrinsicHeight()獲取)
        // 不需要設(shè)置setBounds(x,y,width,height)

        /**
         * 初始化光標(biāo)(顏色 & 粗細(xì))
         */
         // 原理:通過 反射機制 動態(tài)設(shè)置光標(biāo)
         // 1. 獲取資源ID
         cursor = typedArray.getResourceId(R.styleable.SuperEditText_cursor, R.drawable.cursor);
         try {

            // 2. 通過反射 獲取光標(biāo)屬性
            Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
            f.setAccessible(true);
            // 3. 傳入資源ID
            f.set(this, cursor);

         } catch (Exception e) {
            e.printStackTrace();
         }

        /**
         * 初始化分割線(顏色谤狡、粗細(xì)灸眼、位置)
         */
         // 1. 設(shè)置畫筆
         mPaint = new Paint();
         mPaint.setStrokeWidth(2.0f); // 分割線粗細(xì)

         // 2. 設(shè)置分割線顏色(使用十六進制代碼,如#333墓懂、#8e8e8e)
         int lineColorClick_default = context.getResources().getColor(R.color.lineColor_click); // 默認(rèn) = 藍色#1296db
         int lineColorunClick_default = context.getResources().getColor(R.color.lineColor_unclick); // 默認(rèn) = 灰色#9b9b9b
         lineColor_click = typedArray.getColor(R.styleable.SuperEditText_lineColor_click, lineColorClick_default);
         lineColor_unclick = typedArray.getColor(R.styleable.SuperEditText_lineColor_unclick, lineColorunClick_default);
         color = lineColor_unclick;

         mPaint.setColor(lineColor_unclick); // 分割線默認(rèn)顏色 = 灰色
         setTextColor(color); // 字體默認(rèn)顏色 = 灰色

         // 3. 分割線位置
         linePosition = typedArray.getInteger(R.styleable.SuperEditText_linePosition, 5);
         // 消除自帶下劃線
         setBackground(null);


   /**
     * 步驟3:通過監(jiān)聽復(fù)寫EditText本身的方法來設(shè)置所有樣式
     * 監(jiān)聽方法:onTextChanged() & onFocusChanged()
     * 調(diào)用時刻:當(dāng)輸入框內(nèi)容變化時 & 焦點發(fā)生變化時
     */
      @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        setDeleteIconVisible(hasFocus() && text.length() > 0,hasFocus());
        // hasFocus()返回是否獲得EditTEXT的焦點焰宣,即是否選中
        // setDeleteIconVisible() = 根據(jù)傳入的是否選中 & 是否有輸入來判斷是否顯示刪除圖標(biāo)->>關(guān)注1
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        setDeleteIconVisible(focused && length() > 0,focused);
        // focused = 是否獲得焦點
        // 同樣根據(jù)setDeleteIconVisible()判斷是否要顯示刪除圖標(biāo)->>關(guān)注1
    }

    /**
     * 關(guān)注1
     * 作用:設(shè)置分割線顏色
     */
    private void setDeleteIconVisible(boolean deleteVisible,boolean leftVisible) {
        color = leftVisible ? lineColor_click : lineColor_unclick;
        setTextColor(color);
        invalidate();
    }

   /**
     * 步驟4:繪制分割線
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(color);
        setTextColor(color);
        // 繪制分割線
        // 需要考慮:當(dāng)輸入長度超過輸入框時,所畫的線需要跟隨著延伸
        // 解決方案:線的長度 = 控件長度 + 延伸后的長度
        int x=this.getScrollX(); // 獲取延伸后的長度
        int w=this.getMeasuredWidth(); // 獲取控件長度

        // 傳入?yún)?shù)時捕仔,線的長度 = 控件長度 + 延伸后的長度
                canvas.drawLine(0, this.getMeasuredHeight()- linePosition, w+x,
                        this.getMeasuredHeight() - linePosition, mPaint);

    }
}

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SuperEditText">

        <attr name="ic_delete" format="reference" />
        <attr name="delete_x" format="integer"  />
        <attr name="delete_y" format="integer"  />
        <attr name="delete_width" format="integer"  />
        <attr name="delete_height" format="integer"  />
        
        <attr name="ic_left_click" format="reference" />
        <attr name="ic_left_unclick" format="reference" />
        <attr name="left_x" format="integer"  />
        <attr name="left_y" format="integer"  />
        <attr name="left_width" format="integer"  />
        <attr name="left_height" format="integer"  />

        <attr name="lineColor_click" format="color"  />
        <attr name="lineColor_unclick" format="color"  />
        <attr name="linePosition" format="integer"  />

        <attr name="cursor" format="reference" />

    </declare-styleable>
</resources>

cursor.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid android:color="@color/lineColor_click" />
    <size android:width="1dp" />

</shape>

5. 完整源碼地址

Carson_Ho的Github地址:Super_EditText


6. 具體使用

具體請看文章:Android自定義View:你需要一款簡單實用的SuperEditText(一鍵刪除&自定義樣式)


7. 貢獻代碼

  • 希望你們能和我一起完善這款簡單 & 好用的SuperEditText控件匕积,具體請看:貢獻代碼說明
  • 關(guān)于該開源項目的意見 & 建議可在Issue上提出佛嬉。歡迎 Star

8. 總結(jié)

相信你一定會喜歡上 這款簡單 & 好用的SuperEditText控件

已在Github上開源:Super_EditText闸天,歡迎 Star 暖呕!

效果圖

Carson帶你學(xué)自定義View文章系列:
Carson帶你學(xué)自定義View:自定義View基礎(chǔ)
Carson帶你學(xué)自定義View:一文梳理自定義View工作流程
Carson帶你學(xué)自定義View:Measure過程
Carson帶你學(xué)自定義View:Layout過程
Carson帶你學(xué)自定義View:Draw過程
Carson帶你學(xué)自定義View:手把手教你寫一個完整的自定義View
Carson帶你學(xué)自定義View:Canvas類全面解析
Carson帶你學(xué)自定義View:Path類全面解析


歡迎關(guān)注Carson_Ho的簡書

不定期分享關(guān)于安卓開發(fā)的干貨,追求短苞氮、平湾揽、快,但卻不缺深度笼吟。


請點贊库物!因為你的鼓勵是我寫作的最大動力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贷帮,一起剝皮案震驚了整個濱河市戚揭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撵枢,老刑警劉巖民晒,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锄禽,居然都是意外死亡潜必,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門沃但,熙熙樓的掌柜王于貴愁眉苦臉地迎上來磁滚,“玉大人,你說我怎么就攤上這事宵晚〈谷粒” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵淤刃,是天一觀的道長晒他。 經(jīng)常有香客問我,道長钝凶,這世上最難降的妖魔是什么仪芒? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮耕陷,結(jié)果婚禮上掂名,老公的妹妹穿的比我還像新娘。我一直安慰自己哟沫,他們只是感情好饺蔑,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嗜诀,像睡著了一般猾警。 火紅的嫁衣襯著肌膚如雪孔祸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天发皿,我揣著相機與錄音崔慧,去河邊找鬼。 笑死穴墅,一個胖子當(dāng)著我的面吹牛惶室,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播玄货,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼皇钞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了松捉?” 一聲冷哼從身側(cè)響起夹界,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隘世,沒想到半個月后可柿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡以舒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年趾痘,在試婚紗的時候發(fā)現(xiàn)自己被綠了慢哈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔓钟。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖卵贱,靈堂內(nèi)的尸體忽然破棺而出滥沫,到底是詐尸還是另有隱情,我是刑警寧澤键俱,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布兰绣,位于F島的核電站,受9級特大地震影響编振,放射性物質(zhì)發(fā)生泄漏缀辩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一踪央、第九天 我趴在偏房一處隱蔽的房頂上張望臀玄。 院中可真熱鬧滨砍,春花似錦它褪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽累贤。三九已至叠穆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臼膏,已是汗流浹背硼被。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留渗磅,地道東北人祷嘶。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像夺溢,于是被迫代替她去往敵國和親论巍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

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