I am trying to understand how some view works. I start reading the source code of CollapsingToolbarLayout. But i am confused, there is not onTouch function in it whereas we need to touch our screen to expand/collapse the view.
So how CollapsingToolbarLayout can be collapsed/expanded with our finger but there's no onTouch method overrided?
Can somebody explain to me?
This is because onTouch isn't the only way to interact with Views.
For CollapsingToolbarLayout, you first need to understand that it is designed to be used as a child of AppBarLayout.
AppBarLayout is pretty much a vertical LinearLayout that's used to implement lots of features that belong to Material Design, including the scrolling features. In other words, this layout is what handles the scrolling for the child View.
In the source of CollapsingToolbarLayout, look at the onAttachedToWindow() method. Inside this method, you'll see that if the parent of this toolbar is an AppBarLayout, then it'll set a custom-defined OffsetUpdateListener into it's parent's addOnOffsetChangedListener() method. Look further down the source to see the definition of it's OffsetUpdateListener.
What this does is CollapsingToolbarLayout is telling it's parent (AppBarLayout) to tell itself if there are any changes to the 'offset' which is the scrolling.
So there's no need for CollapsingToolbarLayout to have an 'OnTouch' override, because it doesn't handle the touches or scrolling. It simply allows it's parent to handle the scrolling while it just tells the parent to let it know when it should react, in other words... when it should collapse or expand.
they use OnOffsetChangedListener which is an interface definition for a callback to be invoked when an AppBarLayout's vertical offset changes.
onOffsetChanged Called when the AppBarLayout's layout offset has been changed. This allows child views to implement custom behavior based on the offset (for instance pinning a view at a certain y value).
here is the reference OnOffsetChangedListener
Basically, CollapsingToolbarLayout is not a View but ViewGroup. It inherits from FrameLayout and acts as container for AppBarLayout when some of view needed to be collapsed/expanded based on app bar's scrolling behavior.
So, it's a wrapper for Toolbar which implements a collapsing app bar. It is designed to be used as a direct child of a AppBarLayout.
Now for your question :
"So how CollapsingToolbarLayout can be collapsed/expanded with our finger but there's no onTouch method overrided?"
Answer is simple, it doesn't. It's the AppBarLayout itself which intercepts touch event based on OffsetChange listener and passes callbacks to it's descendants. CollapsingToolbarLayout has ability to animate toolbar title (collapse/expand) and some other stuffs like scriming background, pinning title etc. so, basically it responds to AppBarLayout's OffsetChange listener when callbacks received.
Check out official reference for more details.
Related
My client requested a collapsing view (triggered by a recycler view) that doesn't graphically belong to AppBar / Toolbar abstraction. While I was able to fake it somehow with the mentioned view sitting really inside CollapsingToolbarLayout, I really feel the code is clumsy and will be a nightmare to maintain.
The name CoordinatorLayout suggests that maybe the collapsing / parallax behaviour could be used anywhere in view hierarchy, but I couldn't find neither example nor any proof of in Android docs. All examples show collapsing views only inside AppBars!
So - is it or is it not possible to collapse any view anywhere with events from some RecyclerView?
Since it was requested - a schematic view of the layout. But the question is really more general. As stated above - I've implemented it putting the collapsable square inside AppBar and setting background to white. It works as required, but looks hacky...
I really feel the code is clumsy and will be a nightmare to maintain.
I don't think so.
Also CollapsingToolbarLayout is optional (Remember, you can use minHeight if you want).
But you have to keep AppBarLayout to get things worked. (Don't forget to set app:elevation="0dp" to the AppBarLayout)
Is it or is it not possible to collapse any view anywhere with events
from some RecyclerView?
Yes, it is possible. By attaching an OnScrollListener to the RecyclerView and manually collapsing it. But I think the AppBarLayout method will be enough for this.
I have nested ViewPagers and RecyclerViews as depicted in the image:
Requirements:
The first level ViewPager1 swipes from left to right. It uses FragmentStatePagerAdapter.
The first level RecyclerView1 scrolls vertically.
The second level ViewPager2 does not swipe - the swipe motion is controlled by a TabLayout. It uses a custom PagerAdapter that returns a View as a page.
The second level RecyclerView does not scroll - it simply wraps a list of dynamic items
What I have working so far:
The first level ViewPager1 and RecyclerView1 works as intended.
The ViewPager2 does not show because its height is defined as "wrap_content"
The ViewPager2/RecyclerView2 prevents RecyclerView1 to scroll up/down.
What I have tried:
Setting RecyclerView1.setNestedScrollingEnabled(false) stops it from passing the onTouch event to its children, but because the ViewPager2/RecyclerView2 wraps its content, it does not know what the size it needs to scroll.
Setting the ViewPager2 to a fixed height solves the scrolling problem. But because it is a fixed height, the content of RecyclerView2 is cut off.
Overriding OnMeasure as described here makes ViewPager2's content wrap, perfectly, but the scrolling no longer work again. I assume it is because OnMeasure is called "after" the View has already been attached?
So basically I need help on how to get the content to wrap but in such a way that RecyclerView 1 knows what the height is so that it can scroll.
EDIT
It turns out I was totally off base with point 3. The OnMeasure workaround DOES work as intended and the scrolling problem is NOT caused by recyclerView not knowing the height. It in fact does. The reason why it doesn't scroll is due to multiple nested scrollable view groups. I found this out by putting Log.i on onTouchEvent() and onInterceptTouchEvent() on all the scrollable view groups. Some surface of the views work, but if the surface has another scrollable child, it starts to cause problems.
Setting RecyclerView2.setNestedScrollingEnabled(false) fixed the vertical scrolling. However, now, the ViewPager2's touch behaviour is interfering with ViewPager1's
On closer inspection, ViewPager1 intercepts touch event when hitting non-scrollable surface, causing the ViewPager1 to call its onTouchEvent() to scroll left and right. However, if I start the touch event over a the ViewPager2's surface, ViewPager1 never intercept and it never handles the swipe left to right.
Unlike a RecyclerView, there is no simple method to disable nestedScrolling. So I tried disabling ViewPager2, but that didn't work and caused the inside views such as buttons not clickable.
I tried to return false in ViewPager2's OnTouchEvent so that it bubbles up the chain, but still, the ViewPager1's OnTouchEvent is never fired.
So I'm stuck again, how do I pass the touch event to the parent when the parent did not intercept the event when it should have. Again, I'm assuming, and again I might be off-base, that ViewPager1 might not intercept because ViewPager2 has requested a disallowInterceptTouchEvent() somewhere in its code? I just don't know where or how to begin to fix this problem.
In my application i have CoordinatorLayout with nested custom view, which has custom MoveUpwardBehavior. When Snackbar appears, I want this view to be pushed over it and it works.
The problem is, that CoordinatorLayout is nested in RelativeLayout which has flag animateLayoutChanges=true. When RelativeLayout animates its views, CoordinatorLayout shrinks a bit (vertically). It causes, that mentioned custom view also moves, but i want to make it stick to its position. Any thoughts, how I can accomplish it?
You need to set CoordinatorLayout to be top-level layout instead of wrapping it inside another parent. The animation and displacement of the views during layout animation is totally normal. You may want to tweak your MoveUpwardBehavior to get the target view to keep the intended behavior, in case that your custom view is not direct descendant of CoordinatorLayout.
I have a vertically scrollable list using a RecyclerView. The layout I'm trying to implement is that when you scroll down far enough and reach a specific item, if you keep scrolling past this item it will stick to the bottom of the screen while the rest of the list continues to scroll behind it. Currently it's implemented by having a scroll listener on the RecyclerView and manually adjusting the position of the sticky view as required, but this is hacky and hard to build on.
Is there an easier way to have this kind of layout? I am currently investigating using a CoordinatorLayout but I'm not sure if it's the right tool for the job.
You can accomplish this using a CoordinatorLayout with a custom behaviour. The behaviour should be applied to the sticky view and make it appear/disappear as the RecyclerView scrolls. You have to override onStartNestedScroll in your behaviour to return true to receive calls for scroll changes.
I want to implement a layout similar to this.
The catch is that the content needs to be a view pager (that contains scrollable content).
My current plan is to implement this with one of the following
Make the main view a linear layout with the top section holding the header taking up fixed space (the viewpager fills whatever is left). Listen for touch events on the linear layout to expand or shrink the header, once the header has reached the minimum size, propagate the scroll events through to the viewpager (how?).
Same as 1., but wrapped in a ScrollView
Same as 1., but listen for scrolling in the viewpager and propagate that back up the view hierarchy (onScroll Listeners of some kind).
Same as 3, but wrapped in a ScrollView
I have a better idea on how to implement 3/4, but it seems like it introduces a lot of coupling, and "feels gross". I have less of an idea on how to do 1/2, but it feels (slightly) less gross. In either case, using the LinearLayout, I would have to override onTouch, rather than onScroll. Not sure which of those is preferable.
Ideally I would like to find a more elegant solution (possibly from Android L), but I welcome insights/pros/cons to my proposed solutions as well.