Android——CoordinatorLayout之Behavior入門學(xué)習(xí)(上)

學(xué)習(xí)資料:

最近一直在看Java的知識(shí)粱快,在簡(jiǎn)書看到上面兩位同學(xué)的博客晚吞。CoordinatorLayout只是簡(jiǎn)單使用過一次嘹叫,也學(xué)習(xí)了解一下秽澳。

十分感謝兩位同學(xué) :)


1. 簡(jiǎn)單使用 <p>

在之前也了解過一點(diǎn)怎么使用CoordinatorLayout,并寫了一篇簡(jiǎn)單入門使用的博客 CoordinatorLayout漆羔、Tablayout梧奢、Toolbar簡(jiǎn)單組合使用

CoordinatorLayout作為一個(gè)中間橋梁性質(zhì)的布局,協(xié)調(diào)著內(nèi)部的childView演痒。之前對(duì)CoordinatorLayout有點(diǎn)誤解亲轨,以為需要配合AppBarLayout才有一些比較炫酷的特效,大錯(cuò)特錯(cuò)鸟顺,BehaviorCoordinatorLayout能夠有協(xié)調(diào)作用以及能支持各種炫酷特效的的關(guān)鍵因素


dodo_lihao同學(xué)的思路很好惦蚊,博客中也把他自己收集的學(xué)習(xí)資料整理了出來器虾,就直接看著他的博客,跟著他的思路來學(xué)習(xí)的蹦锋,感覺他系列博客中寫的案例很容易表現(xiàn)出Behavior的特點(diǎn)兆沙,所以思路和代碼照搬的dodo_lihao同學(xué)的,效果圖也就一樣了莉掂,有點(diǎn)剽竊成果的感覺葛圃,哈哈

運(yùn)行效果

依賴控件:紅色的MoveView
綁定控件:藍(lán)色的TextView

MoevView受手指的控制,手指怎么移動(dòng)就怎么移動(dòng)憎妙;而綁定的TextView則是由MoveView通過Behavior來控制


1.1 布局文件中使用 <p>

布局文件:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <com.szlk.recyclerviewl.view.MoveView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="MOVE"
        android:textColor="@android:color/white" />

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="@string/coordinator_name"
        android:textColor="@android:color/white"
        app:layout_behavior=".view.LearnBehavior" />

</android.support.design.widget.CoordinatorLayout>

最關(guān)鍵的地方就在于app:layout_behavior库正,利用這個(gè)屬性來確定的綁定的目標(biāo)childView

指定綁定目標(biāo)有3種方式:

  1. xml布局通過app:layout_behavior
  2. Java代碼中,child.getLayoutParams().setBehavior()來指定
  3. 在目標(biāo)childView類上厘唾,通過@DefaultBehavior來指定

1.2 MoveView <p>

MoveView就是一個(gè)繼承TextView的很簡(jiǎn)單的自定義View

public class MoveView extends TextView {
    private float lastX, lastY;

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        float x = event.getRawX();
        float y = event.getRawY();
        if (action == MotionEvent.ACTION_MOVE) {
            CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) getLayoutParams();
            //計(jì)算當(dāng)前的左上角坐標(biāo)
            float left = layoutParams.leftMargin + x - lastX;
            float top = layoutParams.topMargin + y - lastY;
            //設(shè)置坐標(biāo)
            layoutParams.leftMargin = (int) left;
            layoutParams.topMargin = (int) top;
            setLayoutParams(layoutParams);
        }
        lastX = x;
        lastY = y;
        return true;
    }
}

主要就是重寫onTouchEvent()來使MoveView可以根據(jù)手指滑動(dòng)在屏幕改變位置


1.3 一個(gè)簡(jiǎn)單的自定義Behavior <p>

LearnBehavior代碼:

public class LearnBehavior extends CoordinatorLayout.Behavior<TextView> {
    private int width, height;

    public LearnBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        width = display.widthPixels;
        height = display.heightPixels;
    }

    /**
     * 綁定
     *
     * @param parent     CoordinatorLayout
     * @param child      使用Behavior的childView褥符,綁定對(duì)象
     * @param dependency 依賴的childView
     * @return true 綁定
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, TextView child, View dependency) {
        return dependency instanceof MoveView;
    }

    /**
     * 依賴的childView 發(fā)生改變時(shí)
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child, View dependency) {
        int top = dependency.getTop();
        int left = dependency.getLeft();

        int x = width - left - child.getWidth();
        int y = height - top - child.getHeight();
        Log.e("x,y", "--->" + x + "-->" + y);
        setPosition(child, x, y);
        return true;
    }

    /**
     * 設(shè)置坐標(biāo)
     */
    private void setPosition(View v, int x, int y) {
        CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams();
        layoutParams.leftMargin = x;
        layoutParams.topMargin = y;
        layoutParams.width = y / 2;
        v.setLayoutParams(layoutParams);
    }
}

CoordinatorLayout.Behavior<TextView>這里使用泛型將綁定的childView限制為了TextView,可以根據(jù)實(shí)際需求來指定類型抚垃,也可以直接指定為View

注意:
當(dāng)在布局文件中使用了Behavior后喷楣,Behavior代碼中確定的交互行為便直接奏效,初始化第一次加載CoordinatorLayout時(shí)鹤树,使用了BehaviorChildView受到onDependentViewChanged()方法的影響抡蛙,第一次加載的位置也會(huì)受到影響,導(dǎo)致和布局文件中指定的位置不相同

官方有好幾個(gè)非常好的學(xué)習(xí)資料魂迄,例如:
android.support.design.widget.AppBarLayout$ScrollingViewBehavior

一個(gè)依賴AppBarLayout后,處理滑動(dòng)事件的Behavior惋耙,對(duì)Behavior中的屬性及方法有了大概了解后捣炬,可以學(xué)習(xí)具體細(xì)節(jié)的設(shè)計(jì)和優(yōu)化


2. Behavior 行為 <p>

直譯就是行為的意思

源碼中的注釋:

    /**
     * Interaction behavior plugin for child views of {@link CoordinatorLayout}.
     * 用于CoordinatorLayout中ChildView交互的行為的插件
     *
     * A Behavior implements one or more interactions that a user can take on a child view.
     *一個(gè)ChildView可以實(shí)現(xiàn)一個(gè)或者多個(gè)Behavior
     *
     * These interactions may include drags, swipes, flings, or any other gestures.
     *交互的行為包括點(diǎn)擊,拖動(dòng)绽榛,滑動(dòng)或者一些其他的收拾操作
     *
     * @param <V> The View type that this Behavior operates on
     * 泛型就是指定使用當(dāng)前Behavior的ChildView類型
     */
    public static abstract class Behavior<V extends View> {
        ...
        方法省略
        ...
    }

需要注意的是Behavior可以幾乎包括所有的交互行為湿酸,配合ViewDragHelper應(yīng)該能夠?qū)崿F(xiàn)出一些很炫酷的交互效果


2.1 常用的方法 <p>

構(gòu)造方法有兩個(gè):

默認(rèn):public Behavior() {}

布局:public Behavior(Context context, AttributeSet attrs) {  }

兩個(gè)構(gòu)造方法也比較容易理解,一個(gè)是默認(rèn)的空參的構(gòu)造方法灭美,一個(gè)是帶有布局屬性AttributeSet的方法推溃,有了這個(gè)構(gòu)造方法,可以直接在布局文件中使用


根據(jù)Behavior的特性届腐,可以將內(nèi)部的方法分以下類:

  • 測(cè)量與布局:
測(cè)量:public boolean onMeasureChild(){}
布局:public boolean onLayoutChild(){}
  • 特定狀態(tài):
//當(dāng)Behavior添加到參數(shù)實(shí)例時(shí)铁坎,回調(diào)
public void onAttachedToLayoutParams(){}

//當(dāng)Behavior與參數(shù)實(shí)例分離時(shí),回調(diào)
public void onDetachedFromLayoutParams(){}

//當(dāng)Behavior關(guān)聯(lián)的對(duì)象想要定位到特定的矩形時(shí)犁苏,回調(diào)
public boolean onRequestChildRectangleOnScreen(){}

//當(dāng)一個(gè)ChildView設(shè)置為回避屬性時(shí)硬萍,回調(diào)
public boolean getInsetDodgeRect(){}

//當(dāng)窗口發(fā)生改變時(shí),回調(diào)
public WindowInsetsCompat onApplyWindowInsets(){}

//需要保存臨時(shí)狀態(tài)信息围详,回調(diào)
public Parcelable onSaveInstanceState(){}

//需要恢復(fù)臨時(shí)狀態(tài)信息朴乖,回調(diào)
public void onRestoreInstanceState(){}

//作用未知
public int getScrimColor(){}
 
//作用未知
public float getScrimOpacity(){}
  • 確定依賴與綁定對(duì)象:
//根據(jù)參數(shù)來確定依賴與綁定對(duì)象
public boolean layoutDependsOn(){}
  • 當(dāng)依賴對(duì)象發(fā)生改變時(shí):
//當(dāng)依賴對(duì)象發(fā)生改變祖屏,包括位置,大小买羞,顏色袁勺,進(jìn)行回調(diào)
public boolean onDependentViewChanged(){}

//當(dāng)依賴對(duì)象被移除時(shí),進(jìn)行回調(diào)
public void onDependentViewRemoved(){}
  • 事件相關(guān):
//攔截事件畜普,在CoordinatorLayout把事件分發(fā)到childView之前
public boolean onInterceptTouchEvent(){}

//消費(fèi)事件
public boolean onTouchEvent(){}
  • 嵌套滑動(dòng):
//CoordinatorLayout中的滑動(dòng)嵌套childView開始啟動(dòng)一次嵌套滾動(dòng)時(shí)期丰,回調(diào)
public boolean onStartNestedScroll(){}

//嵌套滑動(dòng)結(jié)束時(shí),回調(diào)
public void onStopNestedScroll(){}

//當(dāng)一次嵌套滑動(dòng)被CoordiantorLayout識(shí)別并確定時(shí)漠嵌,進(jìn)行回調(diào)
public void onNestedScrollAccepted(){}

//嵌套滾動(dòng)正在進(jìn)行中并且綁定目標(biāo)childView已經(jīng)開始滾動(dòng)或者被CoordinatorLayout接受后試圖滾動(dòng)
public void onNestedScroll(){}

//嵌套滾動(dòng)正在準(zhǔn)備更新進(jìn)度咐汞,并且是在綁定目標(biāo)childView已經(jīng)出現(xiàn)滾動(dòng)距離之前,回調(diào)
public void onNestedPreScroll(){}

//當(dāng)嵌套滾動(dòng)的childView正在開始fling或者一個(gè)動(dòng)作確認(rèn)為fling
public boolean onNestedFling(){}

//當(dāng)滑動(dòng)嵌套childView檢測(cè)到適當(dāng)?shù)臈l件儒鹿,馬上開始一次fling事件前回調(diào)
public boolean onNestedPreFling(){}

暫時(shí)就這么分化撕,分類并不算合理,也無(wú)所謂约炎,目的是以后自己回頭來看時(shí)植阴,能比較清晰能快速定位方法是干嘛的


3. 事件相關(guān) <p>

需求:CoordinatorLayout內(nèi)有一個(gè)可以點(diǎn)擊的TextView,長(zhǎng)按之后圾浅,可以拖動(dòng)掠手,此時(shí)藍(lán)色的TextView要依然可以點(diǎn)擊

運(yùn)行效果

布局代碼:

布局代碼中,并沒有添加Behaivor狸捕,一旦添加了喷鸽,在加載布局之時(shí),Behaivor便開始作用于依賴目標(biāo)childView

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/cl_coordinator_activity"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_gravity="center"
        android:id="@+id/tv_coordinator_activity"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="@string/coordinator_name"
        android:textColor="@android:color/white" />

</android.support.design.widget.CoordinatorLayout>

Activity代碼:

public class CoordinatorLayoutLActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_coordinator_layout_l);
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        initTextView(R.id.tv_coordinator_activity, "TextView-->藍(lán)色被點(diǎn)擊");
        CoordinatorLayout layout = (CoordinatorLayout) findViewById(R.id.cl_coordinator_activity);
        layout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ToastUtils.show(CoordinatorLayoutLActivity.this, "CoordinatorLayout被點(diǎn)擊");
            }
        });
    }

    /**
     * TextView進(jìn)行初始化
     */
    private void initTextView(int id, final String str) {
        final TextView tv = (TextView) findViewById(id);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ToastUtils.show(CoordinatorLayoutLActivity.this, str);
            }
        });

        /**
         *  長(zhǎng)按 灸拍,提示動(dòng)畫效果結(jié)束后 做祝,動(dòng)態(tài)添加 Behavior
         */
        tv.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                //動(dòng)畫提示效果
                animation(tv);
                return true;
            }
        });
    }

    private void animation(final TextView tv) {
        AnimatorSet set = new AnimatorSet();
        set.setInterpolator(new BounceInterpolator());
        set.setDuration(1000);
        set.playTogether(
                ObjectAnimator.ofFloat(tv, "scaleX", 1, 1.5f),
                ObjectAnimator.ofFloat(tv, "scaleY", 1, 1.5f),
                ObjectAnimator.ofFloat(tv, "scaleX", 1.5f, 1),
                ObjectAnimator.ofFloat(tv, "scaleY", 1.5f, 1)
        );
        //動(dòng)畫監(jiān)聽,結(jié)束時(shí)添加 Behavior
        set.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                addBehavior(tv);
            }
        });
        set.start();
    }

    /**
     * 為TextView添加Behavior
     */
    private void addBehavior(TextView tv) {
        tv.setLongClickable(false);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) tv.getLayoutParams();
        //為TextView設(shè)置Behaior
        lp.setBehavior(new LongBehavior());
        ToastUtils.show(CoordinatorLayoutLActivity.this, "可以開始拖動(dòng)了");
    }
}

代碼很簡(jiǎn)單鸡岗,都是一眼能看明白的


LongBehavior代碼:


    private float lastX, lastY;
    private float moveX, moveY;

    public LongBehavior() {
        Log.e("LongBehavior", "新建");
    }

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


    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, TextView child, MotionEvent ev) {
        int action = ev.getAction();
        boolean isIntercept = false;
        float x = ev.getX();
        float y = ev.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                //判斷落點(diǎn)是否在TextView范圍內(nèi)
                //若不在 就進(jìn)行攔截 返回true
                isIntercept = !isInChildView(child, ev);
                if (isIntercept) {
                    Log.e("MotionEvent.ACTION_DOWN", "---->MotionEvent.ACTION_DOWN--->進(jìn)行攔截");
                } else {
                    Log.e("MotionEvent.ACTION_DOWN", "---->MotionEvent.ACTION_DOWN--->不攔截");
                }
                break;
            case MotionEvent.ACTION_MOVE:
                 //滑動(dòng)距離大于10
                if (Math.abs(lastX - x) >= 10 || Math.abs(lastY - y) >= 10) {
                    isIntercept = true;
                }
                break;
        }
        return isIntercept;
    }

    /**
     * 判斷落點(diǎn)是否在childView范圍內(nèi)
     */
    private boolean isInChildView(TextView child, MotionEvent ev) {
        return ev.getX() >= child.getLeft() && ev.getX() <= child.getRight()
                && ev.getY() >= child.getTop() && ev.getY() <= child.getBottom();
    }

    @Override
    public boolean onTouchEvent(CoordinatorLayout parent, TextView child, MotionEvent ev) {
        //根據(jù)是否攔截來執(zhí)行
        if (onInterceptTouchEvent(parent, child, ev)) {
            int action = ev.getAction();
            float x = ev.getX();
            float y = ev.getY();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    moveX = x;
                    moveY = y;
                    break;
                case MotionEvent.ACTION_MOVE:
                    //計(jì)算偏移量
                    float offsetX = x - moveX;
                    float offsetY = y - moveY;
                    // Log.e("offset", "&&&--" + offsetX + "-->" + offsetY);
                    if (Math.abs(offsetX)>= 10 || Math.abs(offsetY) >= 10) {
                        CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
                        layoutParams.leftMargin = (int) (offsetX);
                        layoutParams.topMargin = (int) (offsetY);
                        child.setLayoutParams(layoutParams);
                    }
                    break;
            }
        }
        return true;
    }
}

當(dāng)攔截了DOWN事件之后混槐,后續(xù)的事件便都由CoordinatorLayout來消費(fèi),onTouchEvent返回了True轩性,事件也就終止了声登,onClik便也接收不到事件了,CoordinatorLayout自身的點(diǎn)擊事件不能執(zhí)行了揣苏,


4.最后 <p>

嵌套滾動(dòng)事件悯嗓,下一篇進(jìn)行記錄學(xué)習(xí)

本人很菜,有錯(cuò)誤請(qǐng)指出

共勉 :)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舒岸,一起剝皮案震驚了整個(gè)濱河市绅作,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛾派,老刑警劉巖俄认,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件个少,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡眯杏,警方通過查閱死者的電腦和手機(jī)夜焦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岂贩,“玉大人茫经,你說我怎么就攤上這事∥颍” “怎么了卸伞?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)锉屈。 經(jīng)常有香客問我荤傲,道長(zhǎng),這世上最難降的妖魔是什么颈渊? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任遂黍,我火速辦了婚禮,結(jié)果婚禮上俊嗽,老公的妹妹穿的比我還像新娘雾家。我一直安慰自己,他們只是感情好绍豁,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布芯咧。 她就那樣靜靜地躺著,像睡著了一般竹揍。 火紅的嫁衣襯著肌膚如雪唬党。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天鬼佣,我揣著相機(jī)與錄音,去河邊找鬼霜浴。 笑死晶衷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阴孟。 我是一名探鬼主播晌纫,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼永丝!你這毒婦竟也來了锹漱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤慕嚷,失蹤者是張志新(化名)和其女友劉穎哥牍,沒想到半個(gè)月后毕泌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗅辣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年撼泛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澡谭。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡愿题,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛙奖,到底是詐尸還是另有隱情潘酗,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布雁仲,位于F島的核電站仔夺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏伯顶。R本人自食惡果不足惜囚灼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祭衩。 院中可真熱鬧灶体,春花似錦、人聲如沸掐暮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)路克。三九已至樟结,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間精算,已是汗流浹背瓢宦。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灰羽,地道東北人驮履。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像廉嚼,于是被迫代替她去往敵國(guó)和親玫镐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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