Visual bug with native plugin animation in Ionic 4 Capacitor - java

I'm currently working in a custom android native plugin for Capacitor. The plugin consists in an app's footer which is working fine except for a hiding animation.
The problem is I'm changing the WebView margin everytime the footer is being shown/hide, which makes a black(sometimes is orange, probably because is one of app's main colors) bar appears in the space occupied by the footer. Black bar disappears once the animation ends.
I've tried changing the WebView margin at animation start/end and the result is the same.
Thanks in advance guys, here is some code.
Animation XML:
<translate
android:duration="150"
android:fromYDelta="0"
android:toYDelta="100%" />
WebView margin function:
private void changeWebViewMargin(float pixels) {
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) wb.getLayoutParams();
params.setMargins(0, 0, 0, (int) dpTopixel(getContext(), pixels));
wb.setLayoutParams(params);
wb.requestLayout();
}
Hide function:
#PluginMethod()
public void hide(PluginCall call) {
Boolean animated = call.getBoolean("animated");
if (animated == null) {
animated = false;
}
final boolean finalAnimated = animated;
this.getBridge().getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (finalAnimated && footer.getVisibility() == View.VISIBLE) {
changeWebViewMargin(0f);
Animation myAnim = AnimationUtils.loadAnimation(getBridge().getContext(), R.anim.hide_footer);
myAnim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
footer_img.setVisibility(View.INVISIBLE);
footer.setVisibility(View.INVISIBLE);
btn3.setVisibility(View.INVISIBLE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
footer.startAnimation(myAnim);
footer_img.startAnimation(myAnim);
btn3.startAnimation(myAnim);
} else {
footer_img.setVisibility(View.INVISIBLE);
footer.setVisibility(View.INVISIBLE);
btn3.setVisibility(View.INVISIBLE);
changeWebViewMargin(0f);
}
}
});
}

Instead of working with margins try using WindowInsets:
ViewCompat.setOnApplyWindowInsetsListener(wb, (v, insets) -> {
((ViewGroup.MarginLayoutParams) v.getLayoutParams()).bottomMargin =
insets.getSystemWindowInsetTop() + (int) dpTopixel(getContext(), pixels);
return insets.consumeSystemWindowInsets();
});

Related

How to set an image visibility after animation is complete

I'm trying to set an image visibility after an animation ends, but i'm have difficulty.
I have created few images and set the tag for each. Whenever the user clicks the image the visibility gets set to invisible. I have also created a method that animates the images. I would like when the animation finishes for the visibility of the images to reset to Visible. Apologies,if this sounds a bit unclear i'm not entirely sure how to word this question. Any help would be appreciated.
public void popBubbles(View view) {
final String tag = String.valueOf(view.getTag());
if(tag=="0"){
bubble.setVisibility(View.INVISIBLE);
}else if(tag=="1"){
bubble1.setVisibility(View.INVISIBLE);
}else if(tag=="2"){
bubble2.setVisibility(View.INVISIBLE);
}else if(tag=="3"){
bubble3.setVisibility(View.INVISIBLE);
}else if(tag=="4"){
bubble4.setVisibility(View.INVISIBLE);
}else if(tag=="5"){
bubble5.setVisibility(View.INVISIBLE);
}else if(tag=="6"){
bubble6.setVisibility(View.INVISIBLE);
}else if(tag=="7"){
bubble7.setVisibility(View.INVISIBLE);
}else if(tag=="8"){
bubble8.setVisibility(View.INVISIBLE);
}
}
public void animateBubbles() {
for (final ImageView img : IMGS) {
animation = ObjectAnimator.ofFloat(img, "translationY", 0f, -deviceHeight);
animation.setDuration(4000);
animation.start();
animation.setRepeatCount(ValueAnimator.INFINITE);
}
}
This code is in kotlin but you can add a listener to the animation use the on AnimationEnd() method like this.
animation.addListener(object : Animator.AnimatorListener {
override fun onAnimationEnd(animation: Animator?) {
}
})
Hope this helps.
set Animation listener and override methods. You can write your logic in onAnimationEnd method
animation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
//Do operation on ImageView
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});

How would I animate multiple views at the same time over different points?

I'm quite new to Android Studio, and I would like to animate multiple images at the same time, over multiple coordinates.
Let's say I would like to move image A from 0/0 to 100/100, and then to 200/200; at the same time (eg. by the click of a button), image B shall be moved from 0/100 to 100/100, and then to 200/100. All with simple translations.
(As a plus, I would like to be able to set the duration of the movements independently.)
When the animations have finished, there should be an event (somthing like OnAnimationFinish?) to trigger other things, like enabling the start button again.
What would be the most effective way to do this? I know there is an AnimationSet in Android to store multiple animations, but I don't know if this is helpful here.
Thanks in advance!
You can use ObjectAnimator with AnimatorSet to play multiple animations simultaneously with proper listeners.
For example():
val imageXAnimator = ObjectAnimator.ofFloat(imageView, "translationX", translateX)
val imageYAnimator = ObjectAnimator.ofFloat(imageView, "translationY", translateY)
val imageAlphaAnimator = ObjectAnimator.ofFloat(imageView, "alpha", if (reverse) 0F else 1F)
val animationSet = AnimatorSet()
animationSet.playTogether(
imageXAnimator,
imageYAnimator,
imageAlphaAnimator)
animationSet.interpolator = DecelerateInterpolator()
animationSet.duration = 1000
animationSet.addListener(
onStart = {
//When animation is started
},
onEnd = {
//When animation finishes
}
)
animationSet.start()
Or a simple extension in kotlin which can be called on a specific View:
inline fun View.animateTranslationY(translationY: Float, duration: Long = 1000, startDelay: Long = 0) {
val translationYAnimator = ObjectAnimator.ofFloat(this, "translationY", translationY)
translationYAnimator.duration = duration
translationYAnimator.startDelay = startDelay
translationYAnimator.interpolator = LinearInterpolator()
translationYAnimator.addAnimatorListener(
onStart = { },
onEnd = { }
)
translationYAnimator.start()
}
You can use the "View.animate()" method that allow you to access to its sub-methods and events to manage all these stuff. You just have to call "animate()" on a View (TextView, ImageView, EditBox, etc..) and then call its sub-methods to decide what kind of animation to perform: motion, rotation, changing transparency, etc... For each animation you can set a listener that will be called at Start and/or at the End of animation.
Consider these two methods for both views
private void animateFirstView(final ImageView first, long durationFirst, final long durationSecond) {
first.setTranslationX(0);
first.setTranslationY(0);
first.animate().translationX(100).translationY(100).setDuration(durationFirst).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
first.animate().translationY(200).translationX(200).setDuration(durationSecond).setListener(null).setInterpolator(new LinearInterpolator()).start();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}).setInterpolator(new LinearInterpolator()).start();
}
private void animateSecondView(final ImageView second, long durationFirst, final long durationSecond) {
second.setTranslationX(0);
second.setTranslationY(100);
second.animate().translationX(100).translationY(100).setDuration(durationFirst).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
second.animate().translationY(200).translationX(100).setDuration(durationSecond).setListener(null).setInterpolator(new LinearInterpolator()).start();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}).setInterpolator(new LinearInterpolator()).start();
}
You can do this:
private void playAnimation(final View view, final int endRangeY) {
view.animate().translationY(endRangeY) //endRangeY where you want to end the animation
.alpha(0.0f)
.setDuration(3000)
.setListener(new AnimatorListenerAdapter() {
#Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
}

Java - Endless while loop with FadeIn & FadeOut animation

I have an requirement to show the text on Recyclerview with FadeIn & FadeOut animation.
Below is the dynamic list where it's need to loop infinite items i.e; 1-2-3-1-2-3-1-2-3
List<String> mImageDesc = new List<String>();
mImageDesc.add("1");
mImageDesc.add("2");
mImageDesc.add("3");
I have used below code but which is not working for me.
final Animation animationFadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_in_animation);
final Animation animationFadeOut = AnimationUtils.loadAnimation(mContext, R.anim.fade_out_animation);
Animation.AnimationListener animListener = new Animation.AnimationListener(){
// Required to change the image
int i = 0;
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
if (animation == animationFadeIn) {
// Start fade-out animation
mTxtImageDescription.startAnimation(animationFadeOut);
} else if (animation == animationFadeOut) {
*while(mImageDesc.listIterator().hasNext()) {
System.out.println(mImageDesc.listIterator().next());
mTxtImageDescription.setText(mImageDesc.listIterator().next());
mTxtImageDescription.startAnimation(animationFadeIn);*
}
}
}
};
// Set listener to animation
animationFadeIn.setAnimationListener(animListener);
animationFadeOut.setAnimationListener(animListener);
// Start fade-in animation
mTxtImageDescription.setText(mImageDesc.get(0));
mTxtImageDescription.startAnimation(animationFadeIn);
In the last line off your while loop you have RE-started the 'animationFadeIn' ,so
the 'animListener' callbacks will be called forever .Notice if while loop was that repeating you would have 'StackOverflowError'.

Animation performance slow in Android

Is there any difference in adding single image 10 times and 10 different images(same dimens,same resolution,same file size) one time for animation.?
Because I added 1 image 10 times and provided multiple animation while swiping it was smooth throughout my interaction. but while adding different images (same dimens,same resolution,same file size) for my process, there is severe lag in animating. Some steps i tried to improve performance
1.Adding Layer type and setting null while animation ended (This improves for one time animating, when the animation is continuous, this is not helping)
2.Tried Setting hardwareaccelerated attribute in Manifest
3.Tried setting large heap attribute in manifest
4.Added animation cache for items.
5.Tried running with high RAM devices.
Do we have any options to improve animation performance.?
EDITED:
FrameLayout frame = new FrameLayout(this);
frame.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 700));
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(R.drawable.image1);
arrayList.add(R.drawable.image2);
arrayList.add(R.drawable.image3);
arrayList.add(R.drawable.image4);
arrayList.add(R.drawable.image5);
arrayList.add(R.drawable.image6);
arrayList.add(R.drawable.image7);
arrayList.add(R.drawable.image8);
arrayList.add(R.drawable.image9);
arrayList.add(R.drawable.image10);
arrayList.add(R.drawable.image11);
for(int i=0;i<11;i++){
ImageView imgvw = new ImageView(this);
imgvw.setImageResource(arrayList.get(i));
imgvw.setLayoutParams(new ActionBar.LayoutParams(250, 400));
imgvw.setScaleType(ImageView.ScaleType.FIT_XY);
imgvw.setX(this.getResources().getDisplayMetrics().widthPixels / 2);
imgvw.setRotationY(-45);
imgvw.setScaleY(0.7f);
frame.addView(imgvw);
}
setContentView(frame);
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:{
final AnimatorSet animationSet = new AnimatorSet();
for(int i=0;i<frame.getChildCount();i++){
final ImageView vw = (ImageView) frame.getChildAt(i);
// vw.setLayerType(View.LAYER_TYPE_HARDWARE, null);
animationSet.playTogether(
ObjectAnimator.ofFloat(vw, "rotationY", 45),
ObjectAnimator.ofFloat(vw, "scaleY", 0.8f),
ObjectAnimator.ofFloat(vw, "x", 0)
);
animationSet.setDuration(600);
animationSet.setInterpolator(new DecelerateInterpolator(1.5f));
animationSet.start();
animationSet.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
// vw.setLayerType(View.LAYER_TYPE_NONE,null);
animationSet.start();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
vw.setX(getResources().getDisplayMetrics().widthPixels / 2);
vw.setRotationY(-45);
vw.setScaleY(0.7f);
}
});
}
}
}
Hope you would have added corresponding specific resolution images for different device resolutions.
If using same image in different screen resolutions, those devices with smaller resolutions these kind of lagging may happen.
To generate these images, you should start with your raw resource in vector format and generate the images for each density using the following size scale:
xhdpi: 2.0
hdpi: 1.5
mdpi: 1.0 (baseline)
ldpi: 0.75
More over go through the below suggestions from developer.android wrt screen density
ldpi Resources for low-density (ldpi) screens (~120dpi).
mdpi Resources for medium-density (mdpi) screens (~160dpi). (This is the baseline density.)
hdpi Resources for high-density (hdpi) screens (~240dpi).
xhdpi Resources for extra-high-density (xhdpi) screens (~320dpi).
xxhdpi Resources for extra-extra-high-density (xxhdpi) screens (~480dpi).
xxxhdpi Resources for extra-extra-extra-high-density (xxxhdpi) uses (~640dpi). Use this for the launcher icon only, see note above.
nodpi Resources for all densities. These are density-independent resources. The system does not scale resources tagged with this qualifier, regardless of the current screen's density.
tvdpi Resources for screens somewhere between mdpi and hdpi; approximately 213dpi. This is not considered a "primary" density group. It is mostly intended for televisions and most apps shouldn't need it—providing mdpi and hdpi resources is sufficient for most apps and the system will scale them as appropriate.
If you are adding 10 different images, try using animation list with seperate attributes for each image animation.
By understanding your code in question, I have made some slight alternation such that the reverse animation happens and sequence happens correctly. Hope this helps!!!...
Code inside oncreate method
frame = new FrameLayout(this);
frame.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 700));
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(R.drawable.image1);
arrayList.add(R.drawable.image2);
arrayList.add(R.drawable.image3);
arrayList.add(R.drawable.image4);
arrayList.add(R.drawable.image5);
arrayList.add(R.drawable.image6);
arrayList.add(R.drawable.image7);
arrayList.add(R.drawable.image8);
arrayList.add(R.drawable.image9);
arrayList.add(R.drawable.image10);
//arrayList.add(R.drawable.image11);
for(int i=0;i<arrayList.size();i++){
ImageView imgvw = new ImageView(this);
imgvw.setImageResource(arrayList.get(i));
imgvw.setLayoutParams(new ActionBar.LayoutParams(250, 400));
imgvw.setScaleType(ImageView.ScaleType.FIT_XY);
imgvw.setX(this.getResources().getDisplayMetrics().widthPixels / 2);
imgvw.setRotationY(-45);
imgvw.setScaleY(0.7f);
frame.addView(imgvw);
}
setContentView(frame);
Code for animations:
private int count=0;
private int repeatMode=2;
private int repeatCount=1;
private int duration=300;
private void doAnimation(final FrameLayout frame){
if (count>=frame.getChildCount()){
count=0;
return;
}
ImageView targetView = (ImageView) frame.getChildAt(count++);//frame.getChildCount()-1-count++);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(targetView, "rotationY", 45);
animator1.setRepeatCount(repeatCount);
animator1.setRepeatMode(repeatMode);
animator1.setDuration(duration);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(targetView, "scaleY", 0.8f);
animator2.setRepeatCount(repeatCount);
animator2.setRepeatMode(repeatMode);
animator2.setDuration(duration);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(targetView, "x", 0f);
animator3.setRepeatCount(repeatCount);
animator3.setRepeatMode(repeatMode);
animator3.setDuration(duration);
//sequencial animation
AnimatorSet set = new AnimatorSet();
set.playTogether(animator1, animator2, animator3);
set.start();
set.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
doAnimation(frame);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return onTouchEvent(event);
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
doAnimation(frame);
return true;
}
}
return false;
}

Android Animation Bug wont let me click after animation move to the right

So, here's my problem? I have it where it will slide out and then it will stay in that position and then you should be able to click on it again and it will slide back to the original location.
But instead I have to click on where it FIRST was at. Even when it's slided out. I want to make it after the animation I can click it any where, that it slided out.
Here's my code
public void sideBar()
{
ImageView sidebar = (ImageView)findViewById(R.id.sidebar);
if(out == 0)
{
mSlideInRight = AnimationUtils.loadAnimation(this, R.anim.slide_in_right);
mSlideInRight.setFillAfter(true);
sidebar.startAnimation(mSlideInRight);
out= 1;
}
else if(out == 1)
{
mSlideInLeft = AnimationUtils.loadAnimation(this, R.anim.slide_in_left);
sidebar.startAnimation(mSlideInLeft);
out=0;
}
}
and this is the code for when you click on it
public void onClick(View v) {
ImageView sidebar = (ImageView)findViewById(R.id.sidebar);
ImageView popup = (ImageView)findViewById(R.id.popup);
ImageView popup2 = (ImageView)findViewById(R.id.popup2);
switch(v.getId())
{
case R.id.sidebar:
sideBar();
break;
}
}
Animations only affect how your sidebar ImageView is drawn, not its actual position. After your animation completes, you should update the sidebar's layout parameters to your desired position. You can do this in an AnimationListener if you want the position to be updated as the animation runs or after it is complete, or right after your call to startAnimation if you want the position change to happen as the animation starts.
mSlideInRight.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation anim) {};
#Override
public void onAnimationRepeat(Animation anim) {};
#Override
public void onAnimationEnd(Animation anim) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(...);
// Set properties of layoutParams here
sidebar.setLayoutParams(layoutParams);
};
});
Replace RelativeLayout with whatever layout your ImageView is a child of.

Categories