一起撸个朋友圈吧(step5) - 控件篇【控件组装&评论控件】

项目地址:https://github.com/razerdp/FriendCircle
一起撸个朋友圈吧这是本文所处文集,所有更新都会在这个文集里面哦,欢迎关注

上篇链接:http://www.jianshu.com/p/a2cdf81359fc
下篇链接:http://www.jianshu.com/p/ff9788581fb0

如您所见,在公司项目app提测后,在下终于闲下来继续去撸app了。花了一天时间,抱着服务器大哥的大腿狂问,终于初步弄出了一个服务器出来。

之前欠下的评论控件也得以展示了。

ps:在下非常欢迎PR,如果您有更好的想法,可以PR到dev分支哦-V-


预览图如下:(内容页还没开始,所以目前只有共有控件的拼装)

预览

嗯。。。因为部署在本机,所以网速比较快←_←,而且目前在下只弄了4条数据,同时内容区(可变部分)还没开干,所以画面看起来怪怪的。

先不管那么多

本篇主要讲解评论控件的实现:

评论控件采取的依然是继承TextView,做法跟点赞列表控件差不多,但在ListView里面,我们的评论区实现方案大概有两种:

  • ListView的item嵌套ListView?(不可取)
  • ListView的item嵌套LinearLayout,动态添加我们的自定义控件。
    很明显,我们采取方案二。

首先实现我们的控件,因为评论控件比较简单,所以我们就不需要attrs了。

/**
 * Created by 大灯泡 on 2016/2/23.
 * 评论控件
 */
public class CommentWidget extends TextView {
    private static final String TAG = "CommentWidget";
    //用户名颜色
    private int textColor = 0xff517fae;
    private static final int textSize = 14;

    private int key;
    private SpannableStringBuilderAllVer mSpannableStringBuilderAllVer;

...构造器

 public void setCommentText(CommentInfo info) {
        if (info == null) return;
        boolean hasContent = false;
        //根据hashCode判断内容是否一致
        if (key == 0) {
            key = info.hashCode();
        }
        else {
            hasContent = (key == info.hashCode());
        }
        if (!hasContent) {
            key = info.hashCode();
            setText("");
            setTag(info);
            createCommentStringBuilder(info);
        }
        else {
            try {
                setText(mSpannableStringBuilderAllVer);
            } catch (NullPointerException e) {
                e.printStackTrace();
                Log.e(TAG, "虽然在下觉得不可能会有这个情况,但还是捕捉下吧,万一被打脸呢。。。");
            }
        }
    }

    private void createCommentStringBuilder(@NonNull CommentInfo info) {
        String content = ": " + info.content + "\0";
        if (mSpannableStringBuilderAllVer == null) {
            mSpannableStringBuilderAllVer = new SpannableStringBuilderAllVer();
            boolean isApply = (info.userB == null);
            // 用户B为空,证明是一条原创评论
            if (info.userA != null && isApply) {
                CommentClick userA = new CommentClick.Builder(getContext(), info.userA).setTextSize(textSize).build();
                mSpannableStringBuilderAllVer.append(info.userA.nick, userA, 0);
                mSpannableStringBuilderAllVer.append(content);
            }
            else if (info.userA != null && !isApply) {
                //用户A,B不空,证明是回复评论
                CommentClick userA = new CommentClick.Builder(getContext(), info.userA).setTextSize(textSize).build();
                mSpannableStringBuilderAllVer.append(info.userA.nick, userA, 0);
                mSpannableStringBuilderAllVer.append("回复");
                CommentClick userB = new CommentClick.Builder(getContext(), info.userB).setTextSize(textSize).build();
                mSpannableStringBuilderAllVer.append(info.userB.nick, userB, 0);
                mSpannableStringBuilderAllVer.append(content);
            }
        }
        setText(mSpannableStringBuilderAllVer);
    }

    public CommentInfo getData() throws ClassCastException {
        return (CommentInfo) getTag();
    }

代码不多,而且在下也写得比较清晰(命名二逼明了,咱们不故作深沉),我们主要观察createCommentStringBuilder()方法,这个方法我们主要判断userB,也就是被回复的用户是否为空,如果是空,则证明这是一条原创评论,也就是针对朋友的评论,不空则是回复别人。

然后CommentClick跟我们的点赞控件一样的ClickableSpan实现,这个没啥好说的。

另外需要注意的是记得在回复的内容后加上'\0',详情见点赞列表那篇。

评论控件大概就这样,但本篇文章重头戏在于控件的组装

还记得我们的一起撸个朋友圈吧(step3) - ListAdapter篇吗,我们的BaseItemDelegate留下了公共部分的初始化,这次我们就顺便的弄回。

我们公共的部分有如下几个(公共部分即无论哪种类型的朋友圈,都会存在的控件):

  • 头像/昵称/用户心情文字,组成item_header
  • 发布时间/评论按钮/点赞列表/评论区,组成item_bottom

这几个是无论如何都会存在的,所以我们的布局文件可以单独抽出来复用(xml就不贴了,又长又无聊):

item_header
item_bottom

值得注意的是,item_bootm里面有个地方嵌套布局比较多,原因如下:
我们的点赞&评论控件处于同一个LinearLayout里面,因为这两者之间存在着一条分割线,所以采用LinearLayout,其次,我们的评论列表则是单独使用LinearLayout动态添加控件的,所以这里嵌套了两层,这无法避免。。。(当然,也可以将点赞和评论控件封装在同一个LinearLayout里面,但个人觉得没必要)

布局弄好后,我们完善我们的baseitem代码

public abstract class BaseItemDelegate
        implements BaseItemView<MomentsInfo>, View.OnClickListener, View.OnLongClickListener {
    protected Activity context;
    //顶部
    protected SuperImageView avatar;
    protected TextView nick;
    protected ClickShowMoreLayout textField;
    //底部
    protected TextView createTime;
    protected ImageView commentImage;
    protected FrameLayout commentButton;
    protected LinearLayout commentAndPraiseLayout;
    protected PraiseWidget praiseWidget;
    protected View line;
    protected LinearLayout commentLayout;

    //中间内容层
    protected RelativeLayout contentLayout;

常量大概就是这些,内容层里面也许是gridview(图片),也许是viewGroup(网页分享),这个是不可变甚至可能没有的,所以我们这里仅负责调整间距,不负责其可视性,其余的控件都是共有的。

关于SuperImageView,这个东东是继承ImageView封装了Glide的方法,关于这个在我的毕业论文备忘录中的Day4 - ImageView封装Glide方法有记载,这里就不详述了

在我们的item里面,所有view的操作都是在onBindData进行的,我们父类进行初始化共有控件主要以下几个方法:

 @Override
    public void onBindData(int position, @NonNull View v, @NonNull MomentsInfo data, final int dynamicType) {
        mInfo = data;
        //初始化共用部分
        bindView(v);
        bindShareData(data);
        bindData(position, v, data, dynamicType);
    }
  • bindView(v),这里进行共有控件的findViewById,此处不展示
  • bindShareData(data),这里进行共有控件的数据展示
  • bindData(position, v, data, dynamicType),这个是抽象方法,交由子类实现,确保子类执行到这里的时候父类的共有控件初始完成。

本篇我们关注bindShareData(data)方法。

这个方法内容如下:

/** 共有数据绑定 */
    private void bindShareData(MomentsInfo data) {
        avatar.loadImageDefault(data.userInfo.avatar);
        nick.setText(data.userInfo.nick);
        textField.setText(data.textField);

        if (TextUtils.isEmpty(data.textField) && contentLayout != null) {
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) contentLayout.getLayoutParams();
            params.topMargin = -UIHelper.dipToPx(context, 8);
            contentLayout.setLayoutParams(params);
        }
        else {
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) contentLayout.getLayoutParams();
            params.topMargin = 0;
            contentLayout.setLayoutParams(params);
        }
        createTime.setText(TimeUtil.getTimeString(data.dynamicInfo.createTime));
        setCommentPraiseLayoutVisibility(data);
        //点赞
        praiseWidget.setDatas(data.praiseList);
        //评论
        addCommentWidget(data.commentList);
    }

我们重点关注addCommentWidget,在前面说过,我们的评论列表使用LinearLayout进行addView。

但这会导致一个问题:由于我们是一个listview,而我们的baseitem本质上是一个viewholder,这也就意味着假如我们划出屏幕,再滑回来,就会出现在原有的view基础上又重复add了一次。

也许有人说,那我们每次removeAllViews后再add不就可以了么,这的确可行,但假如量一大,比如连续10条朋友圈都包含着20~50条评论,也就意味着滑出去再滑回来就需要new 50个commentwidget,这造成的就是视觉上的卡顿,体验十分不好。

而我的解决方法目前想到两个:

  • 维持一个池,从池里拿出可用的进行复用(期望,暂未实现)
  • 动态添加/减去差额,多出remove,少了则new(目前采用,实际上这个方法跟上面的池结合最为妥善)

我目前采用方法2,具体操作如下:

  1. 获取当前评论区控件数量,记为childCount

  2. chidCount与bean的评论数(n)比较

  3. childCount>n,则remove掉childCount-n个view(期望维护一个池,将remove掉的放到复用池)

  4. childCount<n,则new出n-childCount个view

  5. childCount=n,则进行步骤3

  6. 所有view进行数据绑定(数据更新)

这样做的好处就是减少了new对象的操作,起码滑起来顺畅好多。

具体代码如下:

 private void addCommentWidget(List<CommentInfo> commentList) {
        if (commentList == null || commentList.size() == 0) return;
        /**
         * 优化方案:
         * 因为是在listview里面,那么复用肯定有,意味着滑动的时候必须要removeView或者addView
         * 但为了性能提高,不可以直接removeAllViews
         * 于是采取以下方案:
         *    根据现有的view进行remove/add差额
         *    然后统一设置
         * */
        final int childCount = commentLayout.getChildCount();
        if (childCount < commentList.size()) {
            //当前的view少于list的长度,则补充相差的view
            int subCount = commentList.size() - childCount;
            for (int i = 0; i < subCount; i++) {
                CommentWidget commentWidget = new CommentWidget(context);
                LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                params.topMargin=1;
                params.bottomMargin=1;
                commentWidget.setLayoutParams(params);
                commentWidget.setLineSpacing(4,1);
                commentWidget.setOnClickListener(this);
                commentWidget.setOnLongClickListener(this);
                commentLayout.addView(commentWidget);
            }
        }
        else if (childCount > commentList.size()) {
            //当前的view的数目比list的长度大,则减去对应的view
            commentLayout.removeViews(commentList.size(), childCount - commentList.size());
        }
        //绑定数据
        for (int n = 0; n < commentList.size(); n++) {
            CommentWidget commentWidget = (CommentWidget) commentLayout.getChildAt(n);
            if (commentWidget != null) commentWidget.setCommentText(commentList.get(n));
        }
    }

最后是评论控件和点赞列表控件的分割线判定与layout可视性判定:

  /** 是否有点赞或者评论 */
    private void setCommentPraiseLayoutVisibility(MomentsInfo data) {
        if ((data.commentList == null || data.commentList.size() == 0) &&
                (data.praiseList == null || data.praiseList.size() == 0)) {
            //全空,取消显示
            commentAndPraiseLayout.setVisibility(View.GONE);
        }
        else {
            //某项不空,则展示layout
            commentAndPraiseLayout.setVisibility(View.VISIBLE);
            //点赞或者评论某个为空,分割线不展示
            if (data.commentList == null || data.commentList.size() == 0 ||
                    data.praiseList == null || data.praiseList.size() == 0) {
                line.setVisibility(View.GONE);
            }
            else {
                line.setVisibility(View.VISIBLE);
            }
            //点赞为空,取消点赞控件的可见性
            if (data.praiseList == null || data.praiseList.size() == 0) {
                praiseWidget.setVisibility(View.GONE);
            }
            else {
                praiseWidget.setVisibility(View.VISIBLE);
            }
            //评论
            if (data.commentList == null || data.commentList.size() == 0) {
                commentLayout.setVisibility(View.GONE);
            }
            else {
                commentLayout.setVisibility(View.VISIBLE);
            }
        }
    }

其余的相关代码,如bean实体,volley初始化等请看源码,这里就不写出来了。

下一篇将会进行内容页的定制以及默默地精神分裂构造朋友圈虚拟数据。

【附:】本篇JSON数据:

JSON
{
data: {
hostInfo: {
hostid: 1001,
hostAvatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
hostNick: "羽翼君",
hostWallPic: "http://www.pp3.cn/uploads/allimg/111118/10562Cb5-13.jpg"
},
moments: [
{
userInfo: {
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
dynamicInfo: {
dynamicId: 10001,
createUserId: 1001,
dynamicType: 10,
praiseState: 1,
createTime: 1456296202,
candelete: 1
},
textField: "这是第一条朋友圈哦",
praiseList: [
{
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
{
nick: "涵菱",
avatar: "http://img7.3wmm.cc/pic/c/f/d/cfd2c2291ba75df42efedfe4bc62ee39.jpg",
userId: 1004
},
{
nick: "短发美比我在这i",
avatar: "http://img0w.pconline.com.cn/pconline/1310/29/3719457_13667094527.jpg",
userId: 1044
},
{
nick: "丑化小丑不丑。",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021805_451.jpg",
userId: 1054
}
],
commentList: [
{
userA: {
nick: " 振然",
avatar: "http://cdn.duitang.com/uploads/item/201408/30/20140830175648_js4hP.png",
userId: 1014
},
commentId: 1,
content: "新年好",
candelete: 1,
createTime: 1454397315
},
{
userA: {
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
commentId: 1,
content: "hello~",
candelete: 1,
createTime: 1454483655
},
{
userA: {
nick: "诗雁",
avatar: "http://img4.duitang.com/uploads/item/201601/11/20160111175420_ZmTzU.jpeg",
userId: 1006
},
userB: {
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
commentId: 1,
content: "哇,好巧-V-",
candelete: 1,
createTime: 1454483715
}
]
},
{
userInfo: {
nick: "傲露",
avatar: "http://img1.hao661.com/uploads/allimg/c141030/141463I01W940-5IH0.jpg",
userId: 1010
},
dynamicInfo: {
dynamicId: 10003,
createUserId: 1010,
dynamicType: 11,
praiseState: 1,
createTime: 1454743095,
candelete: 0
},
textField: "咳咳。。。。测试一下",
praiseList: [
{
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
},
{
nick: "凌之",
avatar: "http://img5.imgtn.bdimg.com/it/u=3341777813,2293496692&fm=11&gp=0.jpg",
userId: 1003
},
{
nick: "白雪",
avatar: "http://img5.duitang.com/uploads/item/201406/26/20140626190424_TCXuP.jpeg",
userId: 1009
},
{
nick: "柔胤",
avatar: "http://img5.imgtn.bdimg.com/it/u=660454163,590477124&fm=11&gp=0.jpg",
userId: 1011
},
{
nick: "琪家",
avatar: "http://img5.duitang.com/uploads/item/201502/01/20150201174019_A5LYU.png",
userId: 1015
},
{
nick: "暮色伊人。",
avatar: "http://b.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=6a5d1183d358ccbf1be9bd3c29e89006/9213b07eca806538d5541c2295dda144ad348241.jpg",
userId: 1041
},
{
nick: "短发美比我在这i",
avatar: "http://img0w.pconline.com.cn/pconline/1310/29/3719457_13667094527.jpg",
userId: 1044
},
{
nick: "~花舞う街で~",
avatar: "http://c.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=fa3f854c8618367aaddc77d91b43a7e2/bba1cd11728b4710f37cb5a9c3cec3fdfc032307.jpg",
userId: 1045
},
{
nick: "妖视觉〃",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021817_497.jpg",
userId: 1063
},
{
nick: "墨烟三色倾人城。",
avatar: "http://img1.touxiang.cn/uploads/20140815/15-072749_540.jpg",
userId: 1079
},
{
nick: "别嘲笑胖女孩!",
avatar: "http://img1.touxiang.cn/uploads/20140812/12-072839_61.jpg",
userId: 1087
},
{
nick: "默 ’_哀、",
avatar: "http://img1.touxiang.cn/uploads/20140812/12-072932_837.jpg",
userId: 1094
}
],
commentList: [
{
userA: {
nick: "透过骨z1里的傲 つ",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021810_437.jpg",
userId: 1058
},
commentId: 4,
content: "这是啥",
candelete: 0,
createTime: 1454746695
},
{
userA: {
nick: "傲露",
avatar: "http://img1.hao661.com/uploads/allimg/c141030/141463I01W940-5IH0.jpg",
userId: 1010
},
userB: {
nick: "透过骨z1里的傲 つ",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021810_437.jpg",
userId: 1058
},
commentId: 4,
content: "have a test",
candelete: 0,
createTime: 1454746698
},
{
userA: {
nick: "透过骨z1里的傲 つ",
avatar: "http://img1.touxiang.cn/uploads/20141128/28-021810_437.jpg",
userId: 1058
},
userB: {
nick: "傲露",
avatar: "http://img1.hao661.com/uploads/allimg/c141030/141463I01W940-5IH0.jpg",
userId: 1010
},
commentId: 4,
content: "噢~so ga",
candelete: 0,
createTime: 1454750298
}
],
content: {
imgurl: [ ],
dynamicid: 0
}
},
{
userInfo: {
nick: "~花舞う街で~",
avatar: "http://c.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=fa3f854c8618367aaddc77d91b43a7e2/bba1cd11728b4710f37cb5a9c3cec3fdfc032307.jpg",
userId: 1045
},
dynamicInfo: {
dynamicId: 10002,
createUserId: 1045,
dynamicType: 11,
createTime: 1454656515,
candelete: 0
},
praiseList: [ ],
commentList: [
{
userA: {
nick: "皓博",
avatar: "http://t2.du114.com/uploads/160105/18-16010511202M47.jpg",
userId: 1012
},
commentId: 3,
content: "~",
candelete: 0,
createTime: 1454742975
}
],
content: {
imgurl: [ ],
dynamicid: 0
}
},
{
userInfo: {
nick: "白雪",
avatar: "http://img5.duitang.com/uploads/item/201406/26/20140626190424_TCXuP.jpeg",
userId: 1009
},
dynamicInfo: {
dynamicId: 10004,
createUserId: 1009,
dynamicType: 11,
praiseState: 1,
createTime: 1454483715,
candelete: 0
},
textField: "我发发图,我不说话。",
praiseList: [
{
nick: "羽翼君",
avatar: "http://upload.jianshu.io/users/upload_avatars/684042/bd1b2f796e3a.jpg",
userId: 1001
}
],
commentList: [
{
userA: {
nick: "~花舞う街で~",
avatar: "http://c.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=fa3f854c8618367aaddc77d91b43a7e2/bba1cd11728b4710f37cb5a9c3cec3fdfc032307.jpg",
userId: 1045
},
commentId: 2,
content: "路过评论。。。",
candelete: 0,
createTime: 1454742855
}
],
content: {
imgurl: [
"http://img5.duitang.com/uploads/item/201206/06/20120606175201_WZ2F3.thumb.700_0.jpeg",
"http://img5.duitang.com/uploads/item/201206/06/20120606175201_WZ2F3.thumb.700_0.jpeg",
"http://img5.duitang.com/uploads/item/201206/06/20120606175201_WZ2F3.thumb.700_0.jpeg"
],
dynamicid: 10004
}
}
]
},
stateCode: 200,
requestTime: 1456418501691,
start: 4,
loadMore: 0
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容