Draw Curved Text on Canvas with different length of Text in android - android

I need to implement curved text which draw text on circular path on canvas.
It does draw circular path using
canvas.drawTextOnPath(QUOTE, circle, 485, 20, tPaint);
but it is not working for different length of the text.
Following is my Class to draw circular text on the canavs.
public class CircularTextVie extends View {
private String QUOTE = "";
private Path circle;
private Paint cPaint;
private Paint tPaint;
public CircularTextVie(Context context) {
super(context);
circle = new Path();
cPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
cPaint.setStyle(Paint.Style.STROKE);
cPaint.setColor(Color.LTGRAY);
cPaint.setStrokeWidth(3);
tPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
tPaint.setStyle(Paint.Style.FILL_AND_STROKE);
tPaint.setColor(Color.BLACK);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.restore();
int xPos = (canvas.getWidth() /3);
int yPos = (int) ((canvas.getHeight() / 3) - ((tPaint.descent() + tPaint.ascent()) / 3)) ;
circle.addCircle(xPos, yPos, 150, Path.Direction.CW);
canvas.drawTextOnPath(QUOTE, circle, 485, 20, tPaint);
QUOTE="";
}
public void SetText(String text) {
this.QUOTE = text;
}
public void SetTypeFace(Typeface face) {
cPaint.setTypeface(face);
tPaint.setTypeface(face);
}
public void SetColor(int color) {
cPaint.setColor(color);
tPaint.setColor(color);
}
}
Thanks.

This can be done by varying the x and y positions based on textwidth
Define variables
private Rect textBounds;
private int mTextWidth, mTextHeight,centerX,centerY;
Add the below in the constructor of customview
textBounds = new Rect();
tPaint.getTextBounds(QUOTE, 0, QUOTE.length(), textBounds);
mTextWidth = Math.round(tPaint.measureText(QUOTE.toString())); // Use measureText to calculate width
mTextHeight = textBounds.height(); // Use height from getTextBounds()
Then in onDraw
canvas.drawCircle(centerX,centerY,150,mCirlcePaint);
circle.addCircle(centerX, centerY, 150, Path.Direction.CW);
// Note the 0 that's y offset. textdraw at circumference of the circle. Changing y you probably need to change the radius as well i guess.
canvas.drawTextOnPath(QUOTE, circle, (centerX)-(mTextWidth / 2f), 0, tPaint);
centerX,centerY are the center of the circle ie canvaswidht/2 and canvasHeight/2. I drew a circle for reference
The results for hello
The result for a bigger text
For numbers
Edit: To the question in comment
The math involved is in calculating the semi circle using text length
radius = (float) ((mTextWidth) / (Math.PI)). If text length is greater than your canvas you need to reduce the text size or use the full circle radius = (float) ((mTextWidth) / (2*(Math.PI))). Few other edge case you can consider to draw the text properly.
public class GraphicsView extends View {
private String QUOTE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private Path circle;
private Paint mCirlcePaint;
private Paint tPaint;
private Rect textBounds;
private int mTextWidth, mTextHeight, centerX, centerY;
private float radius;
public GraphicsView(Context context) {
super(context);
circle = new Path();
tPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
tPaint.setStyle(Paint.Style.FILL_AND_STROKE);
tPaint.setColor(Color.BLACK);
tPaint.setTextSize(100f);
textBounds = new Rect();
tPaint.getTextBounds(QUOTE, 0, QUOTE.length(), textBounds);
mTextWidth = Math.round(tPaint.measureText(QUOTE.toString())); // Use measureText to calculate width
mTextHeight = textBounds.height(); // Use height from getTextBounds()
mCirlcePaint = new Paint();
mCirlcePaint.setStyle(Paint.Style.FILL);
mCirlcePaint.setColor(Color.GREEN);
radius = (float) ((mTextWidth) / (Math.PI));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.rotate(180, getWidth() / 2, getHeight() / 2);
canvas.drawCircle(centerX, centerY, radius, mCirlcePaint);
circle.addCircle(centerX, centerY, radius, Path.Direction.CW);
canvas.drawTextOnPath(QUOTE, circle, 0, 0, tPaint);
}
}

Related

Dynamically draw outline oval over a view

I am working with a custom UI element highlights.So I need to draw a half ellipse like shape over a particular view on runtime.
For example:
I have to draw a yellow color half ellipse over a white ellipse.
Help will be appreciated.
public class MyView extends View {
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
float width = (float) getWidth();
float height = (float) getHeight();
float radius;
if (width > height) {
radius = height / 4;
} else {
radius = width / 4;
}
Path path = new Path();
path.addCircle(width / 2,
height / 2, radius,
Path.Direction.CW);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
float center_x, center_y;
final RectF oval = new RectF();
paint.setStyle(Paint.Style.STROKE);
center_x = width / 2;
center_y = height / 2;
oval.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
canvas.drawArc(oval, 90, 180, false, paint);
}
}
Add
new MyView(this)

Animate canvas.drawCircle

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);

Canvas animate "closing the book"

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();
}
}
}

Animate an oval GradientDrawable created programmatically

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();
}
}
}

How do I draw a partial bitmap-arc? Like a circular progress wheel, but with a bitmap that gets more and more displayed.

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;
}
}

Categories

Resources