How to implement circular to check / tick animation in Android? - android

I am trying to animate a custom view
I implemented a circular progress bar custom view, exactly similar to this one
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (totalCount == 0 || progressCount == 0)
{
// No progress, set a different background color and draw a arc and set a icon at the center
progressBackgroundPaint.setColor(outerNoProgressBackgroundColor);
canvas.drawArc(viewRect, PROGRESS_START_DEGREE, PROGRESS_END_DEGREE, false, progressBackgroundPaint);
drawInnerBackgroundWithState(canvas, ProgressState.NO_PROGRESS);
drawCenterIconWithState(centerIconEmptyBitmap, canvas, ProgressState.NO_PROGRESS);
}
else
{
// Change the color first for a progress bar
progressBackgroundPaint.setColor(outerProgressBackgroundColor);
canvas.drawArc(viewRect, PROGRESS_START_DEGREE, PROGRESS_END_DEGREE, false, progressBackgroundPaint);
// Then draw an arc on top of it marking progress
canvas.drawArc(viewRect, PROGRESS_START_DEGREE, getAngle(), false, progressPaint);
// set inner background color and tint center icon with state-appropriate color and draw it in the center
// of the progress circle
if (progressCount < totalCount)
{
canvas.drawOval(viewRect, innerBackgroundPaint);
drawCenterIconWithState(centerIconProgressBitmap, canvas, ProgressState.IN_PROGRESS); // draw bit map function
}
else
{
canvas.drawOval(viewRect, completeBackgroundPaint);
drawCenterIconWithState(centerIconProgressBitmap, canvas, ProgressState.COMPLETE); // draw bitmap function
}
}
}
I call this onDraw using ValueAnimator
public void drawProgressAnimation(float progress)
{
ValueAnimator animator;
if (animator != null)
animator.cancel();
animator = ValueAnimator.ofFloat(0f, progress);
animator.setDuration(600);
progressAnimator.addUpdateListener(animation ->
{
float progress1 = (float)animation.getAnimatedValue();
setProgress(progress1, true);
});
progressAnimator.start();
}
And invalidating in setProgress method which calls onDraw.
But, I need to achieve something like this,
I tried using Animator set along with ValueAnimator, but it didn't work. I tried to call onDraw with sequentially by setting a variable inside the lambda but in vain.
I am not sure whether can do this using AnimatedVectorDrawable. Can somebody help?

I used this repo as reference and implemented what i want. Instead of using random counter to set the speed. I used value animation and used the progress value for every frame to set the arc rate. And used the counter to draw circles that would grow from Zero to radius.
if (isComplete) {
canvas.drawArc(mRect, START_DEGREE, getSweepAngle(), false, progressPaint);
// animate circle progress when arc reaches partial sweep angle
if (getSweepAngle() >= partialSweepAngle)
{
innerBackground.setColor(innerCompleteColor);
circleCounter += circleProgressRate;
if (circleCounter <= radius)
canvas.drawCircle(mRect.centerX(), mRect.centerY(), circleCounter,
innerBackground);
else
canvas.drawCircle(mRect.centerX(), mRect.centerY(), radius, innerBackground);
}
if (circleCounter >= radius)
// draw the icon
}

Related

Can you draw multiple Bitmaps on a single View Method?

currently I am trying to make an animation where some fish move around. I have successfully add one fish and made it animate using canvas and Bitmap. But currently I am trying to add a background that I made in Photoshop and whenever I add it in as a bitmap and draw it to the canvas no background shows up and the fish starts to lag across the screen. I was wondering if I needed to make a new View class and draw on a different canvas or if I could use the same one? Thank you for the help!
Here is the code in case you guys are interested:
public class Fish extends View {
Bitmap bitmap;
float x, y;
public Fish(Context context) {
super(context);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fish1);
x = 0;
y = 0;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, x, y, null);
if (x < canvas.getWidth())
{
x += 7;
}else{
x = 0;
}
invalidate();
}
}
You can draw as many bitmaps as you like. Each will overlay the prior. Thus, draw your background first, then draw your other images. Be sure that in your main images, you use transparent pixels where you want the background to show through.
In your code, don't call Invalidate() - that's what causes Android to call onDraw() and should only be called from somewhere else when some data has changed and needs to be redrawn.
You can do something like this, where theView is the view containing your animation:
In your activity, put this code in onCreate()
myAnimation();
Then
private void myAnimation()
{
int millis = 50; // milliseconds between displaying frames
theView.postDelayed (new Runnable ()
{
#Override public void run()
{
theView.invalidate();
myAnimation(); // you can add a conditional here to stop the animation
}
}, millis);
}

Animating a point on Android canvas

Every time onDraw() is called I am drawing a series of points on the canvas. How do I animate one point so that it changes colour or fades in and out? So essentially goes from orange->red and back or opaque->transparent and back?
I am doing the following:
public void onDraw(Canvas canvas) {
drawDots();
}
private void drawDots() {
canvas.drawCircle(xcoord, ycoord, 20, getPaintObj(param));
}
private Paint getPaintObj(int param) {
if (param % 2 == 0) {
ObjectAnimator colorFade = ObjectAnimator.ofObject(paintObj, "color", new ArgbEvaluator(), 0xff00ff00, 0xffff0000, 0xff0000ff);
colorFade.setDuration(2000);
colorFade.setInterpolator(new LinearInterpolator());
colorFade.setRepeatCount(ValueAnimator.INFINITE);
colorFade.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
invalidate();
}
});
colorFade.start();
return paintObj;
} else {
return otherPaintObj;
}
}
The problem is that it doesn't animate. It sticks to the first colour (0xff00ff00). What am I doing wrong?
ObjectAnimator doesn't work very well with the canvas as I was trying to animate a circle drawn on the canvas itself. Works better for actual objects (TextView, ImageView, etc.) rather than points drawn on canvases.
The solution I used is redrawing the canvas every once in a while and modifying the radius of the circle drawn to simulate an animation.

Android - Draw only parts which are visibile

I've created a custom view that draws via onDraw() overridden method some shapes. This view is scrollable so every time user navigates in the Activity, onDraw() method has called and all the canvas is drawn. In the onDraw() method there are some statements making some hard calculations so my intent is to draw, when user scrolls the view, only the part that were invisible and now, for the scrolling, they are visible.
How can I draw only the part that are visible in my custom view?
#Override
protected void onDraw(Canvas sysCanvas)
{
super.onDraw(sysCanvas);
if(!giaDisegnato) //If I've never drawn before, let's draw
{
if(!listaTl.isEmpty())
{
toDisk= Bitmap.createBitmap(w,h,Bitmap.Config.RGB );
canvas = new Canvas(toDisk);
canvas.drawColor(Color.WHITE);
p.setStyle(Paint.Style.FILL_AND_STROKE);
p.setAntiAlias(true);
p.setStrokeWidth(1);
for(TimelineGrafica t : listaTl)
{
if(inseritaLaPrima)
y = ySalvata + this.yAngoloDestroGiu + DISTANZA_FRA_TIMELINE;
p.setColor(t.getColor());
disegnaPunta(canvas,p,t);
disegnaRettangolo(canvas,p,t);
disegnaGrain(canvas,p,t);
disegnaFatti(canvas,p,t);
inseritaLaPrima = true;
}
y = ySalvata;
inseritaLaPrima = false;
sysCanvas.drawBitmap(toDisk,0,0,p);
}
requestLayout();
giaDisegnato = true;
}
else
{
//Here I've already drawn. So I'd like to redrawn the part of the view that now
//is visible.
sysCanvas.drawBitmap(toDisk,0,0,p);
}
}
Due to the language, it is difficult to know precisely what you are doing.
However, you can check the canvas to know whether you should draw or not using quickReject.
Example:
if(canvas.quickReject(boundingRect, EdgeType.BW)) {
return;
}

How can add selector to custom view?

I create a custom view and add selector to my custom view. Selector works, but background stretches full width and height. And background must displays what I draw.
Full code http://pastebin.com/dmF6DiP8
#Override
protected void onDraw(Canvas canvas)
{
Log.d(TAG, "init onDraw");
if (canvas != null && mDrawable != null)
{
mDrawable.setState(getDrawableState());
canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mBackgroundPaint);
mDrawable.draw(canvas);
}
}
Now normal state:
Now pressed state:
Must be:
What you are doing is drawing the circle first and then drawing a your drawable on top of it. The circle is underneath the drawable so you can't see it. The circle is also being drawn with the same color every time and it is not taking it from the drawable at all.
What you need to do is apply the color to the circle depending on the selector state and remove the call to draw the drawable on the canvas.

Rotating a view in Android

I have a button that I want to put on a 45 degree angle. For some reason I can't get this to work.
Can someone please provide the code to accomplish this?
API 11 added a setRotation() method to all views.
You could create an animation and apply it to your button view. For example:
// Locate view
ImageView diskView = (ImageView) findViewById(R.id.imageView3);
// Create an animation instance
Animation an = new RotateAnimation(0.0f, 360.0f, pivotX, pivotY);
// Set the animation's parameters
an.setDuration(10000); // duration in ms
an.setRepeatCount(0); // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE); // reverses each repeat
an.setFillAfter(true); // keep rotation after animation
// Aply animation to image view
diskView.setAnimation(an);
Extend the TextView class and override the onDraw() method. Make sure the parent view is large enough to handle the rotated button without clipping it.
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.rotate(45,<appropriate x pivot value>,<appropriate y pivot value>);
super.onDraw(canvas);
canvas.restore();
}
I just used the simple line in my code and it works :
myCusstomView.setRotation(45);
Hope it works for you.
One line in XML
<View
android:rotation="45"
... />
Applying a rotation animation (without duration, thus no animation effect) is a simpler solution than either calling View.setRotation() or override View.onDraw method.
// substitude deltaDegrees for whatever you want
RotateAnimation rotate = new RotateAnimation(0f, deltaDegrees,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// prevents View from restoring to original direction.
rotate.setFillAfter(true);
someButton.startAnimation(rotate);
Rotating view with rotate() will not affect your view's measured size. As result, rotated view be clipped or not fit into the parent layout. This library fixes it though:
https://github.com/rongi/rotate-layout
Joininig #Rudi's and #Pete's answers. I have created an RotateAnimation that keeps buttons functionality also after rotation.
setRotation() method preserves buttons functionality.
Code Sample:
Animation an = new RotateAnimation(0.0f, 180.0f, mainLayout.getWidth()/2, mainLayout.getHeight()/2);
an.setDuration(1000);
an.setRepeatCount(0);
an.setFillAfter(false); // DO NOT keep rotation after animation
an.setFillEnabled(true); // Make smooth ending of Animation
an.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {}
#Override
public void onAnimationRepeat(Animation animation) {}
#Override
public void onAnimationEnd(Animation animation) {
mainLayout.setRotation(180.0f); // Make instant rotation when Animation is finished
}
});
mainLayout.startAnimation(an);
mainLayout is a (LinearLayout) field
As mentioned before, the easiest way it to use rotation available since API 11:
android:rotation="90" // in XML layout
view.rotation = 90f // programatically
You can also change pivot of rotation, which is by default set to center of the view. This needs to be changed programatically:
// top left
view.pivotX = 0f
view.pivotY = 0f
// bottom right
view.pivotX = width.toFloat()
view.pivotY = height.toFloat()
...
In Activity's onCreate() or Fragment's onCreateView(...) width and height are equal to 0, because the view wasn't measured yet. You can access it simply by using doOnPreDraw extension from Android KTX, i.e.:
view.apply {
doOnPreDraw {
pivotX = width.toFloat()
pivotY = height.toFloat()
}
}
if you wish to make it dynamically with an animation:
view.animate()
.rotation(180)
.start();
THATS IT
#Ichorus's answer is correct for views, but if you want to draw rotated rectangles or text, you can do the following in your onDraw (or onDispatchDraw) callback for your view:
(note that theta is the angle from the x axis of the desired rotation, pivot is the Point that represents the point around which we want the rectangle to rotate, and horizontalRect is the rect's position "before" it was rotated)
canvas.save();
canvas.rotate(theta, pivot.x, pivot.y);
canvas.drawRect(horizontalRect, paint);
canvas.restore();
fun rotateArrow(view: View): Boolean {
return if (view.rotation == 0F) {
view.animate().setDuration(200).rotation(180F)
true
} else {
view.animate().setDuration(200).rotation(0F)
false
}
}
That's simple,
in Java
your_component.setRotation(15);
or
your_component.setRotation(295.18f);
in XML
<Button android:rotation="15" />

Categories

Resources