I'm developing a custom view and I need to draw drawables inside.
Those drawable must have their position relative.
Here is my code :
#Override
protected void onDraw(Canvas canvas) {
//drawBackground(canvas);
float height = (float) getHeight();
float width = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(width, height);
drawFlowChart(canvas);
drawDrawables(canvas);
canvas.restore();
}
private void drawDrawables(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.motor);
canvas.drawBitmap(bitmap, 0.51f, 0.35f, null);
canvas.restore();
}
But it draw nothing.
Any idea ?
I tried to use the drawBitmap(bitmap, src, dest, paint) but I don't know how to position my bitmap with relative position with this definition.
May be getHeight() and getWidth() function that you call in onDraw return 0 so that canvas scaled and nothing to see.
You can refer two that link to update width and height of canvas to draw:
Android getWidth() return 0 in View's onDraw
Android: How to get a custom View's height and width?
Related
Path drawn on scaled GLES20RecordingCanvas has quality as if it was drawn unscaled in bitmap and then up-scaled.
In contrast, if I create Canvas with backing bitmap and then apply the same scaling transformations to Canvas object I get much superior bitmap.
Here both circles are drawn with Path.addCircle and using Canvas.scale. Upper circle is drawn with scaled GLES20RecordingCanvas and lower is drawn with scaled simple Canvas with backing bitmap.
Some code:
public class TestPathRenderer extends View {
...
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
float distortedWidth = getDistortedWidth();
float distortedHeight = getDistortedHeight();
path.reset();
path.addCircle(distortedWidth/2f, distortedHeight/2f, Math.min(distortedWidth/2f, distortedHeight/2f), Path.Direction.CW);
bitmap = assembleNewBitmap(measuredWidth, measuredHeight);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (renderMode) {
case RENDER_MODE_WO_BITMAP:
drawOnCanvas(canvas);
break;
case RENDER_MODE_WITH_BITMAP:
canvas.drawBitmap(bitmap, 0f, 0f, paint);
break;
default:
throw new UnsupportedOperationException("Undefined render mode: " + renderMode);
}
}
private Bitmap assembleNewBitmap(int w, int h) {
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawOnCanvas(canvas);
return bitmap;
}
private void drawOnCanvas(#NonNull Canvas canvas) {
canvas.save();
canvas.scale(DISTORTION_FACTOR, DISTORTION_FACTOR);
canvas.drawPath(path, paint);
canvas.restore();
}
}
Full example
I can't understand the quality difference with these two cases. For me it seems that they have to be interchangeable.
Poor quality during scaling is a limitation of Hardware Acceleration according to Android docs.
After seeing your question, I decided to check out the source code of the GLES20RecordingCanvas class. And here's what I found out:
GLES20RecordingCanvas extends from GLES20Canvas which extends from HardwareCanvas. This HardwareCanvas class extends from Canvas. But the main difference I noticed is that it overrides the isHardwareAcceleratedMethod() returning true.
So my assumption is that the GLES20RecordingCanvas renders the bitmap with Hardware Acceleration while Canvas doesn't. And this is probably why you get less quality with the GLES20RecorgingCanvas.
I want to diasplay image(bitmap) in SurfaceView using Canvas. My requirement is that size of the canvas greater than image.I need space(eg:10 dp) in each side of the image in Canvas. How I can scale this?
Thanks
Mikahail
You can draw rectangle of desired Canvas size, and then draw your image with parameters (11, 11) which will draw it 10px from corner for each dimension.
Put this method in your SurfaceView Class :D
public void draw(Canvas canvas) {
final float scaleFactoryX = getWidth() / (WIDTH * 1.f);
final float scaleFactoryY = getHeight() / (HEIGHT * 1.f);
if (canvas != null) {
final int savedState = canvas.save();
canvas.scale(scaleFactoryX, scaleFactoryY);
canvas.restoreToCount(savedState);
}
}
I try to implement my own drawable that will corss fade from a simple colored rectangle (that should have the same size as the ImageView where the drawable is in) to a loaded bitmap (over http).
So what I do is override the draw() method of my Drawable:
public void draw(Canvas canvas) {
boolean done = true;
Log.d("Test",
"canvas w: " + canvas.getWidth() + " " + canvas.getHeight());
final int alpha = mAlpha;
final boolean crossFade = mCrossFade;
Paint paint = mStartPaint;
paint.setColor(blueColor);
if (!crossFade || 255 - alpha > 0) {
if (crossFade) {
paint.setAlpha(255 - alpha);
}
// Draw the rect with the color
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
if (crossFade) {
paint.setAlpha(0xFF);
}
}
if (alpha > 0) {
Bitmap bitmap = mEnd;
paint = mEndPaint;
paint.setAlpha(alpha);
if (mBitmapScalingType == null)
canvas.drawBitmap(bitmap, mEndX, mEndY, paint);
else
mBitmapScalingType.draw(bitmap, canvas.getWidth(),
canvas.getHeight(), canvas, paint);
paint.setAlpha(0xFF);
}
if (!done) {
mHandler.post(mInvalidater);
}
}
So the canvas.getWidth() and canvas.getHeight() returns 128 (width) and 150 (height) which is the same as the ImageView has. But the result is:
The blue rectangle has not been painted over the complete canvas and I can't get figure out why.
The ImageView that displays my fadeable drawable has no specific scaletype set.
Any ideas what could be wrong?
I have also noticed that the width and height calculated from setBounds() is 180 x 212 px
#Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
final int width = right - left;
final int height = bottom - top;
Log.d("Test", "width: "+width+" height: "+height);
}
and if I draw the color with 180 x 212 px
canvas.drawRect(0, 0, 180, 212, paint);
the rectangular is now drawn to fill the canvas completely.
Any idea what could be wrong?
Btw. The ImageView is defined like that:
<ImageView android:id="#+id/playerPic"
android:layout_width="64dp"
android:layout_height="75dp"
android:layout_marginRight="10dp"
/>
in xhdpi 64dp = 128px and 75dp = 150 px
This answer doesn't explain the difference in dimensions, but sometimes it's easier to just do things a different way.
If you just want to fill the canvas with a color, you don't need to use drawRect(), just use drawColor(). There's the simple version that just takes a Color, and one that lets you specify a porter-duff mode. This way you don't need dimensions at all, and you can avoid the whole thing.
Maybe you can try to use DisplayMetrics to get the dimensions.
//Get the width and height of the screen
DisplayMetrics d = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(d);
int width = d.widthPixels;
int height = d.heightPixels;
Then just call drawrect with those values
canvas.drawRect(0, 0, width, height, paint);
In my custom views OnDraw method I draw a Bitmap to the center of the Canvas with
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect r = canvas.getClipBounds();
displayWidth = r.right;
displayHeight = r.bottom;
camera.applyToCanvas(canvas);
float zW = (float)bitmapWidth / (float)displayWidth;
float zH = (float)bitmapHeight / (float)displayHeight;
float z = 0.0f;
if (zW>1 || zH>1) {
z = Math.max(zW, zH);
}
canvas.drawColor(Color.DKGRAY);
canvas.drawBitmap(bitmap, (displayWidth/2.0f - (bitmapWidth)/2.0f), (displayHeight/2.0f - bitmapHeight/2.0f), paint);
if (z>0) {
camera.translate(z, -z, z);
}
}
If the Bitmap is larger in height or width is larger then Canvas size (displayWidth, displayHeight), how can I use the Camera class to autozoom to fit the Bitmap and center it to the Canvas. Any ideas?
Try to create a Matrix instance and initialize it using
public boolean setRectToRect (RectF src, RectF dst, Matrix.ScaleToFit stf)
You need to do this only once and keep the matrix in memory. Note that Matrix.ScaleToFit define the CENTER value.
Later when you draw the bitmap use this version of the drawBitmap:
public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)
I need to rotate an ImageView by a few degrees. I'm doing this by subclassing ImageView and overloading onDraw()
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.scale(0.92f,0.92f);
canvas.translate(14, 0);
canvas.rotate(1,0,0);
super.onDraw(canvas);
canvas.restore();
}
The problem is that the image that results shows a bunch of jaggies.
http://img.skitch.com/20100608-gmdy4sexsm1c71di9rgiktdjhu.png
How can I antialias an ImageView that I need to rotate in order to eliminate jaggies? Is there a better way to do this?
If you know that your Drawable is a BitmapDrawable, you can use anti-aliasing in the bitmap's Paint to do something like the following:
/**
* Not as full featured as ImageView.onDraw(). Does not handle
* drawables other than BitmapDrawable, crop to padding, or other adjustments.
*/
#Override
protected void onDraw(Canvas canvas) {
final Drawable d = getDrawable();
if( d!=null && d instanceof BitmapDrawable && ((BitmapDrawable)d).getBitmap()!=null ) {
final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
final int paddingLeft = getPaddingLeft();
final int paddingTop = getPaddingTop();
canvas.save();
// do my rotation and other adjustments
canvas.scale(0.92f,0.92f);
canvas.rotate(1,0,0);
if( paddingLeft!=0 )
canvas.translate(paddingLeft,0);
if( paddingTop!=0 )
canvas.translate(0,paddingTop);
canvas.drawBitmap( ((BitmapDrawable)d).getBitmap(),0,0,p );
canvas.restore();
}
}
Set antialias and filterBitmap to the paint that draw's the bitmap.
I had a similar problem and this worked for me:
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
I did the following to get it to work:
In AndroidManifest.xml I enabled hardware-acceleration <application android:hardwareAccelerated="true">.
Instead of using a custom draw-method, I changed the layer-type of the parent-view to hardware-accelerated with anti-aliasing enabled and added my subviews as follows:
Paint layerPaint = new Paint();
layerPaint.setAntiAlias(true);
layerPaint.setFilterBitmap(true);
layerPaint.setDither(true);
RelativeLayout parentLayout = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.myLayout, null);
parentLayout.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint);
parentLayout.addView(myChildView);
I have to add that clipping cannot be disabled when using this approach.
This rotation solves just one part of problem - jagged edges, but the image instead became wierd.
ifound only one solution yet:
in ondraw or dispatch draw method
Bitmap bmp = ((BitmapDrawable)image.getDrawable()).getBitmap();
double scale = (0.0+bmp.getWidth()+40.0)/getWidth();
if (scale > 1){
bmp = Bitmap.createScaledBitmap(bmp, getWidth()-40, (int) (bmp.getHeight()/scale), true);
}
canvas.drawColor(Color.TRANSPARENT);
canvas.save();
canvas.translate(20, yShift);
int left = borderSize;
int top = borderSize;
int right = bmp.getWidth() - borderSize;
int bottom = bmp.getHeight() - borderSize;
if(showText){
mPaint.setColor(Color.WHITE);
canvas.rotate(getAngel());
canvas.drawRect(left, top, right, bottom, mPaint);
canvas.translate(0,0);
textFrame.draw(canvas);
}else{
// rotaed bitmap
mMatrix.setRotate(getAngel());
// draw bitmap after creation of new rotated bitmap from rotated matrix
canvas.drawBitmap(Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mMatrix, true)
, 0, 0, mPaint);
}
canvas.restore();
#Primoz990's solution worked for me :) I also enabled Dithering, so my final code which removes the jagged edges on rotation is this:
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
bitmapPaint.setDither(true);