I am new to android graphic programming.
I want to put a bitmap in the centre of my canvas. Hence, i use :
public void onDraw(Canvas canvas) {
float canvasx = (float) canvas.getWidth();
float canvasy = (float) canvas.getHeight();
Then i call the bitmap i want to use,
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.myBitmap);
Then i find the coordinate location for my bitmap by using these,
float bitmapx = (float) myBitmap.getWidth();
float bitmapy = (float) myBitmap.getHeight();
float boardPosX = (canvasx - bitmapx) / 2;
float boardPosY = (canvasy - bitmapy) / 2;
Finally, i draw the bitmap using,
canvas.drawBitmap(myBitmap, boardPosX, boardPosY, null);
But, the bitmap is not in the center of the canvas. It's a little bit below the position which i reckon should be the center of the canvas.
Is it correct to get the canvas height and width inside the onDraw() method ?
Any idea what's wrong ?
Thanks in advance.
*Edit :
Finally, i make it work by changing
public void onDraw(Canvas canvas) {
float canvasx = (float) canvas.getWidth();
float canvasy = (float) canvas.getHeight();
to
public void onDraw(Canvas canvas) {
float canvasx = (float) getWidth();
float canvasy = (float) getHeight();
However, i dont know why the change fixes my problem.
Use this:
float boardPosX = ((canvasx/2) - (bitmapx / 2));
float boardPosY = ((canvasy/2) - (bitmapy / 2));
private int mWidth;
private int mHeight;
private float mAngle;
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
mWidth = View.MeasureSpec.getSize(widthMeasureSpec);
mHeight = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(mWidth, mHeight);
}
#Override protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
// Here's the magic. Whatever way you do it, the logic is:
// space available - bitmap size and divide the result by two.
// There must be an equal amount of pixels on both sides of the image.
// Therefore whatever space is left after displaying the image, half goes to
// left/up and half to right/down. The available space you get by subtracting the
// image's width/height from the screen dimensions. Good luck.
int cx = (mWidth - myBitmap.getWidth()) >> 1; // same as (...) / 2
int cy = (mHeight - myBitmap.getHeight()) >> 1;
if (mAngle > 0) {
canvas.rotate(mAngle, mWidth >> 1, mHeight >> 1);
}
canvas.drawBitmap(myBitmap, cx, cy, null);
}
Related
In this Image I want text to totally be in the triangle with CYAN color.
I have created my own ImageView:
public class BookImageView extends android.support.v7.widget.AppCompatImageView {
private static final Float DISCOUNT_SIDE_SIZE = 0.33333F;
private Bitmap bitmap;
private Paint drawPaint = new Paint();
private Paint trianglePaint = new Paint();
{
trianglePaint.setColor(Constants.DISCOUNT_COLOR);
trianglePaint.setStyle(Paint.Style.FILL);
trianglePaint.setShadowLayer(10.0f, 10.0f, 10.0f, Color.parseColor("#7f000000"));
trianglePaint.setAntiAlias(true);
drawPaint.setColor(Color.BLACK);
drawPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
drawPaint.setShadowLayer(1f, 0f, 1f, Color.BLACK);
}
// Constractors ...
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap != null) {
Bitmap tempBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas tempCanvas = new Canvas(tempBitmap);
tempCanvas.drawBitmap(bitmap, 0, 0, null);
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
float size = bitmap.getWidth() * DISCOUNT_SIDE_SIZE;
path.lineTo(size, 0);
path.lineTo(0, size);
path.lineTo(0, 0);
path.close();
tempCanvas.drawPath(path, trianglePaint);
float scale = getResources().getDisplayMetrics().density;
drawPaint.setTextSize((int) (14 * scale));
Rect textBounds = new Rect();
drawPaint.getTextBounds("50%", 0, "50%".length(), textBounds);
int x = (int) (size / 2) - textBounds.width() / 2;
int y = (int) (size / 2) - textBounds.height() / 2;
tempCanvas.save();
tempCanvas.rotate(-45, x, y);
tempCanvas.drawText("50%", x, y, drawPaint);
tempCanvas.restore();
setImageDrawable(new BitmapDrawable(getContext().getResources(), tempBitmap));
}
}
#Override
public void setImageBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
invalidate();
}
}
what can I do to solve this problem ?
You can try something like this
1) Measure the width of your text
Use measureText
2) From the point you are drawing calculate the width remaining to draw
3) Now depending on the use case you can curtail the length of text or scale the text as needed
int textWidthRequired = (int) drawPaint.measureText(textToDraw);
int widthRemainingToDraw = totalWidth/2 - textDrawX;
if(textWidthRequired > widthRemainingToDraw){
//handling
}
// draw text
tempCanvas.drawText(textToDraw,textDrawX, textDrawY, drawPaint);
Depending on how high up you want the text to be, you can use properties of similar triangles to first determine the maximum width of the text. In your case, size = the triangle's base, and size = the triangle's height/altitude. Let's define two variables:
Correction: The altitude won't be equal to the base. You'd need to calculate the altitude in order to use the below solution.
float triangleBase = size; // triangle base
float triangleAltitude = size; // Calculate this.
Let's say we want the text to be halfway up the center of the triangle:
float textYHeight = triangleHeight/2;
We figure out the width of the triangle at this point by using the following formula since the sides of similar triangles are proportional:
baseOfTriangleA/baseOfTriangleB = altitudeOfTriangleA/altitudeOfTriangleB;
float triangleWidthAtTextYLocation = (textYHeight * triangleBase)/triangleAltitude;
Now that we know what the width of the triangle is at this location, we can just iterate through different text scales until the text width is less than the value triangleWidthAtTextYlocation.
float scale = getResources().getDisplayMetrics().density;
int scaleFactor = 0;
drawPaint.setTextSize((int) (scaleFactor * scale));
Rect textBounds = new Rect();
drawPaint.getTextBounds("50%", 0, "50%".length(), textBounds);
while(textBounds.length < triangleWidthAtTextYLocation){
// Re-measure the text until it exceeds the width
scaleFactor++;
drawPaint.setTextSize((int) (scaleFactor * scale));
drawPaint.getTextBounds("50%", 0, "50%".length(), textBounds);
}
// Once we know the scaleFactor that puts it over the width of the triangle
// at that location, we reduce it by 1 to be just under that width:
scaleFactor = Maths.abs(scaleFactor - 1);
// final text size:
drawPaint.setTextSize((int) (scaleFactor * scale));
I'm starting with Android Wear and I want to make a circle animation, making grow.
I know how to do it, I think, but it's doing it very very slow, hope you can help me
I have this class variable
Paint mAnimation;
intialized on the method OnCreate
mAnimation = new Paint(Paint.ANTI_ALIAS_FLAG);
mAnimation.setStyle(Paint.Style.FILL);
mAnimation.setColor(Color.GREEN);
and on the OnDraw method I have
#Override
public void onDraw(Canvas canvas, Rect bounds) {
int width = bounds.width();
int height = bounds.height();
float centerX = width / 2f;
float centerY = height / 2f;
// Draw the background.
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
canvas.drawCircle(0, centerY, radiusPlus, mAnimation);
radiusPlus += 20;
}
The animation is "correct", but is very slow, like if was paused.
Thanks!!
EDIT
I found a example and now I finally found why. I didn't invalidate the view at the end of the OnDraw. Now It's working fine. Thanks.
#Override
public void onDraw(Canvas canvas, Rect bounds) {
int width = bounds.width();
int height = bounds.height();
float centerX = width / 2f;
float centerY = height / 2f;
// Draw the background.
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
canvas.drawCircle(0, centerY, radiusPlus, mAnimation);
radiusPlus += 5;
if (isVisible() && !isInAmbientMode()) {
invalidate();
}
}
I now have a View that is added programically after the onCreate (Depending on some other variables). Everything works as it should and it draws part of a circle.
But my question is how do i redraw it later on ? I need to change the angle in the circle after some data is fetched.
Code for the WindRose :
public class WindRose extends View {
public WindRose(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvasTest = canvas;
float height = (float) getHeight();
float width = (float) getWidth();
float radius;
if (width > height) {
radius = height / 2;
} else {
radius = width / 2;
}
// radius = (height )/ 2;
Path path = new Path();
path.addCircle(width, height, radius, Path.Direction.CCW);
// / 2
Resources resources = getResources();
int color = resources.getColor(R.color.green_back);
paint.setColor(color);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL);
float center_x, center_y;
center_x = width / 2;
center_y = height / 2;
//Formulas :
//SD = Start Degree
//ED = End Degree
//If cakepiece passes 0 (East)
//SD, 360-(SD+ED)
//Else :
//SD, (ED-SD)
oval.set(center_x - radius, center_y - radius, center_x + radius, center_y + radius);
if (End > Start) {
canvas.drawArc(oval, Start, (End - Start), true, paint);
} else if (End < Start) {
canvas.drawArc(oval, Start, ((360 - Start) + End), true, paint);
}
}
}
If i update the Start and End variables nothing happens. i have also tried to call invalidate on the class but also there i dont get any redrawing.
Invalidate that i've tried :
WindRose windrose = new WindRose(this);
windrose.invalidate();
Was called from the main class which WindRose is part of.
How should i do this correctly ?
Maybe calling the invalidate() method of the view will help.
You can read more here(http://developer.android.com/reference/android/view/View.html), but:
"To force a view to draw, call invalidate()."
Note, also that you can invalidate only parts of a view
I created an rotated imageview with background rotation but the result is "aliased".
The usual way is to create a Paint with anti-alias flag then use it to draw bitmap.
However, the background is draw by the view's draw(Canvas canvas) method, so i can't use a paint with canvas.drawBitmap method.
Here my code to rotate the imageview including the background :
#Override
public void draw(Canvas canvas)
{
canvas.save();
canvas.rotate(mAngle%360, canvas.getClipBounds().exactCenterX(), canvas.getClipBounds().exactCenterY());
canvas.scale(scaleWidth, scaleHeight, canvas.getClipBounds().exactCenterX(), canvas.getClipBounds().exactCenterY());
super.draw(canvas);
canvas.restore();
}
and my onMeasure method which adapts the height and the width depending of the angle
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int w = getDrawable().getIntrinsicWidth();
int h = getDrawable().getIntrinsicHeight();
double a = Math.toRadians(mAngle);
int width = (int) (Math.abs(w * Math.cos(a)) + Math.abs(h * Math.sin(a)));
int height = (int) (Math.abs(w * Math.sin(a)) + Math.abs(h * Math.cos(a)));
scaleWidth = (width != 0) ? ((float) w) / width : 1;
scaleHeight = (height != 0) ? ((float) h) / height : 1;
setMeasuredDimension(width, height);
}
So, How can i "enable" anti aliasing on this rotation?
Maybe use setDrawFilter on the canvas?
A DrawFilter subclass can be installed in a Canvas. When it is present, it can modify the paint that is used to draw (temporarily). With this, a filter can disable/enable antialiasing, or change the color for everything this is drawn.
edit: see here: https://stackoverflow.com/a/13191948/1954497
I have an ImageView subclass that is supposed to display a logo in the bottom corner of the displayed image. I have this onDraw code to draw the icon on top of the image. It works fine on ICS+ but nothing lower. Does anyone know the reason for this?
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int canvasHeight = canvas.getHeight();
int canvasWidth = canvas.getWidth();
Drawable icon = getResources().getDrawable(R.id.icon);
int width = icon.getIntrinsicWidth();
int height = iconcon.getIntrinsicHeight();
int x = canvasWidth - width - PADDING;
int y = canvasHeight - height - PADDING;
icon.setBounds(x, y, canvasWidth - PADDING, canvasHeight - PADDING);
icon.draw(canvas);
}
you may want to ignore canvas.getWidth() and canvas.getHeight() and use the values received in onSizeChanged(int w, int h, int oldw, int oldh) instead, because sometimes they don't match and canvas.getWidth()/getHeight() gives strange results.