I have the following oval GradientDrawable created programmatically to display a circle:
GradientDrawable drawable = new GradientDrawable();
drawable.setColor(Color.TRANSPARENT);
drawable.setShape(GradientDrawable.OVAL);
drawable.setStroke(wheelStrokeWidth, Color.parseColor(WHEEL_STROKE_COLOR));
drawable.setSize(2*wheelRadius+wheelStrokeWidth,2*wheelRadius+wheelStrokeWidth);
ImageView iv = new ImageView(activity);
iv.setImageDrawable(drawable);
I would like to animate the drawing of this circle, to make it draw progressively. First it's just an arc of 0°, then 1°, then progressively it goes to a full 360° circle (sorry if I'm not clear, have trouble to express it in English).
Anyone has an idea on how to do that?
Thank you!
This is not a perfect solution. but this may give you some Idea.
public class MyView extends View {
private Bitmap mBitmap;
private Paint mPaint;
private RectF mOval;
private float mAngle = 135;
private Paint mTextPaint;
public MyView(Context context) {
super(context);
// use your bitmap insted of R.drawable.ic_launcher
mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.image1);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOval = new RectF();
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(48);
mTextPaint.setTextAlign(Align.CENTER);
mTextPaint.setColor(0xffffaa00);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Matrix matrix = new Matrix();
RectF src = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
RectF dst = new RectF(0, 0, w, h);
matrix.setRectToRect(src, dst, ScaleToFit.CENTER);
Shader shader = new BitmapShader(mBitmap, TileMode.CLAMP,
TileMode.CLAMP);
shader.setLocalMatrix(matrix);
mPaint.setShader(shader);
matrix.mapRect(mOval, src);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xff0000aa);
canvas.drawArc(mOval, -90, mAngle, true, mPaint);
canvas.drawText("click me", getWidth() / 2, getHeight() / 2,
mTextPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float w2 = getWidth() / 2f;
float h2 = getHeight() / 2f;
mAngle = (float) Math.toDegrees(Math.atan2(event.getY() - h2,
event.getX() - w2));
mAngle += 90 + 360;
mAngle %= 360;
if (mAngle == 0) {
mAngle = 360;
}
invalidate();
return true;
}
}
And Credit goes to some other user. He posted this solution at some where (I forgot) and I used that...
By following the idea on this post: Android custom Animation for an ArcShape, I changed my GradientDrawable to a custom View, and made this class to draw progressively an arc:
/**
* An arc with an animation to draw it progressively
*
* Usage:
*
* AnimatedArc aa = new AnimatedArc(...)
* AnimatedArc.ProgressiveDrawing animation = aa.getAnimation(...)
* aa.startAnimation(animation)
*/
public class AnimatedArc extends View {
int delta, startAngle, sweepAngle, currentSweepAngle;
Paint p = new Paint();
Rect rect = new Rect();
RectF rectF = new RectF();
public AnimatedArc(Context context, int strokeWidth, String color, int startAngle, int sweepAngle, boolean doNotAnimate) {
super(context);
p.setAntiAlias(true);
p.setColor(Color.parseColor(color));
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(strokeWidth);
delta = strokeWidth / 2;
this.startAngle = startAngle;
this.sweepAngle = sweepAngle;
this.currentSweepAngle = doNotAnimate ? sweepAngle : 0;
}
public ProgressiveDrawing getAnimation(int duration) {
return new ProgressiveDrawing(duration);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.getClipBounds(rect);
rectF.set(rect.left+delta, rect.top+delta, rect.right-delta, rect.bottom-delta);
canvas.drawArc(rectF, startAngle, currentSweepAngle, false, p);
}
public class ProgressiveDrawing extends Animation {
public ProgressiveDrawing(int duration) {
setDuration(duration);
setInterpolator(new LinearInterpolator());
}
public ProgressiveDrawing(int duration, Interpolator interpolator) {
setDuration(duration);
setInterpolator(interpolator);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
currentSweepAngle = (int) (sweepAngle * interpolatedTime);
postInvalidate();
}
}
}
Related
Note: I know there are a lot of questions and repositories about this, but none seems to fit what I try to achieve.
Background
Given a bitmap of any aspect-ratio, I wish to set it as the content of an ImageView (using a drawable only, without extending the ImageView), so that the content will be center-cropped, and yet in the shape of a circle.
All of this, with minimal memory usage, because the images could be quite large sometimes. I do not want to create a whole new Bitmap just for this. The content is already there...
The problem
All solutions I've found lack one of the things I've written: some do not center-crop, some assume the image is square-shaped, some create a new bitmap from the given bitmap...
What I've tried
Other than trying various repositories, I've tried this tutorial, and I tried to fix it for the case of non-square aspect ratios, but I've failed.
Here's its code, in case the website will get closed:
public class RoundImage extends Drawable {
private final Bitmap mBitmap;
private final Paint mPaint;
private final RectF mRectF;
private final int mBitmapWidth;
private final int mBitmapHeight;
public RoundImage(Bitmap bitmap) {
mBitmap = bitmap;
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
}
#Override
public void draw(Canvas canvas) {
canvas.drawOval(mRectF, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRectF.set(bounds);
}
#Override
public void setAlpha(int alpha) {
if (mPaint.getAlpha() != alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public int getIntrinsicWidth() {
return mBitmapWidth;
}
#Override
public int getIntrinsicHeight() {
return mBitmapHeight;
}
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
invalidateSelf();
}
#Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
invalidateSelf();
}
#Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
invalidateSelf();
}
public Bitmap getBitmap() {
return mBitmap;
}
}
A very good solution I've found (here) does exactly what I need, except it uses it all in the ImageView itself, instead of creating a drawable. This means that I can't set it, for example, as the background of a view.
The question
How can I achieve this?
EDIT: this is the current code, and as I wanted to add border, it also has this code for it:
public class SimpleRoundedDrawable extends BitmapDrawable {
private final Path p = new Path();
private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public SimpleRoundedDrawable(final Resources res, final Bitmap bitmap) {
super(res, bitmap);
mBorderPaint.setStyle(Paint.Style.STROKE);
}
public SimpleRoundedDrawable setBorder(float borderWidth, #ColorInt int borderColor) {
mBorderPaint.setStrokeWidth(borderWidth);
mBorderPaint.setColor(borderColor);
invalidateSelf();
return this;
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
p.rewind();
p.addCircle(bounds.width() / 2,
bounds.height() / 2,
Math.min(bounds.width(), bounds.height()) / 2,
Path.Direction.CW);
}
#Override
public void draw(Canvas canvas) {
canvas.clipPath(p);
super.draw(canvas);
final float width = getBounds().width(), height = getBounds().height();
canvas.drawCircle(width / 2, height / 2, Math.min(width, height) / 2, mBorderPaint);
}
}
I hope this is how things should really work.
EDIT: It seems that the solution works only from specific Android version, as it doesn't work on Android 4.2.2. Instead, it shows a squared image.
EDIT: it seems that the above solution is also much less efficient than using BitmapShader (Link here). It would be really great to know how to use it within a drawable instead of within a customized ImageView
--
Here's the current modified version of the below solutions. I hope it will be handy for some people:
public class SimpleRoundedDrawable extends Drawable {
final Paint mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG), mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Bitmap mBitmap;
int mSide;
float mRadius;
public SimpleRoundedDrawable() {
this(null);
}
public SimpleRoundedDrawable(Bitmap bitmap) {
this(bitmap, 0, 0);
}
public SimpleRoundedDrawable(Bitmap bitmap, float width, #ColorInt int color) {
mBorderPaint.setStyle(Paint.Style.STROKE);
mBitmap = bitmap;
mSide = mBitmap == null ? 0 : Math.min(bitmap.getWidth(), bitmap.getHeight());
mBorderPaint.setStrokeWidth(width);
mBorderPaint.setColor(color);
}
public SimpleRoundedDrawable setBitmap(final Bitmap bitmap) {
mBitmap = bitmap;
mSide = Math.min(bitmap.getWidth(), bitmap.getHeight());
invalidateSelf();
return this;
}
public SimpleRoundedDrawable setBorder(float width, #ColorInt int color) {
mBorderPaint.setStrokeWidth(width);
mBorderPaint.setColor(color);
invalidateSelf();
return this;
}
#Override
protected void onBoundsChange(Rect bounds) {
if (mBitmap == null)
return;
Matrix matrix = new Matrix();
RectF src = new RectF(0, 0, mSide, mSide);
src.offset((mBitmap.getWidth() - mSide) / 2f, (mBitmap.getHeight() - mSide) / 2f);
RectF dst = new RectF(bounds);
final float strokeWidth = mBorderPaint.getStrokeWidth();
if (strokeWidth > 0)
dst.inset(strokeWidth, strokeWidth);
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
Shader shader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
shader.setLocalMatrix(matrix);
mMaskPaint.setShader(shader);
matrix.mapRect(src);
mRadius = src.width() / 2f;
}
#Override
public void draw(Canvas canvas) {
Rect b = getBounds();
if (mBitmap != null)
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), mRadius, mMaskPaint);
final float strokeWidth = mBorderPaint.getStrokeWidth();
if (strokeWidth > 0)
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), mRadius + strokeWidth / 2, mBorderPaint);
}
#Override
public void setAlpha(int alpha) {
mMaskPaint.setAlpha(alpha);
invalidateSelf();
}
#Override
public void setColorFilter(ColorFilter cf) {
mMaskPaint.setColorFilter(cf);
invalidateSelf();
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
If I'm following you correctly, your Drawable class would be like so:
public class CroppedDrawable extends BitmapDrawable {
private Path p = new Path();
public CroppedDrawable(Bitmap b) {
super(b);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
p.rewind();
p.addCircle(bounds.width() / 2,
bounds.height() / 2,
Math.min(bounds.width(), bounds.height()) / 2,
Path.Direction.CW);
}
#Override
public void draw(Canvas canvas) {
canvas.clipPath(p);
super.draw(canvas);
}
}
An example usage would be:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mila);
CroppedDrawable cd = new CroppedDrawable(bitmap);
imageView.setImageDrawable(cd);
Which, with your previous sample image, would give something like this:
try this minimalist custom Drawable and modify it to meet your needs:
class D extends Drawable {
Bitmap bitmap;
Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
int side;
float radius;
public D(Bitmap wrappedBitmap) {
bitmap = wrappedBitmap;
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(16);
borderPaint.setColor(0xcc220088);
side = Math.min(bitmap.getWidth(), bitmap.getHeight());
}
#Override
protected void onBoundsChange(Rect bounds) {
Matrix matrix = new Matrix();
RectF src = new RectF(0, 0, side, side);
src.offset((bitmap.getWidth() - side) / 2f, (bitmap.getHeight() - side) / 2f);
RectF dst = new RectF(bounds);
dst.inset(borderPaint.getStrokeWidth(), borderPaint.getStrokeWidth());
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
shader.setLocalMatrix(matrix);
maskPaint.setShader(shader);
matrix.mapRect(src);
radius = src.width() / 2f;
}
#Override
public void draw(Canvas canvas) {
Rect b = getBounds();
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), radius, maskPaint);
canvas.drawCircle(b.exactCenterX(), b.exactCenterY(), radius + borderPaint.getStrokeWidth() / 2, borderPaint);
}
#Override public void setAlpha(int alpha) {}
#Override public void setColorFilter(ColorFilter cf) {}
#Override public int getOpacity() {return PixelFormat.TRANSLUCENT;}
}
As it seems, using "clipPath" isn't efficient and needs to have hardware-acceleration disabled on 4.3 and below.
A better solution is to use something like on this library, using BitmapShader :
https://github.com/hdodenhof/CircleImageView
which is based on :
http://www.curious-creature.com/2012/12/11/android-recipe-1-image-with-rounded-corners/
The relevant code is:
BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
RectF rect = new RectF(0.0f, 0.0f, width, height);
// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);
I still wish to know how to do it all in a drawable, if the input is a bitmap.
A quick draft of a CircleImageDrawable based on my CircleImageView library. This does not create a new Bitmap, uses a BitmapShader to achieve the desired effect and center-crops the image.
public class CircleImageDrawable extends Drawable {
private final RectF mBounds = new RectF();
private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF();
private final Matrix mShaderMatrix = new Matrix();
private final Paint mBitmapPaint = new Paint();
private final Paint mBorderPaint = new Paint();
private int mBorderColor = Color.BLACK;
private int mBorderWidth = 0;
private Bitmap mBitmap;
private BitmapShader mBitmapShader;
private int mBitmapWidth;
private int mBitmapHeight;
private float mDrawableRadius;
private float mBorderRadius;
public CircleImageDrawable(Bitmap bitmap) {
mBitmap = bitmap;
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
}
#Override
public void draw(Canvas canvas) {
canvas.drawCircle(mBounds.width() / 2.0f, mBounds.height() / 2.0f, mDrawableRadius, mBitmapPaint);
if (mBorderWidth != 0) {
canvas.drawCircle(mBounds.width() / 2.0f, mBounds.height() / 2.0f, mBorderRadius, mBorderPaint);
}
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mBounds.set(bounds);
setup();
}
private void setup() {
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderRect.set(mBounds);
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
mDrawableRect.set(mBorderRect);
mDrawableRect.inset(mBorderWidth, mBorderWidth);
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
updateShaderMatrix();
invalidateSelf();
}
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
mShaderMatrix.set(null);
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
} else {
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
}
mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
#Override
public void setAlpha(int alpha) {
mBitmapPaint.setAlpha(alpha);
invalidateSelf();
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
mBitmapPaint.setColorFilter(colorFilter);
invalidateSelf();
}
#Override
public int getOpacity() {
return 0;
}
}
There is already a built-in way to accomplish this and it's 1 line of code (ThumbnailUtils.extractThumbnail())
int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
...
//I added this method because people keep asking how
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
//If the bitmap is wider than it is tall
//use the height as the square crop dimension
if (bitmap.getWidth() >= bitmap.getHeight())
{
dimension = bitmap.getHeight();
}
//If the bitmap is taller than it is wide
//use the width as the square crop dimension
else
{
dimension = bitmap.getWidth();
}
}
If you want the bitmap object to be recycled, you can pass options that make it so:
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
Below is the link for the documentation:
ThumbnailUtils Documentation
How can I make move in coordinates Y, a canvas.drawCircle?
I want to make the sensation like the circle fall where I touch on the screen but I don't know how to animate it.
My onDraw:
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
anchoX = x;
anchoY = y;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
canvas.drawPaint(paint);
/*Texto*/
paint.setColor(Color.BLACK);
paint.setTextSize(80);
canvas.drawText("CONECTA 4", 70, 130, paint);
/*Separador*/
paint.setColor(Color.parseColor("#5C5C5C"));
canvas.drawRect(0, 200, 600, 210, paint);
/*Tablero*/
int radius = 25;
for (int i = 0; i < Game.NFILAS; i++)
for (int j = 0; j < Game.NCOLUMNAS; j++){
if (game.estaVacio(i,j)){
color = Color.WHITE;
paint.setColor(color);
canvas.drawCircle(getPixelFromColumna(j), getPixelFromFila(i), radius, paint);
} else if (game.estaJugador(i,j)){
paint.setColor(coloreado);
canvas.drawCircle(getPixelFromColumna(j), getPixelFromFila(i), radius, paint);
} else {
color = Color.RED;
paint.setColor(color);
canvas.drawCircle(getPixelFromColumna(j),getPixelFromFila(i), radius, paint);
}
}
}
you can check this answer. This is what you were looking for draw a circle with animation
For creating the circle you can you java file like this
public class Circle extends View {
private static final int START_ANGLE_POINT = 90;
private final Paint paint;
private final RectF rect;
private float angle;
public Circle(Context context, AttributeSet attrs) {
super(context, attrs);
final int strokeWidth = 40;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
//Circle color
paint.setColor(Color.RED);
//size 200x200 example
rect = new RectF(strokeWidth, strokeWidth, 200 + strokeWidth, 200 + strokeWidth);
//Initial Angle (optional, it can be zero)
angle = 120;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(rect, START_ANGLE_POINT, angle, false, paint);
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
this.angle = angle;
}
}
and for creating animation
public class CircleAngleAnimation extends Animation {
private Circle circle;
private float oldAngle;
private float newAngle;
public CircleAngleAnimation(Circle circle, int newAngle) {
this.oldAngle = circle.getAngle();
this.newAngle = newAngle;
this.circle = circle;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation) {
float angle = oldAngle + ((newAngle - oldAngle) * interpolatedTime);
circle.setAngle(angle);
circle.requestLayout();
}
}
and you can use like this in xml for defining
<com.package.Circle
android:id="#+id/circle"
android:layout_width="300dp"
android:layout_height="300dp" />
Animate you circle by using this sample code
Circle circle = (Circle) findViewById(R.id.circle);
CircleAngleAnimation animation = new CircleAngleAnimation(circle, 240);
animation.setDuration(1000);
circle.startAnimation(animation);
First of all I never used canvas transformations before. But here is that I have to do:
say I have a canvas that holds an image. I need to transform it like this
to this
I'm able to rotate the canvas like following:
and
Here is the code I'm using for the left one:
public void transformCanvas(Canvas canvas, float percentOpen) {
Camera camera = new Camera();
canvas.translate(0, canvas.getHeight()/2);
camera.translate(0, 0, Math.abs(90 * percentOpen - 90));
camera.rotateY(Math.abs(90 * percentOpen - 90));
Matrix matrix = new Matrix();
camera.getMatrix(matrix);
canvas.concat(matrix);
canvas.translate(0, -canvas.getHeight()/2);
}
percentOpen holds the value from 0 to 1 so that when percentOpen decreases the canvas rotates till it becomes invisible and when percentOpen increases the canvas returns to its original state.
I'm able to animate both left and right variants, I just cannot figure out how should I divide the canvas into two parts (or even should I divide it somehow?) to animate the left and right parts independently.
I tried taking the bitmap of the view, divide it into two Bitmaps and rotate them independently but this was very slow.
I'd be very glad for any help.
How can I rotate the left and right sides independently?
try this custom Animation:
class V extends View implements OnClickListener {
private A animation;
private Paint paint0;
private Paint paint1;
private RectF rect;
private Bitmap bitmap;
public V(Context context) {
super(context);
paint0 = new Paint();
paint0.setColor(0xffaa0000);
paint1 = new Paint();
paint1.setColor(0xffffffff);
rect = new RectF();
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
setOnClickListener(this);
}
#Override
public void onClick(View v) {
Log.d(TAG, "onClick ");
animation = new A(getWidth(), getHeight());
startAnimation(animation);
}
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth();
int h = getHeight();
if (animation != null && animation.hasStarted() && !animation.hasEnded()) {
canvas.save();
canvas.clipRect(0, 0, w / 2, h, Op.REPLACE);
canvas.concat(animation.leftMatrix);
drawStuff(canvas, w, h);
canvas.restore();
canvas.save();
canvas.clipRect(w / 2, 0, w, h, Op.REPLACE);
canvas.concat(animation.rightMatrix);
drawStuff(canvas, w, h);
canvas.restore();
} else {
drawStuff(canvas, w, h);
}
}
public void drawStuff(Canvas canvas, int w, int h) {
int s = Math.min(w, h);
rect.set(0, 0, s, s);
rect.offset((w - s) / 2, (h - s) / 2);
canvas.drawRect(rect, paint0);
rect.left += 64;
rect.top += 64;
canvas.drawOval(rect, paint1);
canvas.drawBitmap(bitmap, rect.left, rect.top, null);
}
class A extends Animation {
private Camera camera;
private int w2;
private int h2;
public Matrix leftMatrix;
public Matrix rightMatrix;
public A(int w, int h) {
camera = new Camera();
leftMatrix = new Matrix();
rightMatrix = new Matrix();
w2 = w / 2;
h2 = h / 2;
setDuration(2000);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
camera.save();
float angle = 80 * interpolatedTime;
camera.rotateY(angle);
camera.getMatrix(leftMatrix);
leftMatrix.preTranslate(-w2, -h2);
leftMatrix.postTranslate(w2, h2);
camera.rotateY(-2 * angle);
camera.getMatrix(rightMatrix);
rightMatrix.preTranslate(-w2, -h2);
rightMatrix.postTranslate(w2, h2);
camera.restore();
}
}
}
I need to make cross fade effect on two bitmaps in a way that first bitmap is being replaced by the second one linearly from top to bottom. In other words first bitmap should
dissapear from top to bottom and in it's place second bitmap should appear.
There are many examples with cross fade with animations (changing alpha), or using TransitionDrawable, but i was not able to find the one i am looking for.
Anyone knows how to do that?
Thanks
i know, try this custom class:
public class MyView extends View implements View.OnClickListener {
private Bitmap mBitmap0;
private Bitmap mBitmap1;
private Matrix mMatrix;
private float mY;
private Rect mRect;
public MyView(Context context) {
super(context);
mBitmap0 = BitmapFactory.decodeResource(getResources(), R.drawable.b0);
mBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.b1);
mMatrix = new Matrix();
mRect = new Rect();
setOnClickListener(this);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
RectF src = new RectF(0, 0, mBitmap0.getWidth(), mBitmap0.getHeight());
RectF dst = new RectF(0, 0, w, h);
mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xff0000aa);
canvas.concat(mMatrix);
Rect r = mRect;
r.set(0, 0, mBitmap0.getWidth(), (int) (mY * mBitmap0.getHeight()));
canvas.drawBitmap(mBitmap0, r, r, null);
r.set(0, (int) (mY * mBitmap1.getHeight()), mBitmap1.getWidth(), mBitmap1.getHeight());
canvas.drawBitmap(mBitmap1, r, r, null);
}
#Override
public void onClick(View v) {
TBAnimation anim = new TBAnimation();
startAnimation(anim);
}
class TBAnimation extends Animation {
public TBAnimation() {
mY = 0;
setDuration(2000);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
mY = interpolatedTime;
invalidate();
}
}
}
or this one (has narrow fade out strip):
public class MyView extends View implements View.OnClickListener {
private static final int FADE_HEIGHT = 32;
private Bitmap mBitmap0;
private Bitmap mBitmap1;
private Matrix mMatrix;
private float mY;
private Rect mRect;
private Paint mPaint0;
private LinearGradient mShader;
private Paint mPaint1;
public MyView(Context context) {
super(context);
mBitmap0 = BitmapFactory.decodeResource(getResources(), R.drawable.b1);
mBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.b0);
mMatrix = new Matrix();
mRect = new Rect();
mPaint0 = new Paint(Paint.ANTI_ALIAS_FLAG);
mShader = new LinearGradient(0, 0, 0, FADE_HEIGHT, 0x00ffffff, 0xffffffff, TileMode.CLAMP);
mPaint0.setShader(mShader);
mPaint1 = new Paint();
mPaint1.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
setOnClickListener(this);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
RectF src = new RectF(0, 0, mBitmap0.getWidth(), mBitmap0.getHeight());
RectF dst = new RectF(0, 0, w, h);
mMatrix.setRectToRect(src, dst, ScaleToFit.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xff0000aa);
canvas.concat(mMatrix);
Rect r = mRect;
int y = (int) (mY * mBitmap0.getHeight());
// draw new bitmap on top
r.set(0, 0, mBitmap0.getWidth(), y + FADE_HEIGHT);
canvas.drawBitmap(mBitmap0, r, r, null);
// draw old bitmap strip fading out
canvas.saveLayer(0, y, mBitmap1.getWidth(), y + FADE_HEIGHT, null, Canvas.ALL_SAVE_FLAG);
canvas.save();
canvas.translate(0, y);
r.set(0, 0, mBitmap1.getWidth(), FADE_HEIGHT);
canvas.drawRect(r, mPaint0);
canvas.restore();
r.set(0, y, mBitmap1.getWidth(), y + FADE_HEIGHT);
canvas.drawBitmap(mBitmap1, r, r, mPaint1);
canvas.restore();
// draw old bitmap on bottom
r.set(0, y + FADE_HEIGHT, mBitmap1.getWidth(), mBitmap1.getHeight());
canvas.drawBitmap(mBitmap1, r, r, null);
}
#Override
public void onClick(View v) {
Bitmap tmp = mBitmap0;
mBitmap0 = mBitmap1;
mBitmap1 = tmp;
TBAnimation anim = new TBAnimation();
startAnimation(anim);
}
class TBAnimation extends Animation {
public TBAnimation() {
mY = 0;
setDuration(3000);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
mY = interpolatedTime;
invalidate();
}
}
}
What I am searching for, is a view that reveals an image in a clockwise circular fashion. When the progress is 25% the first 90 degrees should be revealed and when its 100% the full 360 degrees should be drawn.
It is very close to using the canvas.drawArc(), but this method only works with Paint objects, not bitmaps.
Other answers also only work with full colors:
How To Make Circle Custom Progress Bar in Android
I have found it to be a very hard problem, I hope someone has a solution.
tre this:
public class MyView extends View {
private Bitmap mBitmap;
private Paint mPaint;
private RectF mOval;
private float mAngle = 135;
private Paint mTextPaint;
public MyView(Context context) {
super(context);
// use your bitmap insted of R.drawable.ic_launcher
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOval = new RectF();
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(48);
mTextPaint.setTextAlign(Align.CENTER);
mTextPaint.setColor(0xffffaa00);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Matrix m = new Matrix();
RectF src = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
RectF dst = new RectF(0, 0, w, h);
m.setRectToRect(src, dst, ScaleToFit.CENTER);
Shader shader = new BitmapShader(mBitmap, TileMode.CLAMP, TileMode.CLAMP);
shader.setLocalMatrix(m);
mPaint.setShader(shader);
m.mapRect(mOval, src);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xff0000aa);
canvas.drawArc(mOval, -90, mAngle, true, mPaint);
canvas.drawText("click me", getWidth() / 2, getHeight() / 2, mTextPaint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float w2 = getWidth() / 2f;
float h2 = getHeight() / 2f;
mAngle = (float) Math.toDegrees(Math.atan2(event.getY() - h2, event.getX() - w2));
mAngle += 90 + 360;
mAngle %= 360;
invalidate();
return true;
}
}