Error horizontal ViewPager2 setScrollEnabled = false in a horizontal ViewPager2 . parent pageview2 cant scroll . I want to check when isUserInputEnabled = false then set parent.requestDisallowInterceptTouchEvent(false) . Can you provide the code?
this is func in react-native-pager-view/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt
private fun handleInterceptTouchEvent(e: MotionEvent) {
val orientation = parentViewPager?.orientation ?: return
// Early return if child can't scroll in same direction as parent
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
return
}
if (e.action == MotionEvent.ACTION_DOWN) {
initialX = e.x
initialY = e.y
parent.requestDisallowInterceptTouchEvent(true)
} else if (e.action == MotionEvent.ACTION_MOVE) {
val dx = e.x - initialX
val dy = e.y - initialY
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
// assuming ViewPager2 touch-slop is 2x touch-slop of child
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
if (scaledDx > touchSlop || scaledDy > touchSlop) {
if (isVpHorizontal == (scaledDy > scaledDx)) {
// Gesture is perpendicular, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
} else {
// Gesture is parallel, query child if movement in that direction is possible
if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
// Child can scroll, disallow all parents to intercept
parent.requestDisallowInterceptTouchEvent(true)
} else {
// Child cannot scroll, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
}
}
}
}
}
I tried but I can't finish , I don't know kotlin
Related
I have a circle as ImageView and I want to be able to smoothly rotate it when swiping with a finger. If I swipe slowly, I want the circle to rotate slowly until the swiping action stops. If I swipe faster, I want the image to rotate more.
I've implemented the code that I've found here. This longer version allows to change swiping direction without lifting the finger, so that the circle would start rotating to the other direction.
This is my OnTouchListener class:
open class OnSwipeTouchListenerV2() : View.OnTouchListener {
private val SWIPE_THRESHOLD = 0f
private var initialX = 0f
private var initialY = 0f
private var previousX = 0f
private var previousY = 0f
private var currentX = 0f
private var currentY = 0f
private var diffX = 0f
private var diffY = 0f
private var swipeH = "0" // Horizontal swipe direction (LEFT or RIGHT)
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
initialX = event.x
initialY = event.x
return true
}
MotionEvent.ACTION_MOVE -> {
currentX = event.x
currentY = event.x
// These where original diff calculations
// The issue with this is that diffs get bigger when swiping further
// It is an issue because I'm trying to use the diff as an angle to rotate by
// diffX = currentX - initialX // Original
// diffY = currentY - initialY // Original
diffX = currentX - previousX // My Implementation
diffY = currentY - previousY // My Implementation
when (swipeH) {
"LEFT" -> {
if (currentX > previousX) {
swipeH = "RIGHT"
initialX = previousX
diffX = currentX - initialX
} else {
// Intentionally kept empty
}
}
"RIGHT" -> {
if (currentX < previousX) {
swipeH = "LEFT"
initialX = previousX
diffX = currentX - initialX
} else {
// Intentionally kept empty
}
}
else -> {
if (currentX < initialX) {
swipeH = "LEFT"
} else if (currentX > initialX) {
swipeH = "RIGHT"
} else {
// Intentionally kept empty
}
}
}
previousX = currentX
previousY = currentY
if (abs(diffX) > abs(diffY)) {
if (abs(diffX) > SWIPE_THRESHOLD) {
if (diffX > 0) {
onSwipeRight(diffX)
} else {
onSwipeLeft(diffX)
}
}
} else {
if (abs(diffY) > SWIPE_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom(diffY)
} else {
onSwipeTop(diffY)
}
}
}
return true
}
MotionEvent.ACTION_UP -> {
swipeH = "0"
initialX = 0f
initialY = 0f
previousX = 0f
previousY = 0f
diffX = 0f
diffY = 0f
return true
}
else -> {
return false
}
}
}
open fun onSwipeRight(diffX: Float) {}
open fun onSwipeLeft(diffX: Float) {}
open fun onSwipeTop(diffY: Float) {}
open fun onSwipeBottom(diffY: Float) {}
}
This is my MainActivity file:
class MainActivityBackup : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val wheel = findViewById<ImageView>(R.id.imageView1) as ImageView
window.decorView.setOnTouchListener(object: OnSwipeTouchListenerV2() {
override fun onSwipeLeft(diffX: Float) {
rotateImage(wheel, diffX)
}
override fun onSwipeRight(diffX: Float) {
rotateImage(wheel, diffX)
}
})
}
fun rotateImage(imageView: ImageView, angle: Float) {
imageView.animate().rotation(angle).start()
}
}
It kind of works, but not really... On emulator, if I swipe just a bit, then it works fine, but if I do a continuous swipe slowly, then the circle moves very little during the swipe and makes a normal rotation once the swipe is finished. Same happens during a fast longer swipe: the circle does not move during the swipe but rotates (more than during the slow swipe because I'm using diffX as an angle of how much to rotate) once the swipe is finished.
I've tried a different function for the actual rotation found here. I've tried different interpolators, that did not help.
Maybe I should not be using diffX for rotation? But from Log messages (to get the value of diffX during each ACTION_MOVE) I saw that during a continuous swipe the circle definitely does not rotate as much as it is supposed to.
My guess is that this happens because during a continuous swipe the are a lot of MotionEvent.ACTION_MOVE actions and each of them makes a call to onSwipeLeft or onSwipeRight functions, but they do not finish for some reason because a new MotionEvent.ACTION_MOVE action arrives, right?
So, any ideas how to smoothly rotate a circle?
Solved
Figured it out myself... I guess it was because I was using animation and when there are many ACTION_MOVE events happening, then animation just cannot keep up. This is the change that I've made (not exactly this, but it's the main idea why it was not working properly):
fun rotateImage(imageView: ImageView, angle: Float) {
// old code
imageView.animate().rotation(angle).start()
// new code
imageView.rotation = angle
}
I want to rotate my view by dragging a helper button on the edge of the view.
Here Image Here is video
In this case, when the view is standing still, only the change on event.Y should be affected. As the view becomes horizontal, the event.X change should increase and only the event.X change should affect the view when the view is fully horizontal
Can anyone know how to do this?
current code is, but this worked incorrectly:
emojiBinding.btnRotate.setOnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
boolArray[0] = false
boolArray[2] = false
startPositionOfRotation.x = event.x
startPositionOfRotation.y = event.y
}
MotionEvent.ACTION_MOVE -> {
val degree = getRotationDegree(emojiBinding, event.x, event.y)
val rotState = emojiBinding.root.rotation
var mult = 0f
if (abs(rotState)%90 < 45 ){
mult = if (event.x >=0)1f else -1f
}
else{
mult = if (event.y >=0)1f else -1f
}
emojiBinding.root.rotation += mult * degree
Timber.d("Degree $degree Rotation ${emojiBinding.root.rotation}")
}
MotionEvent.ACTION_UP -> {
startPositionOfRotation.x = 0f
startPositionOfRotation.y = 0f
boolArray[0] = true
boolArray[2] = true
}
}
boolArray[1]
}
I am using "Draw over other aps" to show a chat head like view.
I need to position it relatively and show animation while dismissing it.
Please see first video/gif of google:
Please note:
a) In first video: the view can be position relative to screen blocks designed by system.
b) The movement is smooth.
c) While dismissing/closing view an animation cross is displayed.
Please see second video/gif of my app:
The movement is not that smooth, its plain and no animation.
Below is my code:
floatingView.setOnTouchListener(object : View.OnTouchListener {
private var initialX = 0
private var initialY = 0
private var initialTouchX = 0f
private var initialTouchY = 0f
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
ACTION_UP -> {
overlayView.visibility = INVISIBLE
if (v.isOverlap(overlayView)) {
stopSelf()
}
return false
}
ACTION_MOVE -> {
overlayView.visibility = View.VISIBLE
layoutParams.x = (initialX + (event.rawX - initialTouchX).toInt())
layoutParams.y = (initialY + (event.rawY - initialTouchY).toInt())
windowManager.updateViewLayout(floatingView, layoutParams)
return false
}
ACTION_DOWN -> {
initialX = layoutParams.x
initialY = layoutParams.y
initialTouchX = event.rawX
initialTouchY = event.rawY
return false
}
}
return false
}
}
How should I tweak it to achieve the first video ?
I am using WindowManager.LayoutParams
var floatingView = LayoutInflater.from(this).inflate(R.layout.chat, null)
var windowManager: WindowManager getSystemService(WINDOW_SERVICE) as WindowManager
var layoutParams: WindowManager.LayoutParams = WindowManagerLayoutParams(WRAP_CONTENT, WRAP_CONTENT, LAYOUT_FLAG, FLAG_NOT_FOCUSABLE, TRANSLUCENT).apply {
gravity = TOP or START
x = 0
y = 100
}
windowManager.addView(floatingView, layoutParams)
Please try below code and check if it's not working then tell me.
#SuppressLint("ClickableViewAccessibility")
private fun onTouchListener()
: View.OnTouchListener {
return View.OnTouchListener { view, event ->
val x = event.rawX.toInt()
val y = event.rawY.toInt()
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
val lParams = view.layoutParams as RelativeLayout.LayoutParams
xDelta = x - lParams.leftMargin
yDelta = y - lParams.topMargin
}
MotionEvent.ACTION_UP -> {
}
MotionEvent.ACTION_MOVE -> {
val layoutParams = view
.layoutParams as RelativeLayout.LayoutParams
layoutParams.leftMargin = x - xDelta
layoutParams.topMargin = y - yDelta
layoutParams.rightMargin = 0
layoutParams.bottomMargin = 0
view.layoutParams = layoutParams
}
}
your_view.invalidate()
true
}
}
I'm using viewPager2 along with tab layout. I only have 2 fragments.
There is a RecyclerView in my second fragment and a swipe refresh layout.
My problem is when I try to swipe down to refresh , it conflicts with the Viewpager's vertical swipe and I'm not able to swipe down correctly.
So is there a way to disable just the vertical swipe of view Pager ? I still want it to be able to swipe horizontally.
You can disable viewpager2 scroll by setting isUserInputEnabled to false
viewPager2.isUserInputEnabled = false
This will disable all events, but in your case i think you need to disable move down action only, you need to intercept touch listener and call requestDisallowInterceptTouchEvent(true) on view pager.
You can use this wrapper from google samples
/**
* Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem
* where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as
* ViewPager2. The scrollable element needs to be the immediate and only child of this host layout.
*
* This solution has limitations when using multiple levels of nested scrollable elements
* (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).
*/
class NestedScrollableHost : FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
private var touchSlop = 0
private var initialX = 0f
private var initialY = 0f
private val parentViewPager: ViewPager2?
get() {
var v: View? = parent as? View
while (v != null && v !is ViewPager2) {
v = v.parent as? View
}
return v as? ViewPager2
}
private val child: View? get() = if (childCount > 0) getChildAt(0) else null
init {
touchSlop = ViewConfiguration.get(context).scaledTouchSlop
}
private fun canChildScroll(orientation: Int, delta: Float): Boolean {
val direction = -delta.sign.toInt()
return when (orientation) {
0 -> child?.canScrollHorizontally(direction) ?: false
1 -> child?.canScrollVertically(direction) ?: false
else -> throw IllegalArgumentException()
}
}
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
handleInterceptTouchEvent(e)
return super.onInterceptTouchEvent(e)
}
private fun handleInterceptTouchEvent(e: MotionEvent) {
val orientation = parentViewPager?.orientation ?: return
// Early return if child can't scroll in same direction as parent
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
return
}
if (e.action == MotionEvent.ACTION_DOWN) {
initialX = e.x
initialY = e.y
parent.requestDisallowInterceptTouchEvent(true)
} else if (e.action == MotionEvent.ACTION_MOVE) {
val dx = e.x - initialX
val dy = e.y - initialY
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
// assuming ViewPager2 touch-slop is 2x touch-slop of child
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
if (scaledDx > touchSlop || scaledDy > touchSlop) {
if (isVpHorizontal == (scaledDy > scaledDx)) {
// Gesture is perpendicular, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
} else {
// Gesture is parallel, query child if movement in that direction is possible
if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
// Child can scroll, disallow all parents to intercept
parent.requestDisallowInterceptTouchEvent(true)
} else {
// Child cannot scroll, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
}
}
}
}
}
}
Then wrap your view pager contents -recyclerView- ,
<androidx.viewpager2.integration.testapp.NestedScrollableHost
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/first_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF" />
</androidx.viewpager2.integration.testapp.NestedScrollableHost>
ViewPager2 scrolling Disable
viewPager2 user input scrolling disable.
import androidx.viewpager2.widget.ViewPager2;
private ViewPager2 viewPager;
viewPager = findViewById(R.id.view_pager);
viewPager.setUserInputEnabled(false);
What I am trying to do is the following:
I have a fragment that has scrollview with a video on top. What I am a trying to achieve is having the video to float when I scroll.
Similar behavior to this link: https://www.independentarabia.com/jsonfeed/api/v2/node/34291
I looked into picture in picture mode but with no luck
Can anyone give me an idea of how this behavior can be achieved?
In case someone was looking to do something like this:
I referred to the jw player library:
scroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, _, scrollY, _, oldScrollY ->
x = 0
if (scrollY > 200) {
if (!mPlayerContainer.isMovable) {
movable = false
x = 2
} else {
x = 0
val momentView = v.getChildAt(v.childCount - 1)
val diff = (momentView.bottom - (scroll.height + scroll
.scrollY))
if (diff < 50) {
val layoutParamsNew =
RelativeLayout.LayoutParams(mPlayerContainer.width, mPlayerContainer.height)
layoutParamsNew.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
layoutParamsNew.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
val displayMetrics = resources.displayMetrics
layoutParamsNew.setMargins(
0,
0,
(displayMetrics.density * 16).roundToInt(),
(displayMetrics.density * 16).roundToInt()
)
mPlayerContainer.layoutParams = layoutParamsNew
} else {
val layoutParamsNew =
RelativeLayout.LayoutParams(mPlayerContainer.width, mPlayerContainer.height)
layoutParamsNew.addRule(RelativeLayout.CENTER_VERTICAL)
layoutParamsNew.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
val displayMetrics = resources.displayMetrics
layoutParamsNew.setMargins(
0,
0,
(displayMetrics.density * 16).roundToInt(),
(displayMetrics.density * 16).roundToInt()
)
mPlayerContainer.layoutParams = layoutParamsNew
}
}
} else if (scrollY < 200 && mPlayerContainer.isMovable) {
movable = true
x = 1
}
if (x != 0) {
toggleMovablePlayer()
}
})
private fun toggleMovablePlayer() {
if (!movable) {
// Set the player container to movable, in order to intercept touch events.
mPlayerContainer.isMovable = true
// Disable fullscreen rotation handling on the JW Player.
mPlayerView!!.setFullscreen(mPlayerView!!.fullscreen, false)
// Disable controls.
mPlayerView!!.controls = false
if (mPlayerState != PlayerState.PLAYING && mPlayerState != PlayerState.BUFFERING) {
// Start playback in case the user hasn't done this yet, since we don't want to have
// a movable player that does not play anything...
mPlayerView!!.play()
}
// Scale the player.
mInitialLayoutParams = mPlayerContainer.layoutParams
val newWidth = (mPlayerContainer.width / SCALING_FACTOR)
val newHeight = (mPlayerContainer.height / SCALING_FACTOR)
val layoutParams = RelativeLayout.LayoutParams(newWidth.toInt(), newHeight.toInt())
// Position the player in the right bottom corner.
mPlayerContainer.layoutParams = getInitialMovablePlayerLayoutParams(layoutParams)
// Set an onTouchListener on the player which handles MotionEvents.
mPlayerContainer.setOnTouchListener(View.OnTouchListener { v, event ->
if (v.id == R.id.player_container) {
val layoutParams = v.layoutParams as RelativeLayout.LayoutParams
when (event.action) {
MotionEvent.ACTION_DOWN ->
// Notify the MovablePlayerLayout that we started consuming
// events in order to receive ACTION_MOVE events.
return#OnTouchListener true
MotionEvent.ACTION_MOVE -> {
var topMargin = event.rawY.toInt() - v.height
var leftMargin = event.rawX.toInt() - v.width / 2
// Make sure that the view can not go "out of bounds"
if (topMargin < 0) {
// Out of bounds: TOP
topMargin = 0
}
if (topMargin > mContentContainer.height - mPlayerContainer.height) {
// Out of bounds: BOTTOM
topMargin = mContentContainer.height - mPlayerContainer.height
}
if (leftMargin < 0) {
// Out of bounds: LEFT
leftMargin = 0
}
if (leftMargin > mContentContainer.width - mPlayerContainer.width) {
// Out of bounds: RIGHT
leftMargin = mContentContainer.width - mPlayerContainer.width
}
layoutParams.topMargin = topMargin
layoutParams.leftMargin = leftMargin
// Make sure the align rules have been removed.
layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
layoutParams.removeRule(RelativeLayout.CENTER_VERTICAL)
layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT)
layoutParams.rightMargin = 0
layoutParams.bottomMargin = 0
// Set the new layout parameters
v.layoutParams = layoutParams
return#OnTouchListener true
}
}
}
false
})
} else {
// Disable the movable property of the MovableViewLayout.
mPlayerContainer.isMovable = false
// Restore the initial layout parameters.
mPlayerContainer.layoutParams = mInitialLayoutParams
// Remove the onTouchListener.
mPlayerContainer.setOnTouchListener(null)
// Re-enable the controls.
mPlayerView!!.controls = true
// Re-enable fullscreen rotation handling, and go to fullscreen if we're in landscape mode.
mPlayerView!!.setFullscreen(
resources.configuration.orientation === Configuration.ORIENTATION_LANDSCAPE,
true
)
}
}
private fun setInitialLayoutParams() {
val displayMetrics = resources.displayMetrics
if (resources.configuration.orientation === Configuration.ORIENTATION_PORTRAIT) {
/*mPlayerContainer.layoutParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, displayMetrics.widthPixels / 16 * 9
) // 16:9*/
mPlayerContainer.layoutParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
200f,
resources.displayMetrics
).toInt()
)
} else {
// We need to use height to calculate a 16:9 ratio since we're in landscape mode.
mPlayerContainer.layoutParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, displayMetrics.heightPixels / 16 * 9
) // 16:9
// Toggle fullscreen, since we're in landscape mode.
mPlayerView!!.setFullscreen(true, true)
}
}
/**
* Positions the movable player to the right bottom corner.
*
* #param layoutParams
* #return
*/
private fun getInitialMovablePlayerLayoutParams(layoutParams: RelativeLayout.LayoutParams): RelativeLayout.LayoutParams {
layoutParams.addRule(RelativeLayout.CENTER_VERTICAL)
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
val displayMetrics = resources.displayMetrics
layoutParams.setMargins(0, 0, Math.round(displayMetrics.density * 16), Math.round(displayMetrics.density * 16))
return layoutParams
}