From 5b7b8184d01f089eb2f474fe2da0ba47cc355c33 Mon Sep 17 00:00:00 2001 From: LinZong <353388639@qq.com> Date: Wed, 5 May 2021 11:56:59 +0800 Subject: [PATCH 1/3] Fix RecyclerView layouts all items regardless remaining spaces --- .../codeview/views/BidirectionalScrollView.kt | 85 +++++++++---------- .../src/main/res/layout/layout_code_view.xml | 5 +- 2 files changed, 44 insertions(+), 46 deletions(-) diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt index ad5c0b1..2c07d7a 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt @@ -1,12 +1,15 @@ package io.github.kbiakov.codeview.views import android.content.Context +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView import android.util.AttributeSet import android.view.MotionEvent import android.view.View +import android.view.View.MeasureSpec import android.view.View.MeasureSpec.makeMeasureSpec import android.widget.HorizontalScrollView -import io.github.kbiakov.codeview.dpToPx +import io.github.kbiakov.codeview.R /** * @class BidirectionalScrollView @@ -18,67 +21,61 @@ import io.github.kbiakov.codeview.dpToPx */ class BidirectionalScrollView : HorizontalScrollView { - private var currentX = 0 - private var currentY = 0 - private var isMoved = false - constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - override fun dispatchTouchEvent(event: MotionEvent): Boolean { - when (event.action) { - MotionEvent.ACTION_DOWN -> { - currentX = event.rawX.toInt() - currentY = event.rawY.toInt() - return super.dispatchTouchEvent(event) - } - MotionEvent.ACTION_MOVE -> { - val deltaX = Math.abs(currentX - event.rawX) - val deltaY = Math.abs(currentY - event.rawY) - scroll(event) + private lateinit var codeContentRv: RecyclerView - val movedOnDistance = dpToPx(context, 2) - if (deltaX > movedOnDistance || deltaY > movedOnDistance) { - isMoved = true - } - } - MotionEvent.ACTION_UP -> { - if (!isMoved) { - return super.dispatchTouchEvent(event) - } - isMoved = false - } - MotionEvent.ACTION_CANCEL -> { - isMoved = false - } - } + override fun onAttachedToWindow() { + super.onAttachedToWindow() + codeContentRv = findViewById(R.id.rv_code_content) + } + + override fun dispatchTouchEvent(event: MotionEvent): Boolean { + super.dispatchTouchEvent(event) + // consumed by this ViewGroup. return true } - private fun scroll(event: MotionEvent) { - val x2 = event.rawX.toInt() - val y2 = event.rawY.toInt() - val posX = currentX - x2 - val posY = currentY - y2 - scrollBy(posX, posY) + override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { + super.onInterceptTouchEvent(ev) + // let touch event be stolen(intercepted) here. + return true + } - currentX = x2 - currentY = y2 + override fun onTouchEvent(ev: MotionEvent?): Boolean { + // first, force child recyclerview handles vertical scrolling. + codeContentRv.onTouchEvent(ev) + // then handle horizontal scrolling here. + super.onTouchEvent(ev) + // finally mark this event is handled. + return true } override fun measureChild(child: View, parentWidthMeasureSpec: Int, parentHeightMeasureSpec: Int) { val zeroMeasureSpec = makeMeasureSpec(0) - child.measure(zeroMeasureSpec, zeroMeasureSpec) + // let the child RecyclerView know the actual height. + child.measure(zeroMeasureSpec, parentHeightMeasureSpec) } override fun measureChildWithMargins( - child: View, - parentWidthMeasureSpec: Int, widthUsed: Int, - parentHeightMeasureSpec: Int, heightUsed: Int + child: View, + parentWidthMeasureSpec: Int, widthUsed: Int, + parentHeightMeasureSpec: Int, heightUsed: Int ) = with(child.layoutParams as MarginLayoutParams) { + val widthMeasureSpec = makeMeasureSpec(leftMargin + rightMargin, MeasureSpec.UNSPECIFIED) - val heightMeasureSpec = makeMeasureSpec(topMargin + bottomMargin, MeasureSpec.UNSPECIFIED) + /** + * Let the child RecyclerView know the actual height again. + * Because [RecyclerView.LayoutManager.mHeightMode] is determined by MeasureSpec mode in [RecyclerView.onMeasure]. + * If gives a [MeasureSpec.UNSPECIFIED] here, mHeightMode will be 0 and [LinearLayoutManager.LayoutState.mInfinite] will be true. + * It will cause [LinearLayoutManager.fill] iterates all items in adapter instead of remaining space allowed, + * which blocks main thread for a long time if there are many items in adapter, + */ + val heightMode = MeasureSpec.getMode(parentHeightMeasureSpec) + val parentHeight = MeasureSpec.getSize(parentHeightMeasureSpec) + val heightMeasureSpec = makeMeasureSpec(parentHeight + topMargin + bottomMargin, heightMode) child.measure(widthMeasureSpec, heightMeasureSpec) } diff --git a/codeview/src/main/res/layout/layout_code_view.xml b/codeview/src/main/res/layout/layout_code_view.xml index ab0491f..956d691 100644 --- a/codeview/src/main/res/layout/layout_code_view.xml +++ b/codeview/src/main/res/layout/layout_code_view.xml @@ -5,16 +5,17 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> + From b95566b779589748bfcc895c7d77c364847ba7f5 Mon Sep 17 00:00:00 2001 From: LinZong <353388639@qq.com> Date: Wed, 5 May 2021 12:05:51 +0800 Subject: [PATCH 2/3] Add comments to explains implementation of onTouchEvent in BidirectionalScrollView --- .../github/kbiakov/codeview/views/BidirectionalScrollView.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt index 2c07d7a..cdd6766 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt @@ -45,6 +45,9 @@ class BidirectionalScrollView : HorizontalScrollView { } override fun onTouchEvent(ev: MotionEvent?): Boolean { + // call recyclerview's and its onTouchEvent here + // to support cross-direction scrolling like 'scroll' method does before. (eg: 45° left top to right down) + // first, force child recyclerview handles vertical scrolling. codeContentRv.onTouchEvent(ev) // then handle horizontal scrolling here. From ae50b0e8dc04369b1f52334ee5edbd941048a25d Mon Sep 17 00:00:00 2001 From: LinZong <353388639@qq.com> Date: Wed, 5 May 2021 12:06:56 +0800 Subject: [PATCH 3/3] clean unused import --- .../io/github/kbiakov/codeview/views/BidirectionalScrollView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt b/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt index cdd6766..693bbba 100644 --- a/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt +++ b/codeview/src/main/java/io/github/kbiakov/codeview/views/BidirectionalScrollView.kt @@ -6,7 +6,6 @@ import android.support.v7.widget.RecyclerView import android.util.AttributeSet import android.view.MotionEvent import android.view.View -import android.view.View.MeasureSpec import android.view.View.MeasureSpec.makeMeasureSpec import android.widget.HorizontalScrollView import io.github.kbiakov.codeview.R @@ -69,6 +68,7 @@ class BidirectionalScrollView : HorizontalScrollView { ) = with(child.layoutParams as MarginLayoutParams) { val widthMeasureSpec = makeMeasureSpec(leftMargin + rightMargin, MeasureSpec.UNSPECIFIED) + /** * Let the child RecyclerView know the actual height again. * Because [RecyclerView.LayoutManager.mHeightMode] is determined by MeasureSpec mode in [RecyclerView.onMeasure].