Place many circular shapes on top of imageview android - android

So I'm making an game like "Spot The Difference" and while I can find the center of the difference I cant place a shape on top of it correctly. Using "Paint Code" I generated a class which helps me create the shape but there is still a problem with placement. I need to add many circular shapes over the center of the differences that were found. Here is the shape class and what I have tried so far:
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Path;
public class StyleKit {
// Resizing Behavior
public enum ResizingBehavior {
AspectFit, //!< The content is proportionally resized to fit into the target rectangle.
AspectFill, //!< The content is proportionally resized to completely fill the target rectangle.
Stretch, //!< The content is stretched to match the entire target rectangle.
Center, //!< The content is centered in the target rectangle, but it is NOT resized.
}
// Canvas Drawings
// Tab
private static class CacheForRoundImage {
private static Paint paint = new Paint();
private static RectF ovalRect = new RectF();
private static Path ovalPath = new Path();
}
private static void drawRoundImage(Canvas canvas, RectF frame, int borderColor) {
// General Declarations
Paint paint = CacheForRoundImage.paint;
// Oval
RectF ovalRect = CacheForRoundImage.ovalRect;
ovalRect.set(frame.left + 2f,
frame.top + 2f,
frame.right - 2f,
frame.bottom - 2f);
Path ovalPath = CacheForRoundImage.ovalPath;
ovalPath.reset();
ovalPath.addOval(ovalRect, Path.Direction.CW);
paint.reset();
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(3.5f);
paint.setStrokeMiter(10f);
canvas.save();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(borderColor);
canvas.drawPath(ovalPath, paint);
canvas.restore();
}
// Canvas Images
// Tab
public static Bitmap imageOfRoundImage(PointF imageSize, int borderColor) {
Bitmap imageOfRoundImage = Bitmap.createBitmap((int) imageSize.x, (int) imageSize.y, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfRoundImage);
StyleKit.drawRoundImage(canvas, new RectF(0f, 0f, imageSize.x/3, imageSize.y/3), borderColor);
return imageOfRoundImage;
}
// Resizing Behavior
public static void resizingBehaviorApply(ResizingBehavior behavior, RectF rect, RectF target, RectF result) {
if (rect.equals(target) || target == null) {
result.set(rect);
return;
}
if (behavior == ResizingBehavior.Stretch) {
result.set(target);
return;
}
float xRatio = Math.abs(target.width() / rect.width());
float yRatio = Math.abs(target.height() / rect.height());
float scale = 0f;
switch (behavior) {
case AspectFit: {
scale = Math.min(xRatio, yRatio);
break;
}
case AspectFill: {
scale = Math.max(xRatio, yRatio);
break;
}
case Center: {
scale = 1f;
break;
}
}
float newWidth = Math.abs(rect.width() * scale);
float newHeight = Math.abs(rect.height() * scale);
result.set(target.centerX() - newWidth / 2,
target.centerY() - newHeight / 2,
target.centerX() + newWidth / 2,
target.centerY() + newHeight / 2);
}
}
And these are the methods for displaying the shapes:
private void paintOverImage(int[] centerOfShape) {
Bitmap firstImage = BitmapFactory.decodeResource(getResources(), R.drawable.space1);
PointF pointF = new PointF(125, 125);
Bitmap overlayImage = StyleKit.imageOfRoundImage(pointF, Color.BLACK);
Bitmap mergedImages = mergeImages(firstImage, overlayImage, centerOfShape);
changedImageImv.setImageBitmap(mergedImages);
}
private Bitmap mergeImages(Bitmap firstImage, Bitmap secondImage, int[] centerOfShape) {
Bitmap result = Bitmap.createBitmap(firstImage.getWidth(), firstImage.getHeight(), firstImage.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(firstImage, 0f, 0f, null);
canvas.drawBitmap(secondImage, 10, 10, null);
return result;
}
After all these lines of code nothing happens. The StyleKit class is correct, I mess something in the following methods. Also the centerOfShape table contains to values which are the coordinates that the shape should appear.

Apparently I was loading all the time the original image. Here is the solution:
Bitmap firstImage = ((BitmapDrawable) changedImageImv.getDrawable()).getBitmap();

Related

android canvas draw text in the triangle

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

How to create a partially-rounded-corners-rectangular drawable with center-crop and without creating new bitmap?

Background
I've already seen how to create a drawable that's circular out of a bitmap, and also how to add an outline (AKA stroke) around it, here.
The problem
I can't find out how to do a similar task for rounding only some of the corners of the bitmap, inside the drawable, without creating a new bitmap, and still do it for a center-crop ImageView.
What I've found
This is what I've found, but it does create a new bitmap, and when using it in an imageView with center-crop (source here):
/**
* Create rounded corner bitmap from original bitmap.
*
* #param input Original bitmap.
* #param cornerRadius Corner radius in pixel.
* #param squareTL,squareTR,squareBL,squareBR where to use square corners instead of rounded ones.
*/
public static Bitmap getRoundedCornerBitmap(final Bitmap input, final float cornerRadius, final int w, final int h,
final boolean squareTL, final boolean squareTR, final boolean squareBL, final boolean squareBR) {
final Bitmap output = Bitmap.createBitmap(w, h, Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Rect rect = new Rect(0, 0, w, h);
final RectF rectF = new RectF(rect);
// make sure that our rounded corner is scaled appropriately
Paint paint = new Paint();
paint.setXfermode(null);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
// draw rectangles over the corners we want to be square
if (squareTL)
canvas.drawRect(0, 0, w / 2, h / 2, paint);
if (squareTR)
canvas.drawRect(w / 2, 0, w, h / 2, paint);
if (squareBL)
canvas.drawRect(0, h / 2, w / 2, h, paint);
if (squareBR)
canvas.drawRect(w / 2, h / 2, w, h, paint);
paint.setXfermode(PORTER_DUFF_XFERMODE_SRC_IN);
canvas.drawBitmap(input, 0, 0, paint);
return output;
}
And, this is what I've found for creating a rounded corners drawable that acts on all corners:
public static class RoundedCornersDrawable extends Drawable {
private final float mCornerRadius;
private final RectF mRect = new RectF();
private final BitmapShader mBitmapShader;
private final Paint mPaint;
public RoundedCornersDrawable(final Bitmap bitmap, final float cornerRadius) {
mCornerRadius = cornerRadius;
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(false);
mPaint.setShader(mBitmapShader);
mRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
}
#Override
protected void onBoundsChange(final Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(0, 0, bounds.width(), bounds.height());
}
#Override
public void draw(final Canvas canvas) {
canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}
But this solution only works well if the imageView shows the content while maintaining the same aspect ratio as the bitmap, and also has its size pre-determined.
The question
How to create a center-cropped drawable, that shows a bitmap, has rounded corners for specific corners, and also be able to show an outline/stroke around it?
I want to do it without creating a new bitmap or extending ImageView. Only use a drawable that has the bitmap as the input.
The SMART way is to use the PorterDuff blending mode. It's really simple and slick to create any fancy shading, blending, "crop" effect. you can find a lot of good tutorial about PorterDuff. here a good one.
I always use this library to achieve what you are looking for. you can round any corner you want and also add stroke.
https://github.com/vinc3m1/RoundedImageView
You can use it or see it's source codes just for inspiration.
EDIT
there is no need to use Image View and make bitmap or drawable yourself and show it in Image View.
Just replace Image View with Rounded Image View and it will handle everything for you without any extra work in code !
here is sample :
<com.makeramen.roundedimageview.RoundedImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageView1"
android:scaleType="centerCrop"
app:riv_corner_radius="8dp"
app:riv_border_width="2dp"
app:riv_border_color="#333333"
app:riv_oval="false" />
In code, just pass any image resource to it or use any Image Loader with it.
RoundedImageView myRoundedImage=(RoundedImageView)findViewById(R.id.imageView1);
myRoundedImage.setImageResource(R.drawable.MY_DRAWABLE);
// OR
ImageLoader.getInstance().displayImage(YOUR_IMAGE_URL, myRoundedImage);
if you want to just make specific corners rounded try this:
<com.makeramen.roundedimageview.RoundedImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/imageView1"
android:scaleType="centerCrop"
app:riv_corner_radius_top_right="8dp"
app:riv_corner_radius_bottom_right="8dp"
app:riv_border_width="2dp"
app:riv_border_color="#333333"
app:riv_oval="false" />
Well, you can create a new .xml drawable named my_background and paste this code below:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<solid android:color="#00000000"/>
<corners
android:bottomLeftRadius="12dp"
android:bottomRightRadius="12dp"
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />
<stroke
android:width="1dp"
android:color="#000000"
/>
</shape>
Then, you set your ImageButton's, ImageView's background to your new drawable, like this:
android:background="#drawable/my_background"
android:scaleType="centerCrop"
or programatically:
myView.setBackground(R.drawable.my_background);
Hope it helps!
EDIT:
To programmatically create a similar drawable, you can use it:
GradientDrawable shape = new GradientDrawable();
shape.setShape(GradientDrawable.RECTANGLE);
shape.setCornerRadii(new float[] { 8, 8, 8, 8, 0, 0, 0, 0 });
shape.setColor(Color.TRANSPARENT);
shape.setStroke(3, Color.BLACK);
v.setBackgroundDrawable(shape);
Ok, here's my try. The only gotcha is that "int corners" is meant to be a set of flags. Such as 0b1111 where each 1 represents a corner to be rounded, and 0 is the opposite. The order is TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT.
example usage first, formatted for readability:
((ImageView) findViewById(R.id.imageView)).setImageDrawable(
new RoundedRectDrawable(
getResources(),
bitmap,
(float) .15,
0b1101,
8,
Color.YELLOW
)
);
code:
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.SystemClock;
/**
* Created by mliu on 4/15/16.
*/
public class RoundedRectDrawable extends BitmapDrawable {
private final BitmapShader bitmapShader;
private final Paint p;
private final RectF rect;
private final float borderRadius;
private final float outlineBorderRadius;
private final int w;
private final int h;
private final int corners;
private final int border;
private final int bordercolor;
public RoundedRectDrawable(final Resources resources, final Bitmap bitmap, float borderRadiusSeed, int corners, int borderPX, int borderColor) {
super(resources, bitmap);
bitmapShader = new BitmapShader(getBitmap(),
BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
final Bitmap b = getBitmap();
p = getPaint();
p.setAntiAlias(true);
p.setShader(bitmapShader);
w = b.getWidth();
h = b.getHeight();
rect = new RectF(0,0,w,h);
borderRadius = borderRadiusSeed * Math.min(w, h);
border = borderPX;
this.corners = corners;
this.bordercolor = borderColor;
outlineBorderRadius = borderRadiusSeed * Math.min(w+border,h+border);
}
#Override
public void draw(final Canvas canvas) {
if ((corners&0b1111)==0){
if (border>0) {
Paint border = new Paint();
border.setColor(bordercolor);
canvas.drawRect(rect, border);
}
canvas.drawRect(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border, p);
} else {
if (border >0) {
Paint border = new Paint();
border.setColor(bordercolor);
canvas.drawRoundRect(rect, outlineBorderRadius, outlineBorderRadius, border);
if ((corners & 0b1000) == 0) {
//top left
canvas.drawRect(rect.left, rect.top, rect.width() / 2, rect.height() / 2, border);
}
if ((corners & 0b0100) == 0) {
//top right
canvas.drawRect(rect.width() / 2, rect.top, rect.width(), rect.height() / 2, border);
}
if ((corners & 0b0010) == 0) {
//bottom left
canvas.drawRect(rect.left, rect.height() / 2, rect.width() / 2, rect.height(), border);
}
if ((corners & 0b0001) == 0) {
//bottom right
canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width(), rect.height(), border);
}
}
canvas.drawRoundRect(new RectF(rect.left + border, rect.top + border, rect.width() - border, rect.height() - border), borderRadius, borderRadius, p);
if ((corners & 0b1000) == 0) {
//top left
canvas.drawRect(rect.left + border, rect.top + border, rect.width() / 2, rect.height() / 2, p);
}
if ((corners & 0b0100) == 0) {
//top right
canvas.drawRect(rect.width() / 2, rect.top + border, rect.width() - border, rect.height() / 2, p);
}
if ((corners & 0b0010) == 0) {
//bottom left
canvas.drawRect(rect.left + border, rect.height() / 2, rect.width() / 2, rect.height() - border, p);
}
if ((corners & 0b0001) == 0) {
//bottom right
canvas.drawRect(rect.width() / 2, rect.height() / 2, rect.width() - border, rect.height() - border, p);
}
}
}
}
So, this handles the outline first, if needed, then the bitmap. It marks the canvas up with a rounded rect first, then "squares out" each corner you don't want to round. Seems highly inefficient, and probably is, but average case run time before minimal optimizations (corners = 0b0000, 10 canvas.draw calls) takes ~ 200us on a S7. And, that time is SUPER inconsistent based on phone usage. I've gotten as low as 80us and as high as 1.5ms.
NOTES/WARNING: I am BAD at this. This is not optimal. There's probably better answers already here on SO. The subject matter is just a bit uncommon and difficult to search up. I was originally not going to post this, but at time of writing this is still not marked answered, and the library OP did not wish to use due to problems with their Drawable actually use a very similar approach as my terrible solution. So, now I'm less embarrassed to share this. Additionally, though what I posted today was 95% written yesterday, I know I got some of this code from a tutorial or a SO post, but I can't remember who to attribute cause I didn't end up using it in my project. Apologies whoever you are.

How to set a unique radius for Picasso RoundedCornerTransofrmation()

I have a Recycler view and the view containing n number of url images. The url images are different resolution. I am using Picasso image loader to load the images and set a RoundedCornerTransofrmation() with fixed radius. But different images are setting with different radius.
you can add this class in your package :
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
// enables hardware accelerated rounded corners
// original idea here : http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
#Override
public String key() {
return "rounded";
}
you can use above class like this :
Picasso.with(c).load(s[0]).placeholder(R.drawable.placeholder).transform(new RoundedTransformation(40,4)).error(c.getResources().getDrawable(R.drawable.placeholder)).into(holder.im);
I have found the solution to set fixed radius for different resolution images in Picasso roundedCornerTransformation()
Implement the custom class with implements com.squareup.picasso.Transformation
and modify the transform method as like below
#Override
public Bitmap transform(final Bitmap source) {
//Get the Image Width Form incoming image
int radius = source.getWidth();
//Set the percentage value for radius
radius = (radius*10)/100;
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
Based on the percentage value, we can set image curve radius as fixed.
Thanks.

Drawing Oval on canvas with PAINT object in Android

In the following code, I'm trying to draw an oval which be enlarged with time.
Bitmap currBitmap = null;
Canvas currCanvas = null;
//Config Paint Case2
final Paint currPaint = new Paint();
List BlocksList = null;
boolean bSet = false;
public void DrawOval(Bitmap src, int nRadiusprct)
{
// image size
int width = src.getWidth();
int height = src.getHeight();
//create bitmap output
if(currBitmap == null)
currBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// set canvas for painting
if(currCanvas == null)
{
currCanvas = new Canvas(currBitmap);
currCanvas.drawARGB(0, 0, 0, 0);
MainActivity.imgMain.setImageBitmap(currBitmap);
}
// config paint Case1
/*final Paint currPaint = new Paint();
currPaint.setAntiAlias(true);
currPaint.setColor(Color.BLACK);*/
// config paint Case2
if(!bSet)
{
currPaint.setAntiAlias(true);
currPaint.setColor(Color.BLACK);
// create Xfer mode
currPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
bSet = true;
}
// config rectangle for embedding
int nMidWidth = width/2;
int nMidHeight = height/2;
float fPercent = (nRadiusprct / 100.0f);
float fLeft = nMidWidth * (1 - fPercent);
float fRight = nMidWidth * (1 + fPercent);
float fTop = nMidHeight * (1 - fPercent);
float fBottom = nMidHeight * (1 + fPercent);
final Rect rect = new Rect(0, 0, width, height);
final RectF rectF = new RectF(fLeft, fTop, fRight, fBottom);
currCanvas.drawOval(rectF, currPaint);
// create Xfer mode, Config Paint Case1
//currPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
currCanvas.drawBitmap(src, rect, rect, currPaint);
MainActivity.imgMain.invalidate();
}
Now, As you can see write a comment "Config Paint Case1" or "Config Paint Case2", case1 represents the case where I create a paint instance each time the method is called, while case 2 represents where I define a member object in the class so I can use it whenever I need, the problem that when I'm using the first case everything is working perfectly and accurate, while when I'm using the second case nothing happened, The main thing that in my opinion I don't need to create a paint object each time so I need to optimize my code more and more, but why this happened here.....
Finally I got an answer to this question, A great help by this article Transparency with JPEGs done right that describes how PorterDuff modes are work, In that article I found this Xfermodes Example which gives me where is the error and here is the description.
Actually, I don't need to create a paint object everytime, what I need is to deal with the xfermodes in a correct way, the perfect code is:
Bitmap currBitmap = null;
Canvas currCanvas = null;
//Config Paint Case2
final Paint currPaint = new Paint();
List BlocksList = null;
boolean bSet = false;
public void DrawOval(Bitmap src, int nRadiusprct)
{
// image size
int width = src.getWidth();
int height = src.getHeight();
//create bitmap output
if(currBitmap == null)
currBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// set canvas for painting
if(currCanvas == null)
{
currCanvas = new Canvas(currBitmap);
MainActivity.imgMain.setImageBitmap(currBitmap);
}
// config paint Case2
if(!bSet)
{
currPaint.setAntiAlias(true);
currPaint.setColor(Color.BLACK);
bSet = true;
}
// config rectangle for embedding
int nMidWidth = width/2;
int nMidHeight = height/2;
float fPercent = (nRadiusprct / 100.0f);
float fLeft = nMidWidth * (1 - fPercent);
float fRight = nMidWidth * (1 + fPercent);
float fTop = nMidHeight * (1 - fPercent);
float fBottom = nMidHeight * (1 + fPercent);
final Rect rect = new Rect(0, 0, width, height);
final RectF rectF = new RectF(fLeft, fTop, fRight, fBottom);
Xfermode BeforEPaintXferMode = currPaint.getXfermode();
currCanvas.drawOval(rectF, currPaint);
// create Xfer mode
currPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// draw source image to canvas
currCanvas.drawBitmap(src, rect, rect, currPaint);
currPaint.setXfermode(BeforEPaintXferMode);
MainActivity.imgMain.invalidate();
}
Now, as you can see I just storing the current xfermode before painting, then I set it to SRC_IN mode and finally, I go back to the original one. and everything is working perfectly.

Drawing text on canvas appropriately

I am try to draw text and bitmap images in my app. I would like the text to be drawn above the bitmap icons but am having difficulties in achieving that.
How do i modify or change my code so it displays at the top of each icon
my code:
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
xCanvas = canvas.getWidth();
yCanvas = canvas.getHeight();
Paint textPaint2 = new Paint();
textPaint2.setStyle(Paint.Style.FILL_AND_STROKE);
textPaint2.setAntiAlias(true);
textPaint2.setColor(Color.WHITE);
textPaint2.setTextSize(30);
textPaint2.setTextAlign(Align.CENTER);
destination = new Location("manual");
for (int j = 0; j < placesListItems.size(); j++){
song = placesListItems.get(j);
this.lat = myLat.get(j);
this.lng = myLng.get(j);
this.name=song.get(KEY_NAME);
try {
this.icon = ICON.get(j);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Log.d("the latitude",(String.valueOf(this.lat)));
// Log.d("the longitude",(String.valueOf(this.lng)));
// Double.parseDouble(song.get(KEY_LNG));
destination.setLatitude(this.lat);
destination.setLongitude(this.lng);
//Log.d("Place name",name );
this.location.distanceTo(destination);
// compute rotation matrix
float rotation[] = new float[9];
float identity[] = new float[9];
if (lastAccelerometer != null && lastCompass != null) {
boolean gotRotation = SensorManager.getRotationMatrix(rotation,
identity, lastAccelerometer, lastCompass);
if (gotRotation) {
float cameraRotation[] = new float[9];
// remap such that the camera is pointing straight down the
// Y
// axis
SensorManager.remapCoordinateSystem(rotation,
SensorManager.AXIS_X, SensorManager.AXIS_Z,
cameraRotation);
// orientation vector
orientation = new float[3];
SensorManager.getOrientation(cameraRotation, orientation);
canvas.save();
// Translate, but normalize for the FOV of the camera --
// basically, pixels per degree, times degrees == pixels
float dx = (float) ((canvas.getWidth() / horizontalFOV) * (Math
.toDegrees(orientation[0]) - this.location
.bearingTo(destination)));
float dy = (float) ((canvas.getHeight() / verticalFOV) * Math
.toDegrees(orientation[1]));
// wait to translate the dx so the horizon doesn't get
// pushed off
canvas.translate(0.0f, 0.0f - dy);
// now translate the dx
canvas.translate(0.0f - dx, 0.0f);
canvas.drawText((truncate(this.name,10).concat("...")), canvas.getWidth()/2 - 50, canvas.getHeight() / 2 - 100,
textPaint2);
canvas.drawBitmap(icon, canvas.getWidth()/2 - icon.getWidth()/2, canvas.getHeight()/2 - icon.getHeight()/2, null);
canvas.restore();
}
}
}
}
The trick is to get hold of metrics for the font you're using. You can do this using
Paint.FontMetrics fm = textPaint2.getFontMetrics();
int fontHeight = fm.bottom - fm.top;
(tweaked to use bottom and top which seem to be more accurate)
You can then adjust the vertical location based on the real text size (rather than using arbitrary numbers)
Suppose you have an icon that you want to paint with two lines of text above it (a label and the coordinates), and you want the icon centered at x, y. The following example demonstrates this.
NOTE: You allocate Paint objects inside your draw method - this is really a bad idea, as the objects are always the same and just end up kicking off the garbage collector. A lot. Allocate the once and reuse. I demonstrate this as well.
I draw the crossed lines to point at the target x,y values; you won't need them, but it helps show exactly where the target is relative to the icon and text.
package com.javadude.sample;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.FontMetrics;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
}
public static class MyView extends View {
public MyView(Context context) {
super(context);
}
private Paint textPaint;
private Paint linePaint;
private Drawable drawable;
private float textHeight;
private float baselineOffset;
private void drawIconAndText(Canvas canvas, int centerX, int centerY, Drawable drawable, String text) {
// draw the drawable centered on (targetXcenter, targetYcenter)
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
// determine upper-left corner of drawable location
int x = centerX - w/2;
int y = centerY - h/2;
// draw the icon
drawable.setBounds(x, y, x+w, y+h);
drawable.draw(canvas);
float textY = y - baselineOffset;
// note that drawText centers the text at the given location due to Align.CENTER
canvas.drawText(text, centerX, textY, textPaint);
// if you had used Align.LEFT, you would need to offset the start of the text as follows:
// float textWidth = textPaint.measureText(text);
// canvas.drawText(text, centerX - textWidth/2, textY, textPaint);
// draw the coordinates above it
textY = textY - textHeight; // move up a line
canvas.drawText("(" + centerX + "," + centerY + ")", centerX, textY, textPaint);
}
private void initPaint() {
linePaint = new Paint();
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setColor(Color.BLUE);
textPaint = new Paint();
textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
textPaint.setAntiAlias(true);
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(30);
textPaint.setTextAlign(Align.CENTER);
FontMetrics fontMetrics = textPaint.getFontMetrics();
baselineOffset = fontMetrics.bottom;
// bottom is the maximum amount that the text descends
// I'm not sure why this is different from descent...
textHeight = fontMetrics.bottom - fontMetrics.top;
drawable = getResources().getDrawable(R.drawable.ic_launcher);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = canvas.getWidth();
int height = canvas.getHeight();
int targetXcenter = width/2;
int targetYcenter = height/2;
// only allocate objects once it at all possible!
if (textPaint == null)
initPaint();
canvas.drawColor(Color.BLACK); // draw background
// draw lines to show where the target is
canvas.drawLine(0, targetYcenter, width-1, targetYcenter, linePaint);
canvas.drawLine(targetXcenter, 0, targetXcenter, height-1, linePaint);
drawIconAndText(canvas, targetXcenter, targetYcenter, drawable, "Sample Text");
// draw lines to show where the target is
canvas.drawLine(0, 200, width-1, 200, linePaint);
canvas.drawLine(100, 0, 100, height-1, linePaint);
drawIconAndText(canvas, 100, 200, drawable, "More Text");
}
}
}

Categories

Resources