Android Toolbar show/hide blank space - android

I'm trying to have my toolbar hide or show when the user is scrolling a list. To do it, I'm using a translation but a blank space appears instead of my actionBar. If I use a setVisibility(View.GONE), the blank space will appear during the animation and hide when it's done which is ugly..
Here is a short video of my issue
And here is how i do my animation (from Google I/O app) :
public void showToolbar(boolean show){
if (show) {
toolbar.animate()
.translationY(0)
.alpha(1)
.setDuration(HEADER_HIDE_ANIM_DURATION)
.setInterpolator(new DecelerateInterpolator());
} else {
toolbar.animate()
.translationY(-toolbar.getBottom())
.alpha(0)
.setDuration(HEADER_HIDE_ANIM_DURATION)
.setInterpolator(new DecelerateInterpolator());
}
}
And here is my layout :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainContent">
<include layout="#layout/toolbar"
android:id="#+id/my_toolbar" />
<fragment
android:name="com.ar.oe.fragments.SectionsFragment"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"/>
</RelativeLayout>
And my toolbar
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/my_toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
/>

You need animate your container and not your toolbar.
Try this:
RelativeLayout mainContent = (RelativeLayout) findById(R.id.mainContent);
public void showToolbar(boolean show){
if (show) {
mainContent.animate()
.translationY(0)
.alpha(1)
.setDuration(HEADER_HIDE_ANIM_DURATION)
.setInterpolator(new DecelerateInterpolator());
} else {
mainContent.animate()
.translationY(-toolbar.getBottom())
.alpha(0)
.setDuration(HEADER_HIDE_ANIM_DURATION)
.setInterpolator(new DecelerateInterpolator());
}
}
It work for me ;)
If you want to move your fragment with your toolbar, you need animate your fragment and not only your bar.

It is better that you show slide up translate animation for both toolbar and your below fragment (together), when user is trying to scroll, such that only toolbar goes out of the view and fragment reaches the top. (within say, 200ms). To do this, translate the whole outer Relative Layout by say some 20% (you can change such that only toolbar goes out of the view) :
Add slide_up.xml in your anim folder :
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fillAfter="false"
android:fromYDelta="0%"
android:toYDelta="-20%"
android:duration="200"/>
</set>
Then, when scroll event is triggered, do the following :
...
RelativeLayout rel = (RelativeLayout)findViewById(R.id.mainContent);
Animation slideUp = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_up);
rel.startAnimation(slideUp);
...
Hope this helps...

Related

Transition Manager - Sliding a view out is not working as wanted

I'm trying to slide this view in and out using Transition + Transition manager, however when hitting the hide button to make the view GONE it doesn't have the sliding animation. However, the show button does have the sliding in animation to make the view VISIBLE again.
#OnClick(R.id.testBtn)
public void onTestBtnClick(){
//hide
Transition transition = new Slide(Gravity.START);
transition.setDuration(600);
TransitionManager.beginDelayedTransition(mParentLayout, transition);
mLayout.setVisibility(View.GONE);
}
#OnClick(R.id.testBtn2)
public void onTestBtn2Click(){
//show
Transition transition = new Slide(Gravity.START);
transition.setDuration(600);
TransitionManager.beginDelayedTransition(mParentLayout, transition);
mLayout.setVisibility(View.VISIBLE);
}
I've tried changing the gravity of testBtn2 to Gravity.END, but that causes it to slide all the way starting from the right side of the screen.
Here's the layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/main_activity_root_view"
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:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="#+id/testBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:text="Hide"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="#+id/testBtn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="#id/testBtn"
android:text="show"
/>
<LinearLayout
android:id="#+id/layout"
android:orientation="vertical"
android:layout_width="100dp"
android:layout_height="250dp"
android:background="#drawable/background_side_bar_corners"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Not sure whats wrong with your code. i have created a sample, just try the code below it working fine. make sure to add android:visibility="gone" for panel view in layout so that its hidden at first launch.
public class MainActivity extends AppCompatActivity {
boolean isShowing = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_damm_activty);
findViewById(R.id.testBtn).setOnClickListener(v -> {
showSlidingPanel(!isShowing);
});
}
private void showSlidingPanel(boolean show) {
ViewGroup parent = findViewById(R.id.main_activity_root_view);
View layout = findViewById(R.id.layout);
Transition transition = new Slide(Gravity.START);
transition.setDuration(450);
transition.addTarget(R.id.layout);
transition.setInterpolator(new AccelerateDecelerateInterpolator());
TransitionManager.beginDelayedTransition(parent, transition);
layout.setVisibility(show ? View.VISIBLE : View.GONE);
isShowing = show;
}
}
Create these animations
slide up
<translate
android:duration="400"
android:fromYDelta="100%"
android:toYDelta="0" />
slide down
<translate
android:duration="400"
android:fromYDelta="0"
android:toYDelta="100%" />
When you want to set the layout visible
binding.musicOptionsLayout.setVisibility(View.GONE); binding.musicOptionsLayout.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_down));
When you want to set the layout Gone
binding.musicOptionsLayout.setVisibility(View.GONE); binding.musicOptionsLayout.startAnimation(AnimationUtils.loadAnimation(v.getContext(), R.anim.slide_down));
set android:visibility="gone" on XML..
Hope this will fix your issues...
The excellent answer here is very similar to this question.
I have noticed the constraint layout needs to be set up very specifically - if the view you are setting to GONE is constrained to it's sibling, those constraints seem to take precedence over any transition manager animation, i.e. setting a view to GONE just instantly kills it instead of smoothly animating, because the other view's constraints kick in immediately.
My solution was to use a guideline and have two views constrained to it, and set the GuidelineBegin for the transition manager. Just another option that may help:
val rootView = binding.constraintLayout
val constraintSet = ConstraintSet()
constraintSet.clone(rootView)
val transition = AutoTransition()
transition.duration = 150L
transition.excludeTarget(R.id.orders_recycler_view, true)
constraintSet.setGuidelineBegin(binding.guideline.id, if (showingDetailView) 0.px else 64.px)
TransitionManager.beginDelayedTransition(rootView as ViewGroup, transition)
constraintSet.applyTo(rootView)
Kotlin solution
Call the method wherever you want. Use FALSE to Slide out and TRUE
to Slide in.
targetView = View you want to slide in / out
rootLayout = Main layout where the targetView is located
In this example, it will slide from RIGHT to LEFT. If you want LEFT
to RIGHT, change Gravity.END to Gravity.START
private fun shareSlideInOut(show: Boolean) {
val slide = Slide(Gravity.END)
slide.duration = 450
slide.addTarget(targetView)
slide.interpolator = AccelerateDecelerateInterpolator()
TransitionManager.beginDelayedTransition(rootLayout, slide)
shareCodeLayout.visibility = if (show) View.VISIBLE else View.GONE
}

How to achieve this effect in Snackbar

Here is the gif:
There are several features:
Fade in and immediately out.
It has margin. Because the default effect is match parent width.
Now I have already solved the margin problem. But I don't know how to achieve the animation effect. And here's my code:
snackbar_animation.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fillAfter="true"
android:shareInterpolator="true">
<alpha
android:fromAlpha="0.1"
android:toAlpha="1" />
<scale
android:fromXScale="0.5"
android:fromYScale="0.0"
android:toXScale="1"
android:toYScale="1"/>
</set>
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button snackbar;
private CoordinatorLayout coordinatorLayout;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.snackbar_test);
coordinatorLayout = findViewById(R.id.coordinator);
snackbar = findViewById(R.id.show_snackbar);
snackbar.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.show_snackbar:
showSnackbar();
break;
}
}
private void showSnackbar(){
Snackbar snackbar = Snackbar.make(coordinatorLayout,"i am a snack bar",Snackbar.LENGTH_SHORT);
View sbView = snackbar.getView();
// add animation
Animation animation = AnimationUtils.loadAnimation(this,R.anim.snackbar_animation);
sbView.setAnimation(animation);
// modify margin
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) sbView.getLayoutParams();
params.setMargins(params.leftMargin + 50,
params.topMargin,
params.rightMargin + 50,
params.bottomMargin + 50);
sbView.setLayoutParams(params);
// show snackbar
snackbar.show();
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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:id="#+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="#+id/show_snackbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingTop="10dp"
android:text="show snackbar" />
</android.support.design.widget.CoordinatorLayout>
If there are something else I need to know about this question, just comment below this question.such as: value animation or other things.I am new to the animation, so if you know the great learning materials, tell me, Thanks.
Read this. setAnimation() just queues an animation. It doesn't actually start anything. SnackBar doesn't call startAnimation when it's show, either.
However, even if you use startAnimation(), your code probably won't work as-is. You need to initially set the scale of your SnackBar to 0, so it's at the proper starting value.
You should use SnackBar#setCallback() and then start your show animation inside the onShow() method. Set your dismiss animation inside the onDismiss() method.

Animate visibility of a view from gone to visible with animation

I have a view that is invisible by default(Just for the first time).
Now I need to switch the visibility to VISIBLE with this animation:
if (myView.getVisibility() == View.INVISIBLE) {
myView.setVisibility(View.VISIBLE);
myView.animate().translationY(0);
}
(Like the SnackBar default animation)
But this isn't working. It will turn visible with default animation
Is there any simple way that I could achieve this?
Note
I'm animating my view to dismiss, like this:
myView.animate().translationY(myView.getHeight());
You can do this using XML animation.
Create a slide-up animation XML using set and alpha and put this XML into your resource anim folder.
slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="500"
android:fromYDelta="100%"
android:toYDelta="0" />
</set>
USE:
Use AnimationUtils.loadAnimation() to load animation from XML and set and start animation using .startAnimation() method.
Here is an example:
ImageView imageView = (ImageView) findViewById(R.id.imageView);
// slide-up animation
Animation slideUp = AnimationUtils.loadAnimation(this, R.anim.slide_up);
if (imageView.getVisibility() == View.INVISIBLE) {
imageView.setVisibility(View.VISIBLE);
imageView.startAnimation(slideUp);
}
Hope this will help~
Add animations using ConstraintLayout
Just add below code above the views whose visibility is updated:
TransitionManager.beginDelayedTransition(constraintLayout)
Note:
ConstraintLayout will only perform animation on its direct children since it only knows when you change layout parameters and constraints on the children that it handles.
ConstraintLayout only animates layout related changes.
For more see this post https://robinhood.engineering/beautiful-animations-using-android-constraintlayout-eee5b72ecae3
This is the best way to animate views visibility :
private void viewGoneAnimator(final View view) {
view.animate()
.alpha(0f)
.setDuration(500)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
}
});
}
private void viewVisibleAnimator(final View view) {
view.animate()
.alpha(1f)
.setDuration(500)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.VISIBLE);
}
});
}
And then call this method wherever you wanted to make a view visible or gone and give the intended view to methods as the parameter.
Just You need to add android:animateLayoutChanges="true" to your layout.
When I set visibility gone to linear_container, linear_bottom will animate from bottom to up and take place of "linear_container".
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/layoutTop"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:id="#+id/linear_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
</LinearLayout>
<LinearLayout
android:id="#+id/linear_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
</LinearLayout>
</LinearLayout>
Based on this answer:
with this methods, I can set the visibility of my view to VISIBLE with a slideUp animation(Like snackbar animation):
int getScreenHeight() {
DisplayMetrics displaymetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
return displaymetrics.heightPixels;
}
public void animateOnScreen(View view) {
final int screenHeight = getScreenHeight();
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "y", screenHeight, (screenHeight * 0.8F));
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
}
Then I can use it like this:
if (myView.getVisibility() == View.INVISIBLE) {
myView.setVisibility(View.VISIBLE);
animateOnScreen(myView);
}

ViewGroup AnimationListener not firing

I'm removing a child from a parent with animateLayoutChanges on. I want to know when the animation is complete, but can't get the listener to fire:
<LinearLayout
android:id="#+id/parent"
android:animateLayoutChanges="true">
<TextView android:id="#+id/child" />
</LinearLayout>
void testRemove() {
ViewGroup parent = (ViewGroup)findViewById(R.id.parent);
parent.setLayoutAnimationListener(listener);
TextView child = findViewById(R.id.child);
child.setVisibility(View.GONE); // this triggers the animation.
}
Animation.AnimationListener listener = new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
// None of these callback methods fire.
}
};
Any ideas why the listener wouldn't fire?
Thanks
http://developer.android.com/reference/android/view/ViewGroup.html#setLayoutAnimationListener(android.view.animation.Animation.AnimationListener)
-------- Update ------------------
A TransitionListener seems to work:
LayoutTransition layoutTransition = parent.getLayoutTransition();
layoutTransition.addTransitionListener(new TransitionListener());
Use the layout animation attribute in your LinearLayout xml,
Docs here : http://developer.android.com/reference/android/view/ViewGroup.html#attr_android:layoutAnimation
add a file to your res/anim folder of fade_in.xml, this will be your fade animation
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
Then add a file to your res/anim folder of layout_fade_in.xml that has a reference to the fade animation #anim/fade_in you just made, this file will be used for android:layoutAnimation values
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animation="#anim/fade_in" />
Now, change your LinearLayout xml slightly to include the android:layoutAnimation attribute and value
<LinearLayout
android:id="#+id/parent"
android:layoutAnimation="#anim/layout_fade_in"
android:animateLayoutChanges="true">
<TextView android:id="#+id/child" />
</LinearLayout>

Adding animation to a ListView in order to expand/collapse content

I have a list view which uses a custom adapter in order to show my custom content. Its layout is the following.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<ImageView
android:id="#+id/itemimage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="5"
android:scaleType="fitCenter"/>
<TextView
android:id="#+id/itemdescription"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16sp"
android:layout_weight="1"/>
</LinearLayout>
<TextView
android:id="#+id/itemtext"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="TEXT CONTENT"
android:layout_weight="1"/>
</LinearLayout>
I would like that the listview only showed the view with the ids itemimage and item description, keeping the itemtext hidden.
The idea is to have a onclicklistener on each item of the list, in order to expand that item so it shows the itemtext content. I know I should use Tweening animation in order to expand/collapse each item, but I can't figure out how to do that.
If you need more code snippets, feel free to ask.
To do that, I built an Animation class, which animates the margin to negative values, making the item disappear.
The animation looks like this:
public class ExpandAnimation extends Animation {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Calculating the new bottom margin, and setting it
mViewLayoutParams.bottomMargin = mMarginStart
+ (int) ((mMarginEnd - mMarginStart) * interpolatedTime);
// Invalidating the layout, making us seeing the changes we made
mAnimatedView.requestLayout();
}
}
}
I have an entire example app for this animation on my blog post
Tried Udinic's solution, but finally chose this alternative:
res/anim/scale_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<scale
android:duration="700"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="0%"
android:toXScale="1.0"
android:toYScale="0.0" />
</set>
Animate ListView implementation:
protected void animateView(final View v, final int animResId, final int endVisibility){
Animation anim = AnimationUtils.loadAnimation(getApplicationContext(),
animResId);
anim.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {
v.setVisibility(View.VISIBLE);
}
public void onAnimationEnd(Animation animation) {
v.setVisibility(endVisibility);
}
public void onAnimationRepeat(Animation animation) {}
});
v.startAnimation(anim);
}
Example calls to animate my ListView (or any View child class):
animateView(listView1, R.anim.scale_down, View.GONE);
animateView(listView1, R.anim.scale_up, View.VISIBLE);
It is working on my KitKat phone.

Categories

Resources