在适配器中同步多个RecyclerView的滚动

7

我想在一个垂直的RecyclerView中实现一个水平的RecyclerView

最终效果应该是这样的:

enter image description here

所以,对于垂直RecyclerView中的每个元素,我需要另一个水平方向上的元素。就像一个课程表,左边是日期,右边是实际的课程安排,可以水平滚动。

我已经成功地通过将一个RecyclerView放在第一个RecyclerView项中来实现这一点。一切都运行得很完美,但所有的水平RecyclerView都是单独滚动的。我想要做的是使所有的水平RecyclerView同步滚动。我该如何实现这个功能?

我在垂直适配器的onBindViewHolder方法中设置适配器和水平RecyclerView的方式如下:

scheduleAdapter = new ScheduleAdapter(context, data);
holder.scheduleRecyclerView.setAdapter(scheduleAdapter);
holder.scheduleRecyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
2个回答

6
每个RecyclerView都应该添加以下的滚动监听器。
m_jParentRecyclerViewLayoutManager是包含了一个RecyclerView的父级RecyclerView。
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener {

    @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            scrollAllRecyclerView(recyclerView, dx, dy);
        }

        private void scrollAllRecyclerView(RecyclerView recyclerView, int dx, int dy) {

                // Scroll children RecyclerViews except the recyclerView that is listened.
                for (int i = 0; i < m_jParentRecyclerViewLayoutManager.getChildCount(); i++) {
                    RecyclerView child = (RecyclerView) m_jParentRecyclerViewLayoutManager.getChildAt(i);
                    if (child != recyclerView) {
                        scroll(child, dx, dy);
                    }
                }
            }
        }

        private void scroll(RecyclerView recyclerView, int dx, int dy) {
            recyclerView.removeOnScrollListener(this);
            recyclerView.scrollBy(dx, dy);
            recyclerView.addOnScrollListener(this);
        }

编辑: 在您的父RecyclerView适配器中。

 @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            RecyclerView recyclerView = new RecyclerView(mContext);
            .. set layout manager & your adapter .

            if (scrollListener != null) {
                recyclerView.removeOnScrollListener(scrollListener );
                recyclerView.addOnScrollListener(scrollListener );
            }
            return new RecyclerViewViewHolder(recyclerView);
        }

编辑2:有一个表格视图库可以同步滚动所有子RecyclerView。您可以查看源代码。


你应该在父RecyclerView的onCreateViewHolder中添加这个滚动监听器,在那里为父RecyclerView的每个项目创建一个RecyclerView。我已经编辑了我的答案。 - ziLk
你应该创建一个自定义的RecyclerView适配器,其中包含一个set/get方法来引用它。 - ziLk
我已经按照上面所说成功地引用了父级RecyclerView,但现在我遇到了一个类转换异常,就在这里RecyclerView child = (RecyclerView) mRecyclerView.getChildAt(i);。两个视图都是RecyclerView,所以我不明白为什么会出现这种情况。 - Phantom
1
好的,我终于成功了。问题是当我滚动时,滚动条会到队列的末尾,导致应用程序冻结。 我会为你的努力点赞。谢谢。 - Phantom
1
我成功地实现了您的解决方案,并进行了一些更改,但核心部分保持不变。我遇到的唯一问题是,如果在快速拖动后一个RecyclerView正在SETTLING时,我点击另一个RecyclerView,第一个RecyclerView将继续滚动,整个过程就会失去同步... - dvdciri
显示剩余12条评论

1
在MainActivity中,有一个垂直的recyclerView。
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        verticalRecyclerView.layoutManager = LinearLayoutManager(this)

        val onScrollListener = object: RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                (recyclerView.adapter as? Adapter)?.matchOffset()
            }
        }

        verticalRecyclerView.addOnScrollListener(onScrollListener)
        verticalRecyclerView.adapter = Adapter(verticalRecyclerView)
    }
}

在适配器中,有一个水平的RecyclerView列表:
class Adapter(private val parentRV: RecyclerView): RecyclerView.Adapter<CustomViewHolder>() {

    var horizontalRecyclerViews = mutableListOf<RecyclerView>()
    var absoluteOffset: Int? = null
    //numberOfItems
    override fun getItemCount(): Int {
        return 25
    }

    fun matchOffset(offset: Int? = absoluteOffset) {
        offset?.let { offsetValue ->
            horizontalRecyclerViews.forEach { recyclerView ->
                val currentOffset = recyclerView.computeHorizontalScrollOffset()
                if (offsetValue != currentOffset) {
                    recyclerView.scrollBy(offsetValue-currentOffset, 0)
                }
            }
        }
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
        val layoutInflator = LayoutInflater.from(parent.context)
        val cellForRow = layoutInflator.inflate(R.layout.item_recycler_view, parent, false)
        return  CustomViewHolder(cellForRow)
    }

    override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
        holder.view.horizontalRecyclerView.layoutManager = LinearLayoutManager(holder.view.context, LinearLayoutManager.HORIZONTAL, false)

        val onTouchListener = object: RecyclerView.OnItemTouchListener {
            override fun onTouchEvent(p0: RecyclerView, p1: MotionEvent) {
            }
            override fun onInterceptTouchEvent(p0: RecyclerView, p1: MotionEvent): Boolean {
                if (p1.action == MotionEvent.ACTION_UP) {
                    absoluteOffset = p0.computeHorizontalScrollOffset()
                    return true
                }
                return false
            }
            override fun onRequestDisallowInterceptTouchEvent(p0: Boolean) {
            }
        }

        val onScrollListener = object: RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                val value = recyclerView.computeHorizontalScrollOffset()
                matchOffset(value)
            }
        }


        val child = BlockAdapter()
        holder.view.horizontalRecyclerView.tag = position
        holder.view.horizontalRecyclerView.clearOnScrollListeners()
        holder.view.horizontalRecyclerView.addOnItemTouchListener(onTouchListener)
        holder.view.horizontalRecyclerView.addOnScrollListener(onScrollListener)
        holder.view.horizontalRecyclerView.adapter = child
        horizontalRecyclerViews.add(holder.view.horizontalRecyclerView)
    }
}

class BlockAdapter(): RecyclerView.Adapter<CustomViewHolder>() {

    //numberOfItems
    override fun getItemCount(): Int {
        return 30
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
        val layoutInflator = LayoutInflater.from(parent.context)
        val cellForRow = layoutInflator.inflate(R.layout.item_block, parent, false)
        return  CustomViewHolder(cellForRow)
    }

    override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
        holder.view.block.setBackgroundColor(
                if (position % 2 == 0) {
                    Color.BLUE
                } else {
                    Color.RED
                }
        )
        holder.view.block.setTextColor(Color.WHITE)
        holder.view.block.text = position.toString()
    }
}

class CustomViewHolder(val view: View): RecyclerView.ViewHolder(view) {

    init {
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接