I am drawing a scaled bitmap onto a canvas and would like to fade my image out at a specified time.
Basically, when my character image goes over a certain section of the canvas, I require the character image to slowly fade away (3 seconds), before the page automatically re-directs to the next java class.
Currently, my image simply redirects to the new java class, please see below some code to how I am creating my image.
Resources res = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, res.getDisplayMetrics());
imgSpacing = (int) px / 2;
int size = (int) ((PhoneWidth / 5) - px);
chrImg = BitmapFactory.decodeResource(getResources(), R.drawable.character);
chrImg = Bitmap.createScaledBitmap(chrImg, size, size, true);
Then within the canvas onDraw:
if(indexX == mazeFinishX && indexY == mazeFinishY)
{
canvas.drawBitmap(finish, j * totalCellWidth, i * totalCellHeight, null);
// As soon as the character moves over this square they are automatically re-directed to new page
// This is where I want to fade the character image out before the re-direct
}
I have looked online, however cannot quite work out how to get the fading to work for my drawable image fetched from my games resources drawable folder. Thanks
If you think there's a possibility you will want to change the fade animation down the road, such as scaling and/or rotating, then you should go with an animation XML.
But for just a quick bitmap fade, you can repeatedly post delayed invalidation messages. You probably want to limit your invalidated area to just where your character bitmap is:
private static final int FADE_MILLISECONDS = 3000; // 3 second fade effect
private static final int FADE_STEP = 120; // 120ms refresh
// Calculate our alpha step from our fade parameters
private static final int ALPHA_STEP = 255 / (FADE_MILLISECONDS / FADE_STEP);
// Initializes the alpha to 255
private Paint alphaPaint = new Paint();
// Need to keep track of the current alpha value
private int currentAlpha = 255;
@Override
protected void onDraw(Canvas canvas) {
...
if(indexX == mazeFinishX && indexY == mazeFinishY) {
// Drawing your wormhole?
int x = j * totalCellWidth;
int y = i * totalCellHeight;
canvas.drawBitmap(finish, x, y, null);
if (currentAlpha > 0) {
// Draw your character at the current alpha value
canvas.drawBitmap(chrImg, x, y, alphaPaint);
// Update your alpha by a step
alphaPaint.setAlpha(currentAlpha);
currentAlpha -= ALPHA_STEP;
// Assuming you hold on to the size from your createScaledBitmap call
postInvalidateDelayed(FADE_STEP, x, y, x + size, y + size);
} else {
// No character draw, just reset your alpha paint
currentAlpha = 255;
alphaPaint.setAlpha(currentAlpha);
// Now do your redirect
}
}
...
}
I'd recommend putting the constants FADE_MILLISECONDS and FADE_STEP into res/integers.xml just so they aren't hard-coded.
The best way for me to explain this is provide a full custom view example that you can pull the pieces from that you need. Below is a view that accomplishes this.
public class CharacterView extends View {
private Paint mCharacterPaint;
private Bitmap mCharacterBitmap;
private Transformation mTransformation;
private AlphaAnimation mFadeOut;
public CharacterView(Context context) {
super(context);
init(context);
}
public CharacterView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CharacterView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
//This would be your character image instead
mCharacterBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
//We need a paint to efficiently modify the alpha
mCharacterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//This is needed to house the current value during the animation
mTransformation = new Transformation();
//Construct an animation that will do all the timing math for you
mFadeOut = new AlphaAnimation(1f, 0f);
mFadeOut.setDuration(500);
//Use a listener to trigger the end action
mFadeOut.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) { }
@Override
public void onAnimationEnd(Animation animation) {
//Trigger your action to change screens here.
}
@Override
public void onAnimationRepeat(Animation animation) { }
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//In this example, touching the view triggers the animation
// your code would run this sequence when the character reaches the
// appropriate coordinates (P.S. I would not advocate putting this code
// inside of onDraw()
mFadeOut.start();
mFadeOut.getTransformation(System.currentTimeMillis(), mTransformation);
invalidate();
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//...existing drawing code...
canvas.drawBitmap(mCharacterBitmap, 0, 0, mCharacterPaint);
if (mFadeOut.hasStarted() && !mFadeOut.hasEnded()) {
mFadeOut.getTransformation(System.currentTimeMillis(), mTransformation);
//Keep drawing until we are done
mCharacterPaint.setAlpha((int)(255 * mTransformation.getAlpha()));
invalidate();
} else {
//Reset the alpha if animation is canceled
mCharacterPaint.setAlpha(255);
}
}
}
The most efficient way to dynamically modify the transparency of what you draw is to apply it using a Paint
drawing to your Canvas
. You will need to continually invalidate()
the view with each frame transition to get the animation to display until the character is gone. The example uses an Animation
object and a Transformation
object to handle all of the timing math to make the animation actually look good.
The gist here is that you start the animation via some external trigger (I wouldn't recommend doing the check for when to fade out in onDraw()
, probably better in the place where those character locations are updated. In my example, for simplicity sake, I started this when the view to touched.
This trigger starts the animation and gets the initial transformation value, then invalidate()
triggers a new onDraw()
. While the animation is running, onDraw()
is repeated due to the invalidate()
and each iteration the alpha on the paint decreases slightly.
When the animation is finished, it will call the AnimationListener
for you, so you can trigger the screen transition inside of onAnimationEnd()
.
if(indexX == mazeFinishX && indexY == mazeFinishY)
{
canvas.drawBitmap(finish, j * totalCellWidth, i * totalCellHeight, null);
// As soon as the character moves over this square they are automtically re-directs to new page
new CountDownTimer(0500, 1000) {
@Override
public void onTick(long millisUntilFinished) {}
@Override
public void onFinish() {
Paint paint = new Paint();
paint.setAlpha(25);
canvas.drawBitmap(chrImg, 0, 0, paint);
}
}.start();
new CountDownTimer(0500, 1000) {
@Override
public void onTick(long millisUntilFinished) {}
@Override
public void onFinish() {
Paint paint = new Paint();
paint.setAlpha(45);
canvas.drawBitmap(chrImg, 0, 0, paint); }
}.start();
new CountDownTimer(0500, 1000) {
@Override
public void onTick(long millisUntilFinished) {}
@Override
public void onFinish() {
Paint paint = new Paint();
paint.setAlpha(70);
canvas.drawBitmap(chrImg, 0, 0, paint); }
}.start();
new CountDownTimer(0500, 1000) {
@Override
public void onTick(long millisUntilFinished) {}
@Override
public void onFinish() {
Paint paint = new Paint();
paint.setAlpha(100);
canvas.drawBitmap(chrImg, 0, 0, paint); }
}.start();
// This is where I want to fade the character image out before the re-direct
new CountDownTimer(3000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
// TODO Auto-generated method stub
}
@Override
public void onFinish() {
// TODO Auto-generated method stub
Intent i = new Intent(currentclass.this, nextClass.class);
startActivity(i);
}
}.start();
}
this will make your bitmap becomes transparent ( the alpha will be 100 ) .. and then redirect to the next class after 3 seconds.
Update: for the bitmap you can't use .setAlpha so we must use a paint, so we created a new paint each time then set the bitmap with the paint. but if you have an imageView you can use .setalpha
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With