2个recyclerview联动

滑动一个rv的时候另一个也一起动的效果
以前看过一个帖子,不记得了,一直想学着写,太久了。找了半天没找到那帖子,所以也就没好的效果图了,就贴下自己实现的图,有点丑,我记得原图好像是个银行的。
最开始长这样,恩,为了省事我就用个颜色块代替具体的布局拉。


image.png

滑动的中间是这样


image.png

最后一个长这样


image.png

这个是用2个recyclerview写的,下边那个item宽度就是屏幕宽,上边那个宽度比屏幕小点,所以中间加了间隔,用itemdecoration写的
下图是代码中变量代表的意思,简单画一下,好理解,实际中你只要按需求修改edgeShow的大小即可。


image.png

先上代码
2个item也就是弄个线性布局包裹一个textview。
item_linkage_top.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
<TextView
    android:id="@+id/tv_num"
    android:gravity="center"
    android:textColor="#fff"
    android:layout_gravity="center"
    android:textSize="25sp"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</LinearLayout>

item_linkage_bottom.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
<TextView
    android:id="@+id/tv_bottom"
    android:gravity="center"
    android:textColor="#fff"
    android:layout_gravity="center"
    android:textSize="25sp"
    android:layout_marginLeft="30dp"
    android:layout_marginRight="30dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</LinearLayout>

完事activity里就2个rv

<android.support.v7.widget.RecyclerView
    android:id="@+id/rv1"
    android:layout_weight="1"
    android:layout_width="match_parent"
    android:layout_height="0dp"/>
<android.support.v7.widget.RecyclerView
    android:id="@+id/rv2"
    android:layout_marginTop="10dp"
    android:layout_weight="1.5"
    android:layout_width="match_parent"
    android:layout_height="0dp"/>

核心kotlin代码
主要原理就是监听rv的滚动距离,根据效果图,其实可以知道两者滚动的距离是不一样的,然后我们可以根据比例,用其中一个算出另外一个应该滚动的距离。

下边的代码适合只有3页数据的情况,因为用到了computeHorizontalScrollOffset方法,而这个方法对于decoration的offset不一样的情况,算出来的偏移量是有误差的,3页的话,第一个和最后一个量变的偏移量一样大,所以刚好没太大影响 .

文末有修改后的代码,就是把computeHorizontalScrollOffset方法删了,弄了2个变量存储offset

    var testDatas = arrayListOf<Int>()
    var smallWidth = 1//上边那个的item宽
    var screenWidth=1;//屏幕宽,也就是rv2的item宽
    var edgeShow=20 //凸出来的那部分距离
    var smallScroll=0//rv1每移动一个item滚动的距离
    var whoScroll=0;//正在触摸哪个rv,1和2分别表示rv1和rv2
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_linkage_recyclerviews)
        testDatas.add(Color.RED)
        testDatas.add(Color.BLUE)
        testDatas.add(Color.GREEN)
        testDatas.add(Color.YELLOW)
        testDatas.add(Color.LTGRAY)
        screenWidth=windowManager.defaultDisplay.width

        smallWidth = screenWidth - edgeShow*4
        smallScroll=screenWidth-edgeShow*3 

    method1()
    }

    private fun method1() {
        rv1.apply {
            layoutManager = LinearLayoutManager(this@ActivityLinkageRecyeclerViews, LinearLayoutManager.HORIZONTAL, false)
            addItemDecoration(object : RecyclerView.ItemDecoration() {

                override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
                    var position = parent.getChildAdapterPosition(view)
                    var count = parent.adapter.itemCount
                    outRect.left = edgeShow/2
                    outRect.right = edgeShow/2
                    if (position == 0) {
                        outRect.left = edgeShow*2
                    } else if (position == count - 1) {
                        outRect.right = edgeShow*2
                    }
                }
            })

            adapter = object : BaseRvAdapter<Int>(testDatas) {
                override fun getLayoutID(viewType: Int): Int {
                    return R.layout.item_linkage_top
                }

                override fun onBindViewHolder(holder: BaseRvHolder, position: Int) {
                    holder.setText(R.id.tv_num, "$position")
                    var params = holder.itemView.layoutParams
                    params.width = smallWidth
                    holder.itemView.setBackgroundColor(testDatas[position])
                }
            }
        }

        rv2.apply {
            layoutManager =LinearLayoutManager(this@ActivityLinkageRecyeclerViews, LinearLayoutManager.HORIZONTAL, false)
            adapter = object : BaseRvAdapter<Int>(testDatas) {
                override fun getLayoutID(viewType: Int): Int {
                    return R.layout.item_linkage_bottom
                }

                override fun onBindViewHolder(holder: BaseRvHolder, position: Int) {
                    holder.setText(R.id.tv_bottom, "$position")
                    holder.itemView.setBackgroundColor(testDatas[ position])
                }
            }
        }
        //2个helper 让rv的item自动居中显示
        PagerSnapHelper().apply {  attachToRecyclerView(rv1)}
        PagerSnapHelper().apply {  attachToRecyclerView(rv2)}


        rv1.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if(whoScroll==1){//等于1表示当前手指触摸的是rv1,用这里的滚动来操作rv2
                 val rv2preScroll= screenWidth*rv1.computeHorizontalScrollOffset()/smallScroll//根据rv1滚动的距离来计算rv2应该滚动的距离,2者的比列就是
                    rv2.scrollBy(rv2preScroll-rv2.computeHorizontalScrollOffset(),0)
                }
            }

            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                if(newState==RecyclerView.SCROLL_STATE_IDLE){
                    whoScroll=0
                }else if(newState==RecyclerView.SCROLL_STATE_DRAGGING||newState==RecyclerView.SCROLL_STATE_SETTLING){
                    whoScroll=1 
                }
            }
        })
        rv2.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if(whoScroll==2){//等于2表示当前触摸的是rv2,用这里的滚动来处理rv1的滚动
                   val rv1preScroll=smallScroll *rv2.computeHorizontalScrollOffset()/screenWidth
                    rv1.scrollBy(rv1preScroll-rv1.computeHorizontalScrollOffset(),0)
                }
            }

            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                if(newState==RecyclerView.SCROLL_STATE_IDLE){
                    whoScroll=0
                }else if(newState==RecyclerView.SCROLL_STATE_DRAGGING||newState==RecyclerView.SCROLL_STATE_SETTLING){
                    whoScroll=2
                }
            }
        })
    }

  //这个方法是必要的,因为如果你两根手指分别按在2个rv上就不好了。。这里显示只能一个手指触摸。
    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        if((ev.action and MotionEvent.ACTION_MASK)==MotionEvent.ACTION_POINTER_DOWN){
            return true
        }
        return super.dispatchTouchEvent(ev)
    }

写的时候发现的问题

我以为SCROLL_STATE_DRAGGING 手指触摸滑动,完事松开手指,还想着接下来就是SCROLL_STATE_SETTLING的状态了,结果不是。
SCROLL_STATE_DRAGGING状态,松开手指,会先SCROLL_STATE_IDLE,完事接着SCROLL_STATE_SETTLING,最后又是SCROLL_STATE_IDLE的状态。

2个方法学习下

需要说明下,这2个方法在item的decoration的offset,margin都一样的情况下才是准确的,否则是不准确的,具体原因后边有分析
rv1.computeHorizontalScrollOffset()
这个就是当前rv的偏移量。最开始是0,比如移动一个item的距离100,那么偏移量就是100,继续移动2个item,那偏移量就是200.

rv2.computeHorizontalScrollRange()
这个就是rv能滚动的总距离,比如一个item宽度是100,有5个item,那么这个就是500.【不算间距的情况】

最近发现的问题

上边写demo测试都用的3个item测试了,然后没事弄了5个,发现从第二个item开始往第三个滑动的时候就出问题了。
被动滑动的那个rv会瞬间漂移一段距离,打印了下日志如下

//rv1的监听
 if(whoScroll==1){//等于1表示当前手指触摸的是rv1,用这里的滚动来操作rv2
                   val rv2preScroll= screenWidth*rv1.computeHorizontalScrollOffset()/smallScroll//根据rv1滚动的距离来计算rv2应该滚动的距离,2者的比列就是
                    rv2.scrollBy(rv2preScroll-rv2.computeHorizontalScrollOffset(),0)
                }
                println("rv1 ====================${dx}")

//rv2的监听
    if(whoScroll==2){//等于2表示当前触摸的是rv2,用这里的滚动来处理rv1的滚动
                    val rv1preScroll=smallScroll *rv2.computeHorizontalScrollOffset()/screenWidth

                    println("rv2 scroll====================${dx}====${rv1preScroll}/${rv1.computeHorizontalScrollOffset()}" +
                            "====first=${(rv1.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()}")
                    rv1.scrollBy(rv1preScroll-rv1.computeHorizontalScrollOffset(),0)
                }

I/System.out: rv2 scroll====================1====993/992====first=0
I/System.out: rv1 ====================1
I/System.out: rv2 scroll====================1====994/993====first=0
I/System.out: rv1 ====================1
I/System.out: rv2 scroll====================1====995/964====first=1
I/System.out: rv1 ====================31
I/System.out: rv2 scroll====================1====996/995====first=1
I/System.out: rv1 ====================1

看日志发现,当rv1的第一个可见的view的position改变的一瞬间,那个获取到的computeHorizontalScrollOffset发生了极大的变化,然后瞬间又变回来了。

题外话:查资料的时候发现还有个rv1.computeHorizontalScrollExtent()方法,感觉返回的就是控件的宽度
看到一篇帖子https://blog.csdn.net/u010291880/article/details/50724663

滑动测试,能感觉到就是第一个item消失的瞬间,第二个item立马冲上去了,就是中间的itemdecoration瞬间没了。

computeHorizontalScrollOffset

看来得仔细看下这个方法了

    static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
            View startChild, View endChild, RecyclerView.LayoutManager lm,
            boolean smoothScrollbarEnabled, boolean reverseLayout) {
        if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null
                || endChild == null) {
            return 0;
        }
        final int minPosition = Math.min(lm.getPosition(startChild),
                lm.getPosition(endChild));
        final int maxPosition = Math.max(lm.getPosition(startChild),
                lm.getPosition(endChild));
        final int itemsBefore = reverseLayout
                ? Math.max(0, state.getItemCount() - maxPosition - 1)
                : Math.max(0, minPosition);
        if (!smoothScrollbarEnabled) {
            return itemsBefore;
        }
        final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild)
                - orientation.getDecoratedStart(startChild));
        final int itemRange = Math.abs(lm.getPosition(startChild)
                - lm.getPosition(endChild)) + 1;
        final float avgSizePerRow = (float) laidOutArea / itemRange;
    //上边这些和computeHorizontalScrollRange里的差不多,可以先到下边看下解释再回来,
//avgSizePerRow 可以看到这里也是算了一下没个item的平均值,然后乘以itemBefore数
//而我们这里的offset是不一样的,所以这里计算出来的就有问题了
        return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
                - orientation.getDecoratedStart(startChild)));
    }

看下最开始的代码,我们的rv1里的offset ,position=0的时候和最后一个位置的offset是不一样的
position为0的时候offset的left是edgeshow*2
computeScrollOffset刚开始startchild=0,endchild=1,laidOutArea 是比较大的,因为第一个的offset的left很大,完事当startChild=1,endchild=2的时候,laidOutArea 就是正常的,也就是变小了,所以上边的日志里
在findFirstVisibleItemPosition由0变到1的时候,也就是上边的情况,laidOutArea 变小了,导致计算出来的
computeScrollOffset的值就瞬间变小了。

computeHorizontalScrollRange

也简单看了下代码,会发现,如果你item的offset设置的不一样,这玩意计算出来感觉是有问题的。

//
    private int computeScrollRange(RecyclerView.State state) {
        if (getChildCount() == 0) {
            return 0;
        }
        ensureLayoutState();
        return ScrollbarHelper.computeScrollRange(state, mOrientationHelper,
                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
                this, mSmoothScrollbarEnabled);
    }
    
        // smooth scrollbar enabled. try to estimate better.
        final int laidOutArea = orientation.getDecoratedEnd(endChild)
                - orientation.getDecoratedStart(startChild);
        final int laidOutRange = Math.abs(lm.getPosition(startChild)
                - lm.getPosition(endChild))
                + 1;
        // estimate a size for full list.
        return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());

如果可见的有2个item,比如startChild=0,endChild=1,下边先算出endChild的右边位置,减去startChild的左边位置,
这算出来的是2个item的距离,然后除以laidOutRange =2的
如果可见的item只有1个,那么laidOutArea 就是它的右边界减去左边界,完事除以laidOutRange =1

最后算的总range,就是乘以itemcount,这里就应该可以看出来,如果我们每个item的offset不一样,那么
getDecoratedStart(View view) Returns the start of the view including its decoration and margin.
getDecoratedEnd(View view) Returns the end of the view including its decoration and margin.
看下这2个方法,可以看到,计算位置的时候还包括了decoration以及margin的。所以,如果item的decoration的offset不一样,或者margin不一样,其实最后算出来的range并不准确,也只是个大概值。

修改后的代码

自己手动存下offset吧,最开始就这样写的,后来看到computeHorizontalScrollOffset方法,还以为发现新大陆了,觉得比自己手动保存offset高大上,而且那时候测试也是3个item,所以没发现问题。
现在又改回去了。。

import android.graphics.Color
import android.graphics.Rect
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.PagerSnapHelper
import android.support.v7.widget.RecyclerView
import android.view.MotionEvent
import android.view.View
import com.charliesong.demo0327.R
import com.charliesong.demo0327.base.BaseActivity
import com.charliesong.demo0327.base.BaseRvAdapter
import com.charliesong.demo0327.base.BaseRvHolder
import kotlinx.android.synthetic.main.activity_linkage_recyclerviews.*

/**
 * Created by charlie.song on 2018/5/21.
 */
class ActivityLinkageRecyeclerViews : BaseActivity() {
    var testDatas = arrayListOf<Int>()
    var smallWidth = 1//上边那个的item宽
    var screenWidth=1;//屏幕宽,也就是rv2的item宽
    var edgeShow=30 //凸出来的那部分距离
    var smallScroll=0//rv1每移动一个item滚动的距离
    var whoScroll=0;//正在触摸哪个rv,1和2分别表示rv1和rv2
    var offset1=0;//rv1的offset
    var offset2=0;//rv2的offset
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_linkage_recyclerviews)
        testDatas.add(Color.RED)
        testDatas.add(Color.BLUE)
        testDatas.add(Color.GREEN)
        testDatas.add(Color.YELLOW)
        testDatas.add(Color.LTGRAY)
        screenWidth=windowManager.defaultDisplay.width

        smallWidth = screenWidth - edgeShow*4
        smallScroll=screenWidth-edgeShow*3

    method1()
    }

    private fun method1() {
        rv1.apply {
            layoutManager = LinearLayoutManager(this@ActivityLinkageRecyeclerViews, LinearLayoutManager.HORIZONTAL, false)
            addItemDecoration(object : RecyclerView.ItemDecoration() {

                override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
                    var position = parent.getChildAdapterPosition(view)
                    var count = parent.adapter.itemCount
                    outRect.left = edgeShow/2
                    outRect.right = edgeShow/2
                    if (position == 0) {
                        outRect.left = edgeShow*2
                    } else if (position == count - 1) {
                        outRect.right = edgeShow*2
                    }
                }
            })

            adapter = object : BaseRvAdapter<Int>(testDatas) {
                override fun getLayoutID(viewType: Int): Int {
                    return R.layout.item_linkage_top
                }

                override fun onBindViewHolder(holder: BaseRvHolder, position: Int) {
                    holder.setText(R.id.tv_num, "$position")
                    var params = holder.itemView.layoutParams
                    params.width = smallWidth
                    holder.itemView.setBackgroundColor(testDatas[position])
                }
            }
        }

        rv2.apply {
            layoutManager =LinearLayoutManager(this@ActivityLinkageRecyeclerViews, LinearLayoutManager.HORIZONTAL, false)
            adapter = object : BaseRvAdapter<Int>(testDatas) {
                override fun getLayoutID(viewType: Int): Int {
                    return R.layout.item_linkage_bottom
                }

                override fun onBindViewHolder(holder: BaseRvHolder, position: Int) {
                    holder.setText(R.id.tv_bottom, "$position")
                    holder.itemView.setBackgroundColor(testDatas[ position])
                }
            }
        }
        //2个helper 让rv的item自动居中显示
        PagerSnapHelper().apply {  attachToRecyclerView(rv1)}
        PagerSnapHelper().apply {  attachToRecyclerView(rv2)}


        rv1.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                offset1+=dx;
                if(whoScroll==1){//等于1表示当前手指触摸的是rv1,用这里的滚动来操作rv2
                   val rv2preScroll= screenWidth*offset1/(smallScroll)//根据rv1滚动的距离来计算rv2应该滚动的距离,2者的比列就是
                    rv2.scrollBy(rv2preScroll-offset2,0)
                }
            }

            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                if(newState==RecyclerView.SCROLL_STATE_IDLE){
                    whoScroll=0
                }else if(newState==RecyclerView.SCROLL_STATE_DRAGGING||newState==RecyclerView.SCROLL_STATE_SETTLING){
                    whoScroll=1
                }
            }
        })
        rv2.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                offset2+=dx;
                if(whoScroll==2){//等于2表示当前触摸的是rv2,用这里的滚动来处理rv1的滚动
                    val rv1preScroll=(smallScroll) *offset2/screenWidth
                    rv1.scrollBy(rv1preScroll-offset1,0)
                }
            }

            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                if(newState==RecyclerView.SCROLL_STATE_IDLE){
                    whoScroll=0
                }else if(newState==RecyclerView.SCROLL_STATE_DRAGGING||newState==RecyclerView.SCROLL_STATE_SETTLING){
                    whoScroll=2
                }
            }
        })
    }
    
    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        if((ev.action and MotionEvent.ACTION_MASK)==MotionEvent.ACTION_POINTER_DOWN){
            return true
        }
        return super.dispatchTouchEvent(ev)
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容