Animate view width - android

Is it possible to control the width animation for a view that has wrap_content? Right now I have a TextView with a barrier to its right side. I have another background View that aligns to this barrier. When the TextView changes text it just flashes to the new width but I want to animate the width with an animator where I can control the timing, interpolator etc. I know that I can set something like animateLayoutChanges but the effect is not customisable.

You can use ObjectAnimator along with AnimatorSet for the animation.
Here is an extension in kotlin for the view on which you want to apply the animation.
inline fun View.animateWidth(duration: Long = 1000, startDelay: Long = 0) {
val scaleX = ObjectAnimator.ofFloat(this, "scaleX", /*Change width*/)
//val scaleY = ObjectAnimator.ofFloat(this, "scaleY", /*Change width*/)
val set = AnimatorSet()
//set.playTogether(scaleX, scaleY)
set.playTogether(scaleX)
set.duration = duration
set.startDelay = startDelay
set.interpolator = LinearInterpolator()
set.addAnimatorListener(
onStart = { this.isClickable = false },
onEnd = { this.isClickable = true }
)
set.start()
}

Related

Make gradient start further away from end of view with GradientDrawable

I am trying to set the background of a view to have a gradient whose color is generated from the Palette api
The gradient will go from a solid and fade out but I want the solid portion to take up a majority of the background. Right now it starts solid and then gradually fades out over the view width, I want it to where it will start fading out from around the center of the view width.
Here is what I do
Palette.from(resource!!.toBitmap()).generate {
if (it != null) {
val paletteColor = it.getDarkVibrantColor("#000000".toColorInt())
val gradientDrawable = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT,
intArrayOf(colorWithAlpha(paletteColor, 0f), colorWithAlpha(paletteColor, 1.0f))
)
gradientDrawable.cornerRadius = 0f
_contentTextBackground.background = gradientDrawable
}
}
Is there a way to set the gradient to start further away from the end of the view?
You can do this via XML to change centerX and centerY attributes of GradientDrawable. But sadly, with GradientDrawable, this is not possible programmatically as discussed on Google Issue Tracker.
For APIs below 29, try using a ShapeDrawable with a LinearGradient Shader as a background. This will give you fine-level control over the transition of the colors.
For API 29+, GradientDrawable allows for similar fine-level control over color transitions with setColors().
private fun getBackgroundGradient(width: Int): Drawable {
val colors = intArrayOf(Color.BLUE and 0x00FFFFFF, Color.BLUE, Color.BLUE)
val offsets = floatArrayOf(0.0f, 0.7f, 1.0f)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Oddly, there is no constructor that accepts the offsets.
GradientDrawable().apply {
orientation = GradientDrawable.Orientation.LEFT_RIGHT
setColors(colors, offsets)
}
} else {
val shader: Shader = LinearGradient(
0f, 0f, width.toFloat(), 1f, colors, offsets, Shader.TileMode.CLAMP
)
val shape = ShapeDrawable(RectShape())
shape.paint.shader = shader
shape
}
}
I used 0.7f as the "center" to make a better (IMO) color transition near the 50% mark, but that value could easily be 0.5f or any other value between 0f and 1.0f.
In the following image, the horizontal bar is the width of the screen and is just a View. The vertical red line splits the screen into two to mark the transition.
As stated by #Abdul Mateen you cannot change the attributes in Graient Drawable. This is only possible if you do it via XML. But there is a workaround:
You could theoretically split your background view in half. Then you have two views on which you could then change the color, based on the Palette Library. One view would then be the solid color and the other one would have to have the Gradient in the Background. If you configure that correctly, you should have a perfect gradient from the middle.
Try extending GradientDrawable and set fades and colour as per you needs.
package com.example.testApp;
import android.app.Activity;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.view.View;
public class TetApp extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
View v = findViewById(R.id.btn);
v.setBackgroundDrawable( new DrawableGradient(new int[] { 0xff666666, 0xff111111, 0xffffffff }, 0).SetTransparency(10));
}
public class DrawableGradient extends GradientDrawable {
DrawableGradient(int[] colors, int cornerRadius) {
super(GradientDrawable.Orientation.TOP_BOTTOM, colors);
try {
this.setShape(GradientDrawable.RECTANGLE);
this.setGradientType(GradientDrawable.LINEAR_GRADIENT);
this.setCornerRadius(cornerRadius);
} catch (Exception e) {
e.printStackTrace();
}
}
public DrawableGradient SetTransparency(int transparencyPercent) {
this.setAlpha(255 - ((255 * transparencyPercent) / 100));
return this;
}
}
}
reference from this: https://stackoverflow.com/a/5241693/14964046
Just add the solid color in array one more
like below
val gradientDrawable = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT,
intArrayOf(colorWithAlpha(paletteColor, 0f), colorWithAlpha(paletteColor, 1.0f), colorWithAlpha(paletteColor, 1.0f))
)
Its not elegant, but works :-D
What I ended up doing was what Mike suggested in the comments was to just add more of the solid color the the color array of the GradientDrawable
so it looked like this
val gradientDrawable = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT,
intArrayOf(colorWithAlpha(paletteColor, 0f), colorWithAlpha(paletteColor, 1.0f), colorWithAlpha(paletteColor, 1.0f), colorWithAlpha(paletteColor, 1.0f))
)

Buttons in a RelativeLayout disappear after changing LayoutParams

I have five buttons in a Relative layout, if I try to change their height dynamically, some buttons disappear.
How buttons are looking before clicking
Buttons after clicking
val w = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45f, resources.displayMetrics)
btn_ran.setOnClickListener {
btn_1.layoutParams = RelativeLayout.LayoutParams(w.toInt(), 700)
btn_3.layoutParams = RelativeLayout.LayoutParams(w.toInt(), 700)
btn_4.layoutParams = RelativeLayout.LayoutParams(w.toInt(), 700)
btn_2.layoutParams = RelativeLayout.LayoutParams(w.toInt(), 700)
btn_5.layoutParams = RelativeLayout.LayoutParams(w.toInt(), 700)
}
All I want to do is to change all of the button's height randomly on the click of a button.
If You only want to change height to random value You can do it in this way:
button1.setOnClickListener {
val h = (100..500).random() //random integer between 100 and 500
button1.layoutParams.height = h
button2.layoutParams.height = h
button1.requestLayout() //refresh layout
}
I suspect that you are losing the relative positioning attributes when you create new LayoutParams objects. Remember that attributes like layout_toEndOf are part of the layout params.
Try this instead:
val w = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45f, resources.displayMetrics)
btn_ran.setOnClickListener {
btn_1.updateLayoutParams { this.width = w }
btn_3.updateLayoutParams { this.width = w }
btn_4.updateLayoutParams { this.width = w }
btn_2.updateLayoutParams { this.width = w }
btn_5.updateLayoutParams { this.width = w }
}
This makes use of the updateLayoutParams extension function that is part of the Core KTX library. It will keep everything about the LayoutParams the same, but also allow you to modify the width.
If you can't use Core KTX, then you can be a little more verbose. Replace each of those calls with something like this:
val params1 = btn_1.layoutParams
params1.width = w
btn_1.layoutParams = params1

android animation move view to another view

I have two views in different layouts I want to move one to another. What's wrong with my code? Y animation plays wrong. First view is located in fragment's layout, second in status bar
...
int p1[] = new int[2];
int p2[] = new int[2];
viewOne.getLocationInWindow(p1);
viewTwo.getLocationInWindow(p2);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.play(ObjectAnimator.ofFloat(expandedImageView, "X", p1[0], p2[0] - p1[0]))
.with(ObjectAnimator.ofFloat(expandedImageView, "Y", p1[1], p2[1] - p1[1]))
.with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale))
.with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale));
I have a another solution for you:(move viewOne to viewTwo)
TranslateAnimation animation = new TranslateAnimation(0, viewTwo.getX()-viewOne.getX(),0 , viewTwo.getY()-viewOne.getY());
animation.setRepeatMode(0);
animation.setDuration(3000);
animation.setFillAfter(true);
viewOne.startAnimation(animation);
Try this
private fun moveView(viewToBeMoved: View, targetView: View) {
val targetX: Float =
targetView.x + targetView.width / 2 - viewToBeMoved.width / 2
val targetY: Float =
targetView.y + targetView.height / 2 - viewToBeMoved.height / 2
viewToBeMoved.animate()
.x(targetX)
.y(targetY)
.setDuration(2000)
.withEndAction {
targetView.visibility = View.GONE
}
.start()
}

ValueAnimator.REVERSE create for REVERSE different behavior

I have simple animation that moves the object in Y axis. I want change the behavior for the REVERSE.
//My Code:
ImageView iv = ... //my view
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "y", 300);
oa.setDuration(100);
oa.setRepeatCount(1);
oa.setRepeatMode(ValueAnimator.REVERSE);
oa.start();
You cannot change the behavior of the reverse mode. Instead, you will need to create an AnimatorSet and play them sequentially.
ImageView iv = ... //my view
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "y", 300);
oa.setDuration(100);
ObjectAnimator oa2 = ObjectAnimator.ofFloat(/* code here */)
// Add any other code for oa2
AnimatorSet set = new AnimatorSet();
set.playSequentially(oa, oa2);
set.start()

How do you animate a change in a view's padding?

I want to animate the change in the padding of a view. The resting place of the translation animation is the same as the padding I want to apply.
TranslateAnimation moveleft = new TranslateAnimation(Animation.ABSOLUTE, 0.0f,
Animation.ABSOLUTE, PADDING, Animation.ABSOLUTE,
0.0f, Animation.ABSOLUTE, 0.0f);
moveLeft.setDuration(500);
moveLeft.setFillAfter(true);
This starts the view's animation then sets the padding. This doesn't exactly work because it cause a graphical glitch.
v.startAnimation(moveleft);
v.setPadding(PADDING, 0, 0,0);
Use ValueAnimator, its really simple and unclutter
say,
we have to change right padding to _20dp where as left, top and bottom padding are _6dp, _6dp and 0 respectively.
ofInt() is varagrs type. the value we have to animate is pass in it as KeyValue pair (arg1 = current value, arg2 = target value,............)
Here we go,
ValueAnimator animator = ValueAnimator.ofInt(view.getPaddingRight(), _20dp);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator){
view.setPadding(_6dp, _6dp, (Integer) valueAnimator.getAnimatedValue(), 0);
}
});
animator.setDuration(200);
animator.start();
Instead of setting your padding right away, why not try an animation listener to set the padding after the animation has completed?
v.setAnimationListener(new Animation.AnimationListener() {
...
#Override
public void onAnimationEnd(){
v.setPadding(PADDING, 0, 0,0);
}
...
});
v.startAnimation(moveleft);
Here is how to animate setPadding in Kotlin:
private fun setPaddingWithAnimation() {
val paddingDp: Int = 20
val density: Float = requireActivity().getResources().getDisplayMetrics().density
val paddingPixel: Int = (paddingDp * density).toInt()
val animator = ValueAnimator.ofInt(recyclerItems.paddingRight, paddingPixel)
animator.addUpdateListener { valueAnimator -> binding.recyclerHealthTips.recyclerHealthTips.setPadding(
paddingPixel, 0, valueAnimator.animatedValue as Int, 0) }
animator.duration = 500
animator.start()
}

Categories

Resources