I am trying to animate a bar (just an ImageView) in the onBindViewHolder method.
If i don't try to animate it, it is working perfectly with this line:
holder.progressBar.getLayoutParams().width = 100;
But as soon as i add a function to animate this bar/imageView, it doesn't work.
I can see the bar only on the first item in the recyclerView and the bar is not animated.
this is the code that i use. what is the problem?
Thanks to all.
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
//holder.progressBarWeatherFactor.getLayoutParams().width = 100;
animateWidth(0, 100, holder.progressBar.getLayoutParams());
}
public void animateWidth(int start, int end, final ViewGroup.LayoutParams myBar)
{
ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
valueAnimator.setDuration(1800);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
#Override
public void onAnimationUpdate( ValueAnimator valueAnimator)
{
myBar.width = ((int) valueAnimator.getAnimatedValue());
}
});
valueAnimator.start();
}
So, in order to help, i found a solution, maybe not the best but it is working and i hope it will help!
(the /10*3/64 is in relation with my image size, so don't matter of this)
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
animateWidth(0, 100, holder.progressBar);
}
public void animateWidth(int initialValue, int finalValue, ImageView image)
{
image.setPivotX(0f);
PropertyValuesHolder barWidth;
barWidth = PropertyValuesHolder.ofFloat("scaleX", 0, ((float)(((image.getResources().getDisplayMetrics().density)/10)*3)/64)*finalValue);
ObjectAnimator.ofPropertyValuesHolder(image, barWidth).setDuration(800).start();
}
Related
I have two recycler views in one screen and in between of them, there is one more button like this.
I want to update the height of recycler view of the upper one to MATCH PARENT with animation, So I had tried this.
binding.moreLl.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ValueAnimator anim = ValueAnimator.ofInt();
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = binding.fragmentWalletRv.getLayoutParams();
layoutParams.height = val;
binding.fragmentWalletRv.setLayoutParams(layoutParams);
}
});
anim.setDuration(500);
anim.start();
}
});
But unfortunately i cant get the expected result. So please guide me how i can do that thing.
Thanks in advance.
If you just want to have an animation when layout changes, no need to use a custom animation.
Try adding default layout change animation by enabling
android:animateLayoutChanges="true"
to the parent layout in xml.
On your more button click, only change the LayoutParams of the Recyclerview.
To change the speed of animation
LinearLayout layout = mContentView.findViewById(R.id.parent_layout);
LayoutTransition lt = layout.getLayoutTransition();
lt.setDuration(2000);
public class SlideAnimation extends Animation {
int mFromHeight;
int mToHeight;
View mView;
public SlideAnimation(View view, int fromHeight, int toHeight) {
this.mView = view;
this.mFromHeight = fromHeight;
this.mToHeight = toHeight;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation) {
int newHeight;
if (mView.getHeight() != mToHeight) {
newHeight = (int) (mFromHeight + ((mToHeight - mFromHeight) * interpolatedTime));
mView.getLayoutParams().height = newHeight;
mView.requestLayout();
}
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
Use above animation like below
rv1 = findViewById(R.id.rv1);
Animation animation = new SlideAnimation(rv1, rv1.getHeight(), 500);
animation.setInterpolator(new AccelerateInterpolator());
animation.setDuration(500);
rv1.setAnimation(animation);
rv1.startAnimation(animation);
I have a search bar that expands when icon is clicked in toolbar. Now, everything is cool when it is expanded, everything works fine, the thing that is not good is that the height isnt good when the search bar is collapsed. The toolbar upon collapsing search bar has bigger height than it had. Here is the code:
private void expand(View view) {
//set Visible
view.setVisibility(View.VISIBLE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(widthSpec, heightSpec);
ValueAnimator mAnimator = slideAnimator(0, view.getMeasuredHeight(), view);
mAnimator.start();
}
private ValueAnimator slideAnimator(int start, int end, final View view) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//Update Height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = value;
view.setLayoutParams(layoutParams);
}
});
return animator;
}
private void collapse(final View view) {
int finalHeight = view.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0, view);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animator) {
//Height=0, but it set visibility to GONE
view.setVisibility(View.INVISIBLE);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}
Everything is good here, check where you call method if you are changing background or any other property of the toolbar.
I need to animate a TextView when i click a button. The height of the TextView is wrap_content. This TextView is inside a RecyclerView row and i need to expand it from visibility gone to his real height with content. I used ValueAnimator.
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = tvAdditional.getLayoutParams();
layoutParams.height = value;
tvAdditional.setLayoutParams(layoutParams);
}
});
return animator;
}
private void expand(View v) {
v.setVisibility(View.VISIBLE);
v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ValueAnimator mAnimator = slideAnimator(0, v.getMeasuredHeight());
mAnimator.start();
}
private void collapse(final View v) {
int finalHeight = v.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animator) {
v.setVisibility(View.GONE);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}
In debug i noticed that when i use getMeasuredHeight() method on view in expand, the value is always 76, also if i added items with more than one row.
P.s. i call expand and collapse inside the click listener of the row.
Screenshot:
SOLVED
I changed my expand method adding a listener in which i set the height to wrap_content at the end of the animation:
private void expand(final View v) {
v.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
final int targetHeight = v.getMeasuredHeight();
ValueAnimator mAnimator = slideAnimator(0, targetHeight);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
v.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
v.requestLayout();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}
When you have the Visibility as GONE, you cannot know it's measured height until it's actually drawn. In order to have that, you need to have GlobalLayoutListener on that TextView, so you can get it's measured height when it's drawn.
In your expand method, I see that you set the Visibility to Visible and then try to measure it, which won't be drawn immediately, so you cannot get the height.
So what you can do to get the actual height of the TextView if it's visibility is initially gone, first you should change the initial visibility to VISIBLE, second, set a Global Layout Listener and as third step, store the TextView's height in a global value after you measure it, and as the last step, you can set the Visibility of TextView as GONE.
This process is normally really quick, so you won't be able to see the TextView's visibility changing from VISIBLE to GONE with your eyes.
I have a button in my layout. And I am animating the position of that button using ObjectAnimator with translationX animation.
ObjectAnimator btnAnimator = ObjectAnimator.ofFloat(myBtn, "translationX",
ViewHelper.getTranslationX(myBtn), 0);
btnAnimator.addListener(new AnimatorListener() {
#Override
public void onAnimationCancel(Animator arg0) {}
#Override
public void onAnimationEnd(Animator arg0) {
Log.i("TAG","Animation Finished");
}
#Override
public void onAnimationRepeat(Animator arg0) {}
#Override
public void onAnimationStart(Animator arg0) {}
});
btnAnimator.setDuration(animationSpeed).start();
Now I would like to have a listener for the TranslationX of that button to notify whenever the TranslationX position of the button changes.
Here's an easy way I found to do what you're after:
btnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Log.e("TAG", "translateX: "+animation.getAnimatedValue("translationX"));
}
});
btnAnimator.setDuration(animationSpeed).start();
Two possible approaches:
1) Override onLayout() in your view to manually compare and detect position changes.
2) Use onLayoutChangeListener on your View:
button.addOnLayoutChangeListener(new OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
// Check your new position vs the old one
}
});
I used this and it worked as a decent way of listening for changes of the view translation.
var previousTranslationX = view.translationX
var previousTranslationY = view.translationY
view.viewTreeObserver.addOnDrawListener {
if (previousTranslationX != view.translationX ||
previousTranslationY != view.translationY) {
previousTranslationX = view.translationX
previousTranslationY = view.translationY
dispatchViewTranslationUpdated(view)
}
}
Simply register a callback to be invoked when the view tree is about to be drawn.
Note: This listener almost called every time the view is drawn!
public class MyView extends View {
private float oldScaleX;
public MyView(Context context) {
super(context);
getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Many things can invoke this method! We don't know why view going
// to be redrawn, So we must determine the cause ourselves.
float newScaleX=getScaleX();
if (oldScaleX!=newScaleX) {
scaleXUpdated();
oldScaleX=newScaleX;
}
}
});
}
private void scaleXUpdated() {
Log.e(TAG,"scaleX updated "+getScaleX);
}
}
How to do scrolling effect like twitter when scroll Up hide viewpager tab (Home, Discover, activity). Or effect like facebook scrolling, while scroll up hide option view(status, photo, checkin) when scroll down show option view. Any example link will do please help.
Easy solution:
public abstract class OnScrollObserver implements AbsListView.OnScrollListener {
public abstract void onScrollUp();
public abstract void onScrollDown();
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
int last = 0;
boolean control = true;
#Override
public void onScroll(AbsListView view, int current, int visibles, int total) {
if (current < last && !control) {
onScrollUp();
control = true;
} else if (current > last && control) {
onScrollDown();
control = false;
}
last = current;
}
Usage:
listView.setOnScrollListener(new OnScrollObserver() {
#Override
public void onScrollUp() {
}
#Override
public void onScrollDown() {
}
});
EDIT: better, you have this library https://github.com/ksoichiro/Android-ObservableScrollView
You can look at this https://github.com/LarsWerkman/QuickReturnListView
Source: https://stackoverflow.com/a/25304575/244702
That's my own implementation:
notice:
View to be hidden should be fixed height
We are not hiding the view by Visiblity.GONE
We are setting the final height to 0px
Here is the code:
//Your view which you would like to animate
final RelativeLayout yourViewToHide = (yourViewToHideativeLayout) findViewById(R.id.topWrapper);
//The initial height of that view
final int initialViewHeight = yourViewToHide.getLayoutParams().height;
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//Try catch block for NullPointerExceptions
try{
//Here is a simple delay. If user scrolls ListView from the top of the screen to the bottom then continue
if(firstVisibleItem % visibleItemCount == 0) {
//Here we initialize the animator, doesn't matter what values You will type in
ValueAnimator animator = ValueAnimator.ofInt(0, 1);
//if Scrolling up
if (fastScrollSB.getProgress() > view.getFirstVisiblePosition()){
//Getting actual yourViewToHide params
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
if (!animator.isRunning()) {
//Setting animation from actual value to the initial yourViewToHide height)
animator.setIntValues(params.height, initialViewHeight);
//Animation duration
animator.setDuration(500);
//In this listener we update the view
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
yourViewToHide.setLayoutParams(params);
}
});
//Starting the animation
animator.start();
}
System.out.println("Scrolling up!");
//If not scrolling
} else if (fastScrollSB.getProgress() == view.getFirstVisiblePosition()) {
System.out.println("Not Scrolling!");
//If scrolling down
} else if (fastScrollSB.getProgress() < view.getFirstVisiblePosition()){
//Getting actual yourViewToHide params
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
if (!animator.isRunning()) {
//Setting animation from actual value to the target value (here 0, because we're hiding the view)
animator.setIntValues(params.height, 0);
//Animation duration
animator.setDuration(500);
//In this listener we update the view
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
yourViewToHide.setLayoutParams(params);
}
});
//Starting the animation
animator.start();
}
System.out.println("Scrolling down!");
}
}
}catch (Exception e){
e.printStackTrace();
}
}
});`
Hope it fits your needs :)
there is no quick example for this. But what you can do is keep track which way you are scrolling and show or hide the view accordingly
For example get first visible position of the ListView keep track of this and if it is smaller then before you know you are scrolling up this way you can show the view. In case it is bigger then hide the view.
This is a simple approach in case you want to have more precise you need to work with onTouchListeners and y coordinates of the movement.
http://developer.android.com/reference/android/widget/ListView.html