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();
}
}
}
Related
I want to draw image inside circle with bitmap and fill color outside.
I have extended AppCompatImageView and override onDraw method, but it displays white circle inside canvas.
following is my class :
class ImageMagnifier2 extends AppCompatImageView {
public ImageMagnifier2(final Context context) {
super(context);
init();
}
#Override
public boolean dispatchTouchEvent(MotionEvent me) {
// Call onTouchEvent of SimpleGestureFilterLongPress class
return super.dispatchTouchEvent(me);
}
private void init() {
setWillNotDraw(false);
setLayerType(LAYER_TYPE_HARDWARE, null);
zoomPos = new PointF(0, 0);
matrix = new Matrix();
paint = new Paint();
paintBackground = new Paint();
paint.setColor(getResources().getColor(android.R.color.transparent));
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!zooming) {
buildDrawingCache();
} else {
bitmap = getDrawingCache();
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
paint.getShader().setLocalMatrix(matrix);
paintBackground.setColor(Color.parseColor("#F227aae0"));//F227aae0
paintBackground.setStyle(Paint.Style.FILL);
paintBackground.setAntiAlias(true);
Rect rectangle = new Rect( 0, 0, canvas.getWidth(), canvas.getHeight() );
canvas.drawRect(rectangle,paintBackground);
canvas.drawCircle(zoomPos.x, zoomPos.y, sizeOfMagnifier - 5, paint);
}
}
It working fine when draw bitmap without outside rectangle or canvas fill color. but i need to fill outside color with bitmap.
Thank you for help in advance.
The following is a code the I wrote previously when I had the same issues, You may want to use the following class:
public class CircleImageView extends AppCompatImageView {
private RectF drawableRect = new RectF();
private Matrix shaderMatrix = new Matrix();
private Paint bmpPaint = new Paint();
private Paint backgroundPaint = new Paint();
private Bitmap bmp;
private int bmpWidth, bmpHeight;
private BitmapShader bmpShader;
private float radius;
public CircleImageView(Context context) {
super(context);
setScaleType(ScaleType.CENTER_CROP);
backgroundPaint.setStyle(Paint.Style.FILL);
backgroundPaint.setColor(Color.TRANSPARENT);
init();
}
#Override
protected void onDraw(Canvas canvas) {
if (bmp == null) return;
canvas.drawPaint(backgroundPaint);
canvas.drawCircle(drawableRect.centerX(), drawableRect.centerY(), radius, bmpPaint);
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) return null;
if (drawable instanceof BitmapDrawable) return ((BitmapDrawable) drawable).getBitmap();
try {
Bitmap bitmap;
if (drawable instanceof ColorDrawable) bitmap = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888);
else bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void initImage() {
bmp = getBitmapFromDrawable(getDrawable());
init();
}
private void init() {
if (getWidth() == 0 && getHeight() == 0) return;
if (bmp == null) {invalidate();return;}
bmpShader = new BitmapShader(bmp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
bmpPaint.setAntiAlias(true);
bmpPaint.setShader(bmpShader);
bmpHeight = bmp.getHeight();
bmpWidth = bmp.getWidth();
drawableRect.set(new RectF(0,0,getWidth(),getHeight()));
radius = Math.min(drawableRect.height() / 2.0f, drawableRect.width() / 2.0f);
updateMatrix();
invalidate();
}
private void updateMatrix() {
float scale;
float dx = 0;
float dy = 0;
shaderMatrix.set(null);
if (bmpWidth * drawableRect.height() > drawableRect.width() * bmpHeight) {
scale = drawableRect.height() / (float) bmpHeight;
dx = (drawableRect.width() - bmpWidth * scale) * 0.5f;
} else {
scale = drawableRect.width() / (float) bmpWidth;
dy = (drawableRect.height() - bmpHeight * scale) * 0.5f;
}
shaderMatrix.setScale(scale, scale);
shaderMatrix.postTranslate((int) (dx + 0.5f) + drawableRect.left, (int) (dy + 0.5f) + drawableRect.top);
bmpShader.setLocalMatrix(shaderMatrix);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
init();
}
public void setBackgroundColor(#ColorInt int backgroundColor) {
backgroundPaint.setColor(backgroundColor);
invalidate();
}
#Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
initImage();
}
#Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
initImage();
}
#Override
public void setImageResource(#DrawableRes int resId) {
super.setImageResource(resId);
initImage();
}
#Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
initImage();
}
/* if this answer solved your problem please mark it as accepted by clicking the check mark next to the answer. Thank you */
}
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 newbie to Android Development, and Canvas drawings.
When I draw stuff on canvas, its easier for me to work on rectangle portion of width=height=1.
I try to scale according, but when trying to drawArc its draw it badly.
Can you please tell me what I'm doing wrong?
public class MyChart extends View{
private RectF dimentionRect;
private Paint dimentionPaint;
private static final String TAG = "VERBOSE";
public MyChart(Context context){
super(context);
initDrawingTools();
}
public MyChart(Context context,AttributeSet attrs) {
super(context,attrs);
initDrawingTools();
}
private void initDrawingTools(){
dimentionPaint = new Paint();
dimentionPaint.setColor(Color.GREEN);
dimentionPaint.setStyle(Style.FILL);
}
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenDimention = Math.min(widthSize, heightSize);
setMeasuredDimension(chosenDimention, chosenDimention);
Log.v(TAG, "onMeasure: "+chosenDimention);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.v(TAG, "onSizeChange");
}
#Override
public void onDraw(Canvas canvas){
float width = (float)getWidth();
Log.v(TAG, "onDraw: "+width);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(width, width);
dimentionRect = new RectF(0,0,1f,1f);
//canvas.drawRect(dimentionRect, dimentionPaint); //
canvas.drawArc(dimentionRect, 0, 180, true, dimentionPaint);
// canvas.drawCircle(0.5f, 0.5f, 0.3f, dimentionPaint);
canvas.restore();
}
}
Ok, I didn't find a solution but I was able to find a work-around for it.
I created a background bitmap and using it for a local Canvas which do the drawing stuff. after all drawings I draw the bitmap again into my canvas.
I know its not elegant but thats the solution I found
private void drawBackground(Canvas canvas){
if(background != null){
canvas.drawBitmap(background, 0,0,backgroundPaint);
Log.e(TAG, "Drawing background");
}
}
private void regenerateBackground(){
if(background != null){
background.recycle();
}
background = Bitmap.createBitmap(getWidth(), getWidth(), Config.ARGB_8888);
RectF rect = new RectF(0,0,getWidth(),getWidth());
Canvas c = new Canvas(background);
c.drawRect(rect, backgroundPaint);
}
#Override
protected void onDraw(Canvas canvas){
drawBackground(canvas);
float width = (float)getWidth();
Canvas c = new Canvas(background);
c.save(Canvas.MATRIX_SAVE_FLAG);
c.scale(width,width);
border = new RectF(0,0,1,1);
//
Paint p = new Paint();
p.setColor(Color.GREEN);
p.setStyle(Style.FILL);
c.drawArc(border, 0, 360,true, p);
c.restore();
}
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();
}
}
}
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;
}
}