Efficient WebView resizing - android

I've implemented floating Toolbar in my app (hides with scroll down, shows on up) and now I see some flickering views inside WebView, to be precise these sticked to the bottom. I've noticed that this happens when I'm resizing WebView due to Toolbar offset change like this:
appBarLayout.addOnOffsetChangedListener(this);
#Override
public final void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
boolean scrollFlagsSet = ((AppBarLayout.LayoutParams) getToolbar().getLayoutParams()).
getScrollFlags() != 0;
int bottom = (!scrollFlagsSet ? 0 : getToolbarHeightAttr()) + verticalOffset;
swipeRefreshLayout.setPadding(0, 0, 0, bottom);
swipeRefreshLayout.requestLayout();
}
swipeRefreshLayout is WebViews parent. Method causes proper resizing WebView and it's pretty efficient, no lags (EDIT: a bit laggy on older devices). But when WebView loads site with some views sticked to the bottom then these views are flickering when resizing occurs. I can even see text in gap between bottom WebViews edge (gap present when scrolling down, view are cutted on the bottom when scrolling up). I've catched this behavior on screens (blue - Toolbar, gray - WebView, light blue & orange - in-webview views sticked to bottom). It looks like this only for a couple of millis (one frame?) and get back to the bottom fixed position. How to fix this?
Same page loaded in Chrome or Firefox behaves correctly, these views are sticked to bottom and have fixed position, doesn't flicker with scroll/toolbar offset change
I'm using this NestedWebView. My app uses a lot of fragments loaded into Activities, which all are extending abstract BaseActivity in which I have CoordinatorLayout. WebViews belong to fragments and are placed in different containers, there is no option to place WebView straight into CoordinatorLayout
edit: XMLs
activity:
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_main_cooridnator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<FrameLayout
android:id="#+id/activity_main_content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white" />
fragment:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/fragment_webview_swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<my.package.widgets.web.NestedWebView
android:id="#+id/fragment_webview_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay" />

Related

Prevent a child in ScrollView from being scrolled out of the screen

Somehow I came across Reddit app, and wondered how their Scrolling mechanism works. As you can see from the gif, there is a Menubar (Up/Downvote-comment-share bar) that always locates within the screen and can’t be scrolled out of the screen when scrolled up/down. When scrolling up, it will be located underneath the Toolbar (the grey bar at the top). When scrolling down, it will be located above the EditTextView (the Add-a-comment bar at the bottom).
Relative layout
|-->Toolbar (android:id="#+id/toolbar")
|-->ScrollView (android:layout_below="#id/toolbar")
|-->Child (This child is located underneath the Toolbar when scrolling up)
|-->Child
|-->Child
If I wanted to write this page, what dependencies, widgets or concepts
would I need to use or look into?
Note: You can give me snippets of codes if you prefer :)
you can use the CoordinatorLayout layout to realize that function. the layout like that. for details, you can see this blog in the medium.
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/behavior_demo_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
android:background="?attr/colorPrimary" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/behavior_demo_swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="#+id/behavior_demo_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>
The toolbar (containing the menu) is outside of the ScrollView in the XML.
Or it may not even be a ScrollView at all.
It may just be view recycling- but even so the toolbar is outside of that XML layout used for displaying the content.
For hiding the TopBar and bottomBar (comment box) I recommend you use Animation
Simply call animation like
//on scrollUp
topLayout.animate().translationY(-300).alpha(0.0f).setDuration(300);
// this will make the layout go up and fade to invisible
//on scrollDown
topLayout.animate().translationY(0).alpha(1.0f).setDuration(300);
// this will make the layout come down and fade to be visible
Implement this will both Views using addOnScrollListener() or I suggest you to have a look at this answer for implementing scroll Up/Down using CoordinateLayout
Hope this will help!

Changing toolbar background alpha value when scrolling with CoordinatorLayout

I am currently trying to use the CoordinatorLayout class to adapt a Toolbar opacity depending on a ScrollView scroll position. I succeed doing this "manually linking" the two views, listening to the ScrollView scroll events and reporting them on the alpha value of my Toolbar background.
The wanted behavior is :
The toolbar is starting in a transparent state (both text and background) and end up totally white background and black text when one view is totally scrolled.
The behavior is roughly this one : Youtube video.
Current implementation
layout file (simplified)
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<my.package.widgets.DetailsTop
android:id="#+id/layout_details_top"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<my.package.widgets.DetailsBottom
android:id="#+id/layout_details_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00FFFFFF"/>
</RelativeLayout>
Fragment implementation
public class SearchVehicleDetailsFragment extends BaseFragment<SearchContract.ParentView> {
#Bind(R.id.layout_details_top) DetailsTop detailsTopLayout;
#Bind(R.id.layout_details_bottom) DetailsBottom detailsBottomLayout;
#Bind(R.id.scrollView) ScrollView scrollView;
#Bind(R.id.toolbar) Toolbar toolbar;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
float alpha = (float) scrollView.getScrollY() / vehicleDetailsTopLayout.getHeight();
toolbar.setBackgroundColor(getColorWithAlpha(alpha, getResources().getColor(R.color.white)));
toolbar.setTitleTextColor(getColorWithAlpha(alpha, getResources().getColor(R.color.dark_grey)));
}
});
}
public static int getColorWithAlpha(float alpha, int baseColor) {
int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24;
int rgb = 0x00ffffff & baseColor;
return a + rgb;
}
}
Problem
However, even if the previous snippets are a particularly simple solution that seems to work perfectly, I am just a little confused about why it didn't work with CoordinatorLayout. Also, I found a guy who seems to have succeed on this task with a custom CoordinatorLayout.Behavior<android.support.v7.widget.Toolbar> implementation.
You can take a look at his custom CoordinatorLayout.Behavior implementation. He also gives some details on Medium. However, due to a lot of just <android.support.v7.widget.Toolbar .../> instead of the full Toolbar tag and a strong lack of understanding of how this part of the Support Design library works, I was never able to implement it...
Question
I would like to understand how CoordinatorLayout, and all the classes around it, works together. So I will probably be able to implement the behavior I am looking for, which is : linking a ScrollView scroll position to the alpha value of a toolbar background color.
Obviously, if you also know how to implement all this, I will be really happy to see your snippet :)
I've been experimenting with behaviors, I am happy you found my article partially helpful.
I redacted the code of the Toolbar chunk because it wasn't important. Simple Toolbar attributes, nothing special about it:
<android.support.v7.widget.Toolbar
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
android:id="#+id/toolbar"
app:layout_behavior=".view.ToolbarBackgroundAlphaBehavior"/>
I would like to understand how CoordinatorLayout, and all the classes
around it, works together. So I will probably be able to implement the
behavior I am looking for, which is : linking a ScrollView scroll
position to the alpha value of a toolbar background color.
In your layout you are missing the CoordinatorLayout as being top parent that wraps everything. Instead of using ScrollView you should use NestedScrollView it works just like ScrollView but the difference is that NestedScrollView can perform nested scrolls in older version, unlike ScrollView which nested scrolls only after API 23.
For a CoordinatorLayout.Behavior to work the target view must be direct descendant of the CoordinatorLayout. As you can see in the article, the Toolbar is direct descendant of the coordinator. Otherwise, the behavior will not work.
In the example NestedScrollView has appbar layout behavior, so that AppBarLayout can expand and collapse, you can find plenty of examples about this.

How to let only specific fragments scroll inside a ViewPager of CoordinatorLayout?

Background
I have a viewPager, with 3 fragments and tabs for them. Each fragment has an intro phase (of its own) that doesn't have any scrollable content.
After leaving the intro phase, there is a recyclerView that the user can scroll in.
The problem
I need to use the new design library, so that when scrolling (only via recyclerView), it will hide the actionBar and let the tabs still be shown.
When the user goes to a fragment that doesn't have a scrollable content yet, the actionBar should re-appear, similar to what "Google Play Newsstand" has. In fact, I would even be happy to have what they have: as soon as you start swiping left/right, re-show the action bar.
Thing is, if I follow the guidelines and samples, I have 2 issues:
The non-scrollable phase for fragments gets truncated at the bottom, as if it can get scrolled.
I can't find how to re-show the actionBar, and make it stuck there till I switch to a scrollable content (either by switching to another fragment, or when the content of the current fragment changes to a scrollable content).
What I've tried
Here's a short snippet of the current layout XML file of the activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:id="#+id/activity_main__coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/activity_main__appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layoutDirection="ltr"
android:theme="?attr/actionBarTheme"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute"/>
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:tabGravity="fill"
app:tabIndicatorColor="#FFffffff"
app:tabIndicatorHeight="3dp"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<include layout="#layout/fabs"/>
</android.support.design.widget.CoordinatorLayout>
<include
layout="#layout/sliding_menu"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>
The fragments have a layout of a ViewAnimator that just switches between phases, while one of them is the non-scrollable content, and the other is the RecyclerView.
I've tried to add a NestedScrollView/ScrollView the non-scrollable content , and force it to fill itself, using android:fillViewport="true" , but it didn't work. For ScrollView it didn't even allow to scroll.
EDIT: Another thing I've tried is to use addOnPageChangeListener on the viewPager, so that in onPageSelected I could set the flags for the toolbar :
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
params.setScrollFlags(!needScrolling? 0 : LayoutParams.SCROLL_FLAG_SNAP | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | LayoutParams.SCROLL_FLAG_SCROLL);
It works, but it has a issues too:
while scrolling horizontally, I can see the content of the non-scrollable fragment being truncated, and when going to the new fragment (stop touching the screen, to let it snap to the fragment), only then it shrinks its size to fit the correct space.
The toolbar doesn't get re-shown.
If the toolbar is hidden due to scrolling on another fragment, and I'm now on the non-scrollable fragment, it actually gets less space to fill than it's supposed to, so it has empty space at the bottom.
EDIT: one solution is to add an empty view of the same height of actionbar (layout_height="?actionBarSize") at the bottom of the non-scrollable fragments's content. However, when the action bar is hidden, I can see the view, so there is empty space. I still need to know how to re-show the actionbar on this case.
The question
How do I set a different behavior for the toolbar, so that it will re-show and stuck on certain states, yet be scrollable only when there is a RecyclerView shown on the current fragment?

NestedScrollView (NSV) in CoordinatorLayout (CL): NSV Not at Top When Loaded

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!

How does the Android L contacts app collapse its toolbar?

I've been trying to reproduce the way that the Contacts app on version 5.0 collapses the toolbar when the listview is scrolled.
Gallery of screenshots demonstrating the desired interaction
Note the collapse of the toolbar in stages, where it displays search+last contact, fades last contact, collapses last contact, collapses search, leaving only the tabs.
So far, I have a toolbar sitting above a recyclerview in a LinearLayout, and the toolbar is used as an actionbar, not standalone.
I can't figure out how to intercept the touch event on the recyclerview and make it shrink the toolbar, and then return the scroll event to the recyclerview. I tried putting the entire thing in a scrollview, but then the recyclerview couldn't calculate it's height properly and displayed no content. I tried overriding onscroll on the recyclerview, and found that it will only notify me when a scroll event started, and provide me with the first visible card id.
The way that looks right, but I can't get working for the life of me, is this:
getSupportActionBar().setHideOnContentScrollEnabled(true);
Which returns:
Caused by: java.lang.UnsupportedOperationException: Hide on content scroll is not supported in this action bar configuration.
Using a traditional actionbar, putting a toolbar below it, and setting hideoncontentscrollenabled also didn't work, scrolling never triggered the hide method on the actionbar.
-- edit --
I was able to get hideOnContentScrollEnabled working on a listview with a traditional actionbar, but the behavior is not the same as the contacts app. This is clearly not the method they used-- it simply triggers .hide() on the actionbar when a fling event occurs on a listview, which is notably different from the contacts app, which drags the toolbar along with the scroll event.
-- /edit --
So I abandoned that route, and put fill_parent on the cardview height, and animated a collapse on the toolbar. But how do I trigger it so that it follows the touch event and then returns the touch event to the recyclerview?
activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/actionBarSize"
android:background="#color/colorPrimary"
/>
<fragment android:name="me.myapplication.FragmentTab"
android:id="#+id/tab_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="8dp"
android:background="#eeeeee"
>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
styles.xml
...
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
MainActivity.java
Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
// Disable the logo in the actionbar, as per material guidelines
toolbar.getMenu().clear();
toolbar.setTitle("My toolbar");
setSupportActionBar(toolbar);
I haven't investigated the source code yet but this guy seems to have made life easy yet enlightening.
https://github.com/ksoichiro/Android-ObservableScrollView
EDIT
Google has just released Android Design Library. Please take a look as it contains all the effects of collapsing toolbars and much more.
Well, I have no idea how they do it, but... why don't you take a peek to the source code? Luckily for us, the Contacts app is still open-source on Android L (others weren't as lucky as Contacts, like Mail, which does not work anymore on L; or Keyboard, which they stopped updating anymore since the launch of their propietary Google Keyboard).
Anyway, here's the source code I think you should look at:
https://github.com/android/platform_packages_apps_contacts/blob/master/src%2Fcom%2Fandroid%2Fcontacts%2Factivities%2FActionBarAdapter.java
Note the method update(boolean skipAnimation) in Line 311, which calls animateTabHeightChange(int start, int end) (Line 437).
My guess is all the magic happens there ;-)
As of June 2015, your desired effect can be accomplished via the so called CollapsingToolbarLayout of the new design support library.
Based on the sample code here, I am figuring that:
the search cardview is child of the toolbar
the missed call cardview belongs to the collapsingtoolbar with the collapseMode attribute set to pin
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="112dp"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="?attr/actionBarSize"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed|enterAlways">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:fitsSystemWindows="false"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways">
<!-- Search layout -->
<android.support.v7.widget.CardView
</android.support.v7.widget.CardView>
</android.support.v7.widget.Toolbar>
<!-- Last call card view-->
<android.support.v7.widget.CardView
app:layout_collapseMode="pin">
</android.support.v7.widget.CardView>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#color/primary_color"
app:layout_scrollFlags="scroll"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
No third party library is required now! Android is officially providing library. You can collapse the toolbar and do many other tweaks.
Check this android-developer's blog
And don't forget to add this dependency in your build.gradle file.
compile 'com.android.support:design:22.2.0'
I found this library that seems to do what you're looking for: https://github.com/kmshack/Android-ParallaxHeaderViewPager and this https://github.com/flavienlaurent/NotBoringActionBar
You can play the video to see the behavior: https://www.youtube.com/watch?v=sCP-b0a1x5Y
It might not be the 'new' standard way of doing it with ToolBar, but it might give you an idea by inspecting the code. It seems to attach a OnScrollListener to the scrolling content and then trigger changes on the size of the bar.
For me https://mzgreen.github.io/2015/06/23/How-to-hideshow-Toolbar-when-list-is-scrolling%28part3%29/ has helped. A source code is found here: https://github.com/mzgreen/HideOnScrollExample/tree/master/app/src/main.
A RecycleView in your layout should look like:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
Note that after starting an application 2 toolbars appear (actionbar and toolbar). So in your activity.java you should write so:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide ActionBar.
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR);
getSupportActionBar().hide();
setContentView(R.layout.your_activity_layout);
...
The toolbar is customized as shown here: https://stackoverflow.com/a/26548766/2914140. I mean, it appears without title and any other elements, so you can add them in a layout.
Android's Contact app doesn't have an easy plug-and-play solution that you can grab for use in your own app.
It does a full implementation, essentially doing it the same way you would do it if you were implementing it from scratch. For context, before looking at the code, keep in mind how the views are laid out:
https://github.com/android/platform_packages_apps_contacts/blob/lollipop-release/res/layout/quickcontact_activity.xml
The MultiShrinkScroller is a FrameLayout which intermediates the scrolling behavior, but the main stuff is in a LinearLayout, so reducing the height of the higher views will "scroll" the lower views upwards.
The key file for the implementation is this one:
https://github.com/android/platform_packages_apps_contacts/blob/lollipop-release/src/com/android/contacts/widget/MultiShrinkScroller.java
public void scrollTo(int x, int y) {
final int delta = y - getScroll();
boolean wasFullscreen = getScrollNeededToBeFullScreen() <= 0;
if (delta > 0) {
scrollUp(delta);
} else {
scrollDown(delta);
}
updatePhotoTintAndDropShadow();
updateHeaderTextSizeAndMargin();
//... other stuff
}
private void scrollUp(int delta) {
// Collapse higher views first
if (getTransparentViewHeight() != 0) {
final int originalValue = getTransparentViewHeight();
setTransparentViewHeight(getTransparentViewHeight() - delta);
setTransparentViewHeight(Math.max(0, getTransparentViewHeight()));
delta -= originalValue - getTransparentViewHeight();
}
// Shrink toolbar as needed
final ViewGroup.LayoutParams toolbarLayoutParams
= mToolbar.getLayoutParams();
if (toolbarLayoutParams.height > getFullyCompressedHeaderHeight()) {
final int originalValue = toolbarLayoutParams.height;
toolbarLayoutParams.height -= delta;
toolbarLayoutParams.height = Math.max(toolbarLayoutParams.height,
getFullyCompressedHeaderHeight());
mToolbar.setLayoutParams(toolbarLayoutParams);
delta -= originalValue - toolbarLayoutParams.height;
}
// Finally, scroll content if nothing left to shrink
mScrollView.scrollBy(0, delta);
}
updatePhotoTintAndDropShadow(); and updateHeaderTextSizeAndMargin(); handle the change in tint and text as it gets collapsed so that it turns into the look and feel of a regular ActionBar/ToolBar.
You could grab the MultiShrinkScroller file itself and adapt it for your own use, but there are probably easier implementations nowadays (including those from Android's design library).

Categories

Resources