i need some help with my toolbar.
Right now i use a collapsing toolbar with image wich collapsed when i scroll up.
I know i can use contentScrim to make the Toolbar transparent and therefore see the image as "toolbar background".
However, i want the image to blur(/fade) when the toolbar is collapsed.
Any suggestions how to achieve this?
You can use this library. (RealTimeBlurView)
For the blur effect, just put the imageview behind the blurview.
To achieve what you want just change blurview's alpha when the app bar is scrolled.
appbar.addOnOffsetChangedListener(new OnOffsetChangedListener() {
#Override
public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) {
float offsetAlpha = (appBarLayout.getY() / appbar.getTotalScrollRange());
blurView.setAlpha( 1 - (offsetAlpha * -1));
}
});
UPDATE
FastBlur
Here's another benchmarking project to showcase all the possible blurring methods in android. Just get the fastest algorithm from the demo and use it in your project.
Hope this helps!
Related
Here I've got quite a complex animation that may be resolved (I believe) in a simple way using the CoordinatorLayout. It has 3 states:
Initial (left screen) - Header view is shown fully (orange
background): Toolbar, grey roundrect (it's actually a photo there)
plus some other views below (TextViews, RatingBar etc)
Scrolling the content up (middle
screen) - roundrect is zooming up with a changing green foreground alpha level over it, so it becomes green while scrolling (well, it is not obvious with these screens. Green background is actually a zoomed roundrect with a green foreground over it, and that is the cause the header background becomes green and not orange)
Scrolling once more (right screen) - the rest of the header should be scrolled up
Scrolling down the content should lead to the appearing of the views in a reverse way accordingly.
I had some experience working with the CoordinatorLayout, but I'm really not sure I understand how to handle 2 anchor points. I understand how the scroll flags work and that for zooming (p. 2) and for changing the foreground alpha I need a custom Behavior implementation, but for now I cannot understand how shall I handle all of this in a complex.
All I've found so far is Saúl Molinero's tutorial and also this tutorial with examples.
So please sorry for the poor description here, I'll update my question of course and will add the source code when I have some success with this issue, but for now I'd be glad to get some hints maybe or tutorials I've missed. Hope someone had something similar in the projects.
Here's my test repo with the code and here is a link to my layout.xml file.
You can get two snapping points with just setting the scroll flags as follows:
<android.support.design.widget.CollapsingToolbarLayout
...stuff...
app:layout_scrollFlags="scroll|enterAlways|snap">
So, fully expanded is one stopping point and with just the toolbar visible is the second stopping point. When the view is scrolled further, the toolbar disappears. So this is how you want things to work when scrolling up.
Now when the app bar is fully collapsed, the app bar will start showing immediately when scrolling down. That is not a surprise, since that is what enterAlways does. If the top of the content has been scrolled out of view, then you won't see it again until after the app bar is fully expanded. So, if this is the behavior you want, we'll just stop there.
However, I think that what you want is the exiting behavior outlined above but with a different entry behavior. You will get the late entry behavior if you set the scroll flags as follows:
<android.support.design.widget.CollapsingToolbarLayout
...stuff...
app:layout_scrollFlags="scroll|snap">
This just deleted the enterAlways flag. With these scroll flags, the app bar will not reappear (once collapsed) until the top of the content is visible and "pulls" the app bar into view.
So, one solution (of what is probably many) is to write a new behavior that will be attached to the AppBarLayout some code that will change the scroll flags once the app bar is fully collapsed and change them back as it opens again. That way you can change the behavior to be what you want and still use the Android machinery to figure out what the specific operations are at the view level. This can be done in a custom view or in the activity - wherever you have access to the scroll state of the app bar and the scrolling flags. It can also be done in a behavior but that is probably not the best place for it.
Oh, and as you have discovered, snapping is janky on API 26.
Here is an implementation of the concept. For simplicity, the implementation is in an activity:
ScrollingActivity.java
public class ScrollingActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
final AppBarLayout appBar = (AppBarLayout) findViewById(R.id.app_bar);
appBar.post(new Runnable() {
#Override
public void run() {
CollapsingToolbarLayout toolbarLayout =
(CollapsingToolbarLayout) findViewById(R.id.toolbar_layout);
setupAppBar(appBar, toolbarLayout);
}
});
}
private void setupAppBar(AppBarLayout appBar, final CollapsingToolbarLayout toolbarLayout) {
// Scroll range is positive but offsets are negative. Make signs agree for camparisons.
final int mScrollRange = -appBar.getTotalScrollRange();
appBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
private boolean mAppBarCollapsed = false;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (verticalOffset == mScrollRange) { // App bar just collapsed
mAppBarCollapsed = true;
AppBarLayout.LayoutParams lp =
(AppBarLayout.LayoutParams) toolbarLayout.getLayoutParams();
int flags = lp.getScrollFlags()
& ~AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS;
lp.setScrollFlags(flags);
toolbarLayout.setLayoutParams(lp);
} else if (mAppBarCollapsed) { // App bar is opening back up
mAppBarCollapsed = false;
AppBarLayout.LayoutParams lp =
(AppBarLayout.LayoutParams) toolbarLayout.getLayoutParams();
int flags = lp.getScrollFlags()
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS;
lp.setScrollFlags(flags);
toolbarLayout.setLayoutParams(lp);
}
}
});
}
}
I want to create a custom view that will be a child of an AppBarLayout. I need this view to collapse partially as I scroll up, but not completely. It will have a minimum height and stay fixed to the top of the AppBarLayout in it's small size mode and then expand back to it's large size mode when the view is scrolled back down.
I've spent a lot of time looking through the source of the AppBarLayout and CoordinatorLayout, and so far I don't see a way to do what I want. It looks like children of AppBarLayout must either stay visible or disappear completely when the view is scrolled up.
Can anyone suggest a way to create a child of an AppBarLayout that will behave this way?
Thank you
Here's the recipe:
If you set android:minHeight, the AppBarLayout will respect that value by not scrolling beyond the point that would make your component smaller. So your XML layout might be something like this:
<com.example.CustomCollapsingLayout
android:layout_width="match_parent"
android:layout_height="320dp"
android:minHeight="108dp"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
Next you want to have your class register an OnOffsetChangedListener with the parent AppBarLayout. Your component will get events as the app bar is scrolled so that you know how to configure your view.
class OnOffsetChangedListener implements AppBarLayout.OnOffsetChangedListener {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
final int scrollRange = appBarLayout.getTotalScrollRange();
float offsetFactor = (float) (-verticalOffset) / (float) scrollRange;
...
This shows you how to find the total scroll range and then find the ratio between the total scroll range and the current scroll position i.e. where the app bar is in its scroll.
You should do what CollapsingToolbarLayout does; override onAttachedToWindow and add the listener there:
// Add an OnOffsetChangedListener if possible
final ViewParent parent = getParent();
if (parent instanceof AppBarLayout) {
if (mOnOffsetChangedListener == null) {
mOnOffsetChangedListener = new OnOffsetChangedListener();
}
((AppBarLayout) parent).addOnOffsetChangedListener(mOnOffsetChangedListener);
}
Take a look at the source code for CollapsingToolbarLayout as it will give you some ideas. Your view needs to do a lot of the same things.
You can also look at my sample project that has an image that scales and moves as the toolbar is scrolled: https://github.com/klarson2/Collapsing-Image
I was using Collapsible Toolbar in my app. On activity launch Collapsible Toolbar is expanded state with scrolling enabled and its working well normally. But now I have a requirement to show a full screen error layout in case my API fails. In that case I have to collapsed toolbar with scrolling effect blocked.
Error Layout shows a Retry Button. On Retry I make API call again and if API gives success I have to again expand Toolbar and enable scrolling effect.
I was able to collapse toolbar with setExpanded(flag, animate) but in that case I am not able to block scrolling effect of Collapsible Toolbar while error layout is shown.
I need to provide a way to block as well as unblock scroll effect + Expand/Collapse Toolbar. Any help would be really appreciated.. !!!
Make your error layout such that it will overlap Collapsible Toolbar. Also set android:clickable="true" to your error layout.
When you set visibility to your error layout, set Toolbar scrolling accordingly.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f3f3f3"
android:orientation="vertical"
>
<!-- Add your other layout including Collapsible Toolbar here.-->
<RelativeLayout
android:id="#+id/errorLayout"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
I created a library AppBarrr to lock the screen in expanded mode, based on my previous answer.
As I said, the height of the Toolbar is the key: the CollapsingToolbarLayout will collapse until the Toolbar's height and will expand until the AppBarLayout's height.
With this library, you must set two layouts as the Toolbar and your Expanded Layout (used to lock the screen and the scroll), it will create a CollapsingToolbarLayout and inflate these layouts inside.
You can declare the animations duration, the color of the inner CollapsingToolbarLayout, the collapsed/expanded title's style, even the height of the locked layout... You could also hide the Expanded Layout if you click outside it. It can support NestedScrollView and ScrollView inside the Expanded Layout. The documentation and a sample app are available on Github.
For those who don't want to use the library, my previous answer shows the way to do it. Here's the output of the previous answer:
Basically, this is the same concept, but no need to write a full class, with the lib you just need to have a simple widget in xml and that's it!
Feel free to use, fork or test. Hope it will be useful ;)
If you use AlertDialog to communicate the error and a ProgressDialog (spinner) to show you are doing stuff, you can block user input while your app is doing it's thing.
A simple solution that you can apply is just use the property
android:visibility="gone"
for the content that you don't want to show and just make your error layout visible by using property android:visibility="visible"
place the error layout at the bottom of your parent layout
once the contents are not visible on screen and error layout is just visible you will achieve the desired result that you want. Hope this helps you.
You can implement the interface and call its methods when to enable or disable the collapsing effect.
public interface AppbarRequestListener {
void unlockAppBarOpen();
void lockAppBarClosed();
}
#Override
public void unlockAppBarOpen() {
appBarLayout.setExpanded(true, false);
appBarLayout.setActivated(true);
setAppBarDragging(false);
}
#Override
public void lockAppBarClosed() {
appBarLayout.setExpanded(false, false);
appBarLayout.setActivated(false);
setAppBarDragging(false);
}
private void setAppBarDragging(final boolean isEnabled) {
CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
behavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() {
#Override
public boolean canDrag(AppBarLayout appBarLayout) {
return isEnabled;
}
});
params.setBehavior(behavior);
}
I have a layout identical to the Play Store where I have a Toolbar, Tab Strip, and ViewPager all in a vertical LinearLayout. I want to achieve the quick return pattern of the Play Store where the Toolbar hides but the TabStrip and ViewPager stay but animate up with the Toolbar.
I have the animating Toolbar part down using animate().translateY() but I can't get the content to shift up with it (at least not smoothly). I've tried something like:
<FrameLayout>
<Toolbar (with WindowActionBarOverlay = true)>
<LinearLayout paddingTop = Toolbar_height>
*Contains all the stuff I don't want to hide*
</LinearLayout>
</FrameLayout>
But this doesn't make the content shift up either. So I tried setting the Top Padding of the LinearLayout to 0 after I animate the Toolbar but that is instantaneous rather than animating with the ToolBar. So I tried to animate the entire LinearLayout instead using animate().translateY() but that is a bit laggy and has some unwanted side effects.
Anyone have any ideas? For RecyclerView and preferably a minSDK of 15.
Try adding an animator listener on the toolbar's translation to update the padding. Back-of-napkin code:
toolbar.animate()
.translateY(-toolbar.getHeight())
.setUpdateListener(new AnimatorUpdateListener()) {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
contentView.setPadding(
contentView.getPaddingLeft(),
// The padding is the inverse of the animation progress.
toolbar.getHeight() * (1f - animation.getAnimatedFraction()),
contentView.getPaddingRight(),
contentView.getPaddingBottom());
}
});
I'd be interested to see what the performance is like updating the layout on each animation frame like that.
I've seen extended height app bar's in the Google Design App Bar guidelines. How do I implement these in Android Lollipop?
You need to use the new Toolbar widget to achieve this. Toolbar has special handling for it's minimum height to declare the amount of space which is used for buttons (and actions).
In the example below, we're setting the height to be 128dp (which is 56dp + 72dp as defined in the spec), but keeping the android:minHeight as the standard actionBarSize (which is usually 56dp). This means that the buttons and actions are constrained to be positioned vertically in the top 56dp. We can then use android:gravity to position the title at the bottom.
<Toolbar
android:id="#+id/toolbar"
android:layout_height="128dp"
android:layout_width="match_parent"
android:minHeight="?android:attr/actionBarSize"
android:background="?android:attr/colorPrimary"
android:gravity="bottom" />
If you're using AppCompat, then change the declaration to use android.support.v7.widget.Toolbar instead and use it's attributes.
Thanks for your question, its answer, and moreover for the implementation of the toolbar in the native and the supportlibrary :)
And we can play more. We can, at runtime, play with the Height and the MinimalHeight.
The height is the ToolBar height, it's simple, every body understand, and the gravity acts according to that height.
The minimalHeight is more tricky and should not be at minimum 56dp. This minHeight is used to place the line of your menuItem. This line is at the midlle of your minHeight.
So you can add this code to your activity to see by yourself the difference. :)
Runnable toolBarAnimator=new Runnable() {
#Override
public void run() {
if(postICS){
// toolbar.setTranslationX(iteration);
// toolbar.setElevation(iteration);
toolbar.setMinimumHeight(iteration * 5);
toolbar.getLayoutParams().height++;
}
uiThreadHanlder.postDelayed(this,16);
iteration++;
if(iteration>150)iteration=0;
}
};
Handler uiThreadHanlder=new Handler();
int iteration=0;
#Override
protected void onResume() {
super.onResume();
//launch the animation
uiThreadHanlder.postDelayed(toolBarAnimator, 1000);
}
#Override
protected void onPause() {
super.onPause();
//stop the animation
uiThreadHanlder.removeCallbacks(toolBarAnimator);
}
Where toolbar is:
toolbar = (Toolbar) findViewById(R.id.toolbar);
When doing this you obtain:
but if you leave the animation continue you obtain:
This is why, setting your toolbar android:layout_height to wrap_content is a good option in most of the case, because the Toolbar will adapt its height according to its contents (and you can change the content at runtime :)
And this is also how you change your toolbar size at runtime.
Thanks Chris Banes for the amazing work you did on the actionbar.