I have a strange issue with NestedScrollView fling on Nexus 5x (7.1.2) and Google Pixel (7.1.1). On other OS versions it works OK.
Fling animation sometimes stops right after lifting up a finger. It stucks and the next few flings may be stopping as well.
In order to reproduce it, you need to fling several times up and down.
In logs these flings look pretty much the same in terms of velocity, direction, etc, so I can't find a real cause of this bug.
Also NestedScrollView doesn't necessarily need to be inside of CoordinatorLayout, it also can have no NestedScrollingChild at all.
For example, this bug is reproducible with one of the following NestedScrollView children:
1) LinearLayout populated with TextViews
2) WebView
3) LinearLayout populated with RecyclerViews.
I know about possible issues with RecyclerView and Behaviours inside of CoordinatorLayout, but it's not related.
So please don't mention any
recyclerView.getLayoutManager().setAutoMeasureEnabled(true);
recyclerView.setNestedScrollingEnabled(false);
or things like that.
Code sample:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="Put a super long text here"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="Put a super long text here"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
So it's clearly a bug in NestedScrollView.
I have made a workaround for this, but still waiting for a proper fix from Google side.
https://github.com/Dimezis/FlingableNestedScrollView/
Edit:
Looks like the issue is fixed in support lib 26.0.0-beta2
https://chris.banes.me/2017/06/09/carry-on-scrolling/
Edit 2:
Although the scrolling works fine now, in my app I can constantly reproduce this bug:
https://issuetracker.google.com/issues/37051723
If someone encounters it as well, you can find a workaround in a thread mentioned.
according to Animating a Scroll Gesture training guide, while overriding computeScroll(), after using mScroller.computeScrollOffset() to calculate proper offset to scroll view, we need use:
ViewCompat.postInvalidateOnAnimation(this);
to animate next scroll.
However, in the NestedScrollView, the computeScroll() looks like this:
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
...
}
}
It doesn't request next scroll animation! Which means that after using mScroller.fling(...), the computeScroll() method will sometimes only get called one time, and view doesn't keep fling.
To fix this problem, I have tried to replace the computeScroll in this way:
public void computeScroll(){
if(mScroller.computeScrollOffset()){
...
ViewCompat.postInvalidateOnAnimation(this);
}
}
It may not sound a good solution, but it just works fine for now.
Recent version of NestedScrollView has added the ViewCompat.postInvalidateOnAnimation(this).
Related
I am currently checking my app for any issues with the new Android 12 overscroll animation. And I came across plenty screens which contain a RecyclerView inside a NestedScrollView. Usually like this:
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout_root"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView [...] />
<TextView [...] />
<TextView [...] />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView_attachment_classifications"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="?marginM"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/textView_categorize_title"
tools:itemCount="4"
tools:listitem="#layout/list_item_adm_attachment_classification" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
This causes some problems with the new overscroll animation. Unfortunately, I cannot show you a recording of the animation problem, but let me describe it: When the screen is at the topmost position and the user is scrolling upwards, all views should stretch a little in relation to the drag event. But it does not do that. It only shows this stretch animation for a very short period of time AFTER the user released their thumb from the screen.
What I have found out so far:
Setting the RecyclerViews isScrollContainer to any value has no impact
Setting the RecyclerViews isNestedScrollingEnabled to any value has no impact
Setting the RecyclerViews overScrollMode to any value has no impact
The same goes for the NestedScrollView
Ironically, replacing the NestedScrollView with a standard ScrollView solves my issue.
I was unable to replicate the problem in a sample app, so it is relatively safe to say that this issue is somewhere in my apps config and architecture. But since using a ScrollView solves my issue, I wanted to know if a NestedScrollView still has any usefulness on API 26 and above or if NestedScrollView is just for backwards compatibility for apps which support older Android versions as well?
Turns out the issues were produced by NestedScrollView and fixed by Google in core-ktx:1.7.0. I had it on version 1.6.0.
i have this problem in my android mobile app, i am using kotlin.
In the main of the project I have a list of cards, made through RecyclerView, when the cards are of a greater number than the screen can accommodate, the vertical scrolling is rightly enabled. As soon as I scroll the list vertically, the action is completed, but upon release I can no longer click anywhere within my app. (clicks work outside the app)
This is the HTML of that piece of code:
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/itemsswipetorefreshSite"
android:layout_width="wrap_content"
android:layout_height="0px"
android:layout_weight="1">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewSite"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="#dimen/twenty">
</androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
This piece of code is contained in a CoordinatorLayout inside which there is a ConstraintLayout which in turn has a LinearLayout that encloses the list.
The same problem does not occur in other parts of the app, or in other components / fragments I have other lists, made in the exact same way, but under this situation they do not produce this bug.
The log that is produced at the time of the bug (from the beginning of the scrolling) is the following:
I/HwViewRootImpl: removeInvalidNode all the node in jank list is out of time
W/HiTouch_PressGestureDetector: Touch pointer move a lot. The moving distance of X is:7.0, limit is:60The moving distance of Y is:61.94568, limit is:60
D/OverScrollerOptimization: fling time is flingTime = 1.9100340578075632 velocity = 1261
D/OverScrollerOptimization: fling time is flingTime = 1.9100340578075632 velocity = 1261
but the same is produced, very similar in the other lists for the same action.
Your every help is truly precious, thank you!
set layout_height to wrap_content in both swipeRefreshlayout and also in your recyclerView
[SOLVED]
Maybe it can help someone who is in the same situation as me: I found that in the adapter file of the list in question I found a
requireActivity (). window.setFlags (WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
that it didn't have to be there and every time I touched the list it was activated. Typical problem of a hasty copy and paste.
In my Android app running on Android 5.1.1 I have a layout using a Toolbar with a TabLayout, and underneath is a ViewPager. All of these are put together in a CoordinatorLayout.
In the first page of the ViewPager is a RecyclerView serving CardView items.
My problem is that my ViewPager keeps getting resized in a way so that my CardView list items are cropped.
My main layout looks basically like this:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent" >
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.design.widget.CoordinatorLayout>
And the first fragment served by my ViewPager looks like:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v7.widget.RecyclerView
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"/>
</FrameLayout>
This renders something that looks like this:
When clicking a button in my layout, I use startActivityForResult to invoke another activity, and when returning to my activity sometimes suddenly my list is cropped so that only half of the first item is visible:
After swiping horizontally to another pager in the ViewPager and then back, the problem disappears, so it does seem a re-layout has not been properly triggered. Pressing HOME and then resuming my activity does NOT resolve the problem though. Note that this happens even if I am not modifying my layout in any way, I am simply returning from a startActivityForResult call. And yes, it only happens sometimes... And I have no background threads running that could explain the apparent random behavior.
At first I thought it was the RecyclerView that had shrunk, but using HierarchyViewer I was able to find that it was actually the entire ViewPager that had shrunk to about half its original height.
I tried various hacks to get around this, including calling invalidate() and requestLayout() on my entire view hiearchy, but nothing seemed to help (although swiping to another page and back again fixes it). Also, those are not the kind of solutions I want to resort to... Then I tried changing my ViewPager height to wrap_content, which did in fact solve this particular problem; after returning to my activity the first item in my RecyclerView is never cropped, and I can scroll down to the other items. However, now instead the very last item of my list is always cropped, as can be seen in this screenshot where the list is scrolled all the way to the bottom:
Since I am now at a point where I don't really understand what's going on, I need some help. What should I really use as the layout_height for my ViewPager, and - above all - why? To me, match_parent makes sense, but how should I be thinking here? Is there a rational reason my views got cropped when using match_parent, or did I in fact encounter a bug in ViewPager, RecyclerView and/or CoordinatorLayout? How do I make sure that my ViewPager consistently fills the entire screen area below the AppBar, and that my RecyclerView can be scrolled vertically to properly render all CardView list items?
It turns out this is almost certainly a bug in CoordinatorLayout or even more likely in AppBarLayout$ScrollingViewBehavior. In an effort to create a MCVE I realized it was the fact that my sub-activity had an IME on screen that caused the shrinking of the ViewPager - when my activity is resumed after onActivityResult, the ViewPager is shrunk as a result of reduced screen real-estate from the IME, but is never expanded again despite the fact that the IME is no longer being shown and the fact that the CoordinatorLayout is indeed expanded.
After debugging and stepping through onLayout and onMeasure of CoordinatorLayout and ViewPager I am now fairly sure that the CoordinatorLayout does not properly propagate the change in size to its children.
I found that I can "fix" the problem by calling requestLayout on my ViewPager, but only if the call is sufficiently delayed (10 ms never works, 100 ms works most of the time):
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mViewPager.requestLayout();
}
}, 100);
}
This obviously isn't a robust solution, but after investigating some more it turns out I probably don't even need CoordinatorLayout since I don't really have any advanced scrolling behavior. So my solution will be to simply go with a LinearLayout or RelativeLayout as my root view group instead. Nice and simple, no need to complicate things.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent" >
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
I will however try to condense this into a simple example and file a bug with Google.
As to what I should use as the height for my ViewPager, my original use of match_parent still seems reasonable. The fact that wrap_content solved the problem (but caused other problems) is probably just due to inconsistencies caused by the bug.
I've experienced a similar issue when using an older version of the support library.
See these related issues:
https://code.google.com/p/android/issues/detail?id=176406
https://code.google.com/p/android/issues/detail?id=176187
Make sure you're using the latest Support library, version 23.1 as of this writing.
In your fragment just remove the frameLayout and make recyclerView parent...I hope it will work:
<android.support.v7.widget.RecyclerView
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp">
<android.support.v7.widget.RecyclerView/>
I am using an NSV in a CL for the ability to have the toolbar compress when the NSV scrolls down. The problem that I am having is that my NSV is not scrolled to the top when it loads, instead, it is offset from the top of the NSV by quite a margin (I am not certain where this spacing is coming from, it's not in the layout).
Please take a look at the screen captures, the first one shows how the NSV loads and you can clearly see the NSV has scrolled down quite a bit from the top by comparing the second (when I scroll the NSV to the top manually):
I did some updates to this layout and it caused this to occur, previously, it loaded at the top without issue. However, I did not add any spacing that should have caused this.
Here is the layout I'm using for this:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/cl_goal_detail"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<android.support.design.widget.AppBarLayout
android:id="#+id/abl_goal_detail"
android:layout_width="match_parent"
android:layout_height="144dp"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar_goal_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/content_space_double"
app:collapsedTitleTextAppearance="#style/title.dark"
app:expandedTitleTextAppearance="#style/display3.plus.dark"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar_goal_detail"
style="#style/toolbar"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="#+id/nsv_goal_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/content_space_half"
android:paddingLeft="#dimen/content_space_half"
android:paddingRight="#dimen/content_space_half"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<FrameLayout
android:id="#+id/container_goal_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"/>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
Any ideas would be appreciated!
OK! After a solid DAY of debugging every single component of my layout and Fragment I identified what I believe is a bug.
First, the issue: Turns out that having elements in your NSV's child view that change visibility to View.GONE upon runtime are causing the list to scroll down. I noticed that the list scrolls to just above the element where the visibility was toggled (including any margins set on the view).
Second, the fix: I fixed this issue by setting all the views to have android:visibility="gone" in the xml layout, then, I toggle each view's visibility as needed. Previously, the views were visible by default and then I worked from there. I just needed to change my logic to start with them all GONE, not terribly difficult.
I assume this works because the views you are going to hide at runtime do not form a part of the overall height calculation when the NSV is created in onCreateView(). Once the fragment progresses past onCreateView() it's safe to dynamically change the views, however, if the views are calculated as part as the height in onCreateView() and THEN hidden with View.GONE, measurements go wonky and you end up with a list scrolled down significantly.
Have you tried adding below line in your viewgroup i.e. FrameLayout in your case
android:descendantFocusability="blocksDescendants"
I think this will also work for you.
If not try it adding in NSV.
In my case, there was an EditText near the bottom of my scrolling content that was grabbing focus. Since NestedScrollView does some weird layout stuff, the focused view didn't scroll to the top when the activity started, so the real cause was not readily apparent. Adding this to the NestedScrollView's child layout fixed it for me:
android:focusableInTouchMode="true"
Your post answer helped me a lot to find out my issue (btw, it was the same). But I got it worked in a different way. I guess you are using a RecyclerView. In my case I'm using 3 RecyclerViews. Well, from your answer I started hiding the recyclers and I found out just one of them was causing this issue. What I did is I populated with a postDelayed:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
recyler.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyler.setAdapter(new MyAdapter(myList));
}
}, 3000);
That worked fine!
I'm making an app with lots of ImageViews and I needed to attach a touch listener in some of them. While doing this, I encountered a problem. If the pointer was held down at the location where the ImageView with touch listeners attached to it and was about to produce a scrolling event, there seems a fighting scene between the Views and the ScrollView in w/c where the event was actually occured and in w/c the event is supposed to belong. The screen scrolls in a fast rate then return where the first pointer was touched down, so it means it is an unwanted behavior.
Setting an onTouchListener in the ImageView makes the scrolling uneasy and unwanted, how do I prevent ImageView from receiving touch events when scrolling?
Some of the codes
Layout: act_map_view.xml
<?xml version="1.0" encoding="utf-8"?>
<com.nkraft.mobiletomblocator.CustomVScroll
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/vScroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none" >
<com.nkraft.mobiletomblocator.CustomHScroll
android:id="#+id/hScroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none" >
<com.nkraft.mobiletomblocator.CustomGridLayout
android:id="#+id/map_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</com.nkraft.mobiletomblocator.CustomGridLayout>
</com.nkraft.mobiletomblocator.CustomHScroll>
</com.nkraft.mobiletomblocator.CustomVScroll>
Notes:
The GridLayout is populated with ImageViews at runtime.
I customized my horizontal and vertical ScrollView so that I can scroll in both directions concurrently i.e diagonal scrolling
From the notes I've mention, I figured a clue from the problem I'm currently facing. I think this issue had been thought before the standard android's scrollbar was implemented. The main reason this occurs to me is that I extended the class of the scrollbars and totally replacing the default behavior of it. So for me to achieve what I want (the scrollbar with diagonal scrolling) I follow this post and replace both my vertical and horizontal scrollview with this TwoDScrollView, also thanks for this post directing me to it.