Reputation: 1028
I want to implement an horizontal RecyclerView inside a vertical RecyclerView.
The end result should be like this:
So, for each element in the vertical RecyclerView, I need another one in an horizontal way. Kind of like a school schedule, with the Day in the left, and the actual schedule on the right, scroll-able horizontally.
I managed to achieve this, by putting an RecyclerView inside of the first RecyclerView item. Everything works perfectly, but all the horizontal RecyclerViews are scrolling separately. What I want to do is make all the horizontal RecyclerViews to sync and scroll at the same time. How can I achieve this?
The way I set the adapter and the horizontal RecyclerView inside the onBindViewHolder method of the vertical adapter is this:
scheduleAdapter = new ScheduleAdapter(context, data);
holder.scheduleRecyclerView.setAdapter(scheduleAdapter);
holder.scheduleRecyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
Upvotes: 7
Views: 4283
Reputation: 31
In MainActivity have recyclerView vertical:
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)
}
}
In Adapter have list recyclerView horizontal:
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 {
}
}
Upvotes: 1
Reputation: 3200
Each recyclerview should add the below scroll listener
.
m_jParentRecyclerViewLayoutManager
is the parent RecyclerView whose items has a 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);
}
EDIT : In your parent recyclerview adapter.
@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);
}
EDIT 2 : There is an Table View library which scroll all child recyclerviews sync. You can check the source code
Upvotes: 6