Avoid object allocations during draw/layout operations error in Android - java

I got this error in Android Studio:
Avoid object allocations during draw/layout operations
Code:
public class cizim extends View {
Path path = new Path();
Paint paint = new Paint();
public cizim (Context c){
super(c);
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
}
#Override
protected void onDraw(Canvas canvas){
canvas.setBitmap(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.foto));
canvas.drawPath(path, paint);
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event){
float x = event.getX();
float y = event.getY();
path.addCircle(x, y, 10, Path.Direction.CW);
invalidate();
return super.onTouchEvent(event);
}
}
I got the error with this code:
canvas.setBitmap(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.foto));
How can I resolve this problem?
I need your help.

Drawing a view is always a heavy task because onDraw method is invoked whenever the view needs to be drawn again, so imagine allocating/creating new objects in that method; it will certainly take a lot of time and will decrease your app performance.
So instead of creating your bitmap every time onDraw is called, create it only once in your view constructor.
Add a new instance variable to your class:
public class cizim extends View {
private Bitmap bitmap;
...
}
Then in your constructor, create that object as follows:
public cizim(Context context) {
super(context);
paint.setColor(Color.BLACK);
paint.setAntiAlias(true);
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.foto);
}
Finally modify your onDraw method to:
#Override
protected void onDraw(Canvas canvas){
canvas.setBitmap(bitmap);
canvas.drawPath(path, paint);
super.onDraw(canvas);
}
Edit:
There is actually another issue with your code that will result in UnsupportedOperationException when calling canvas.setBitmap. If you refer to the documentation, setBitmap will change the bitmap the canvas will draw to. Since you need to draw the bitmap on the screen, you cannot use setBitmap here. So what you actually need is canvas.drawBitmap
Change your onDraw method to:
#Override
protected void onDraw(Canvas canvas){
canvas.drawBitmap(bitmap, 0, 0, paint);
canvas.drawPath(path, paint);
super.onDraw(canvas);
}

Related

Java on android studio, why I cant see the Rectangle i drew in canvas?

Hi guys I recently started developing an android game, but i have encountered an issue with drawRectangle.
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLACK);
canvas.drawRect(new Rect(100,100,100,100), new Paint(Color.WHITE));
}
this Doesnt seem to work, but i have already drew on screen using another class with a draw method using the same logic but im curious why this doesnt work
private Paint myPaint = new Paint();
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLACK);
myPaint.setColor(Color.WHITE);
canvas.drawRect(new Rect(100,100,100,100), myPaint);
}
this doesnt work either
There are three issues in your implementation:-
Paint object doesn't take color in constructor, it takes a flag.
So you could have done something like Paint p = new Paint(Paint.ANTI_ALIAS_FLAG) and then set the color as p.setColor(Color.WHITE).
The Rect object should be something like new Rect(0,0,100,100).
In your case [new Rect(100,100,100,100)] the rectangle will be drawn as a rectangle with 0 width,
0 height and its upper left coordinate will be (100,100) and its
bottom right coordinate will be (100,100).
NEVER create objects in onDraw.
Paint's constructor doesn't take a color. It takes integer flags. So you just made a really weird paint object with every flag set, but you didn't set the color.
See: https://developer.android.com/reference/android/graphics/Paint.html#Paint(int)
This worked
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawColor(Color.BLACK);
myPaint.setColor(Color.WHITE);
canvas.drawRect(testRect, myPaint);
}
Where
private Rect testRect = new Rect(0,0,100,100}
private Paint myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

Change ImageResource Dinamically in Custom ImageView Android

I've working in this error all day and can not solve it. I have a custom ImageView as follows:
public class UserDirectionView extends ImageView {
private float mDirection;
private Drawable arrow;
private boolean mCorrectPosition;
// constructors...
#Override
protected void onDraw(Canvas canvas) {
if (arrow == null) {
arrow = getDrawable();
arrow.setBounds(0, 0, getWidth(), getHeight());
// setImageResource(resId); // Not working
}
canvas.save();
canvas.rotate(mDirection, getWidth() / 2, getHeight() / 2);
arrow.draw(canvas);
canvas.restore();
}
public void updateDirection(float direction, boolean correctPos) {
mDirection = direction;
mCorrectPosition = correctPos;
invalidate();
}
}
I am trying to change the imageResource with setImageResource(resId) but nothing is happening. I've tried to call the method inside of onDraw and also outside my class, like so:
UserDirectionView mUserHint = findview....;
mUserHint.setImageResource(resId);
But the image is not changing.
I've also tried to call invalidate, but didn't work.
How could I change the image resource dinamically?
You are missing the super.onDraw() call, which takes care of updating the drawable. Add
super.onDraw(canvas)
In your onDraw

Custom triangle using Canvas, want to place this view in List row

I have created a custom triangle using canvas, I created a class which is as follows:
#SuppressLint("DrawAllocation")
public class GameView extends View {
private Bitmap bmp;
public GameView(Context context) {
super(context);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.days2);
}
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setStrokeWidth(4);
paint.setColor(android.graphics.Color.RED);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setAntiAlias(true);
paint.setShadowLayer(4.0f, 0.0f, 2.0f, Color.BLACK);
setLayerType(LAYER_TYPE_SOFTWARE, paint);
Point a = new Point(0, 0);
Point b = new Point(canvas.getWidth(), canvas.getHeight()/2);
Point c = new Point(canvas.getWidth(),0);
Path path = new Path();
path.setFillType(FillType.EVEN_ODD);
path.lineTo(b.x, b.y);
path.lineTo(c.x, c.y);
path.lineTo(a.x, a.y);
path.close();
canvas.drawPath(path, paint);
canvas.rotate(36);
paint.setColor(Color.WHITE);
paint.setTextAlign(Align.CENTER);
paint.setTextSize(50);
canvas.drawText("Vertical 45 degree text", (canvas.getHeight()/2)-30 ,0-30 , paint);
}
}
I want to apply this shape to a listview row, however when I place a view in XML which refers to this class. It takes up my whole layout height. Which I dont want. If i specify a height in dp, the view gets clipped. How to overcome this?
Please dont mind the obect allocation in the onDraw, I will remove this and optimize once I get the thing going.

Android Custom Brush Colors

I'm trying to make custom brushes for my Android painting app. I started with Michael's code (found here) and I have managed to get .png brushes and use that as a bitmap and redraw it. It works fine but I can't change the colour. Tried using the setcolorfilter and colormatrixfilter but it doesn't seem to be working. Anyone knows how I can do this?
private Bitmap mBitmapBrush;
private Vector2 mBitmapBrushDimensions;
private List<Vector2> mPositions = new ArrayList<Vector2>(100);
private Paint mPanit;
public MyView(Context c) {
super(c);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mBitmapBrush = BitmapFactory.decodeResource(c.getResources(),R.drawable.brush1);
mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFAAAAAA);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
for (Vector2 pos : mPositions) {
canvas.drawBitmap(mBitmapBrush, pos.a, pos.b, mPanit);
}
invalidate();
}
When I tried using the Colormatrixfilter, the .set function was giving an error.
I had the same issue. In order to change the bitmap color you need to add color to your paint object and apply it into bitmap. See the working example here,
for (Vector2 pos : customBrushMap.get(p)) {
Paint paint = new Paint();
ColorFilter filter = new PorterDuffColorFilter(R.Color.GREEN, PorterDuff.Mode.SRC_IN);
paint.setColorFilter(filter);
canvas.drawBitmap(mBitmapBrush, pos.x, pos.y, paint);
}
Result,

Android Scratch card app

I am doing a small project for college and wondering if someone can tell me how I would go about doing a scratch card app. This app should have one image overlaying another. The one on top should allow the user to remove the image depending on where they rub on the image and thus part of the image that was removed revealing the image underneath. Pretty much like a scratch card. Any help would be great!
This is the code im using at the moment .
public class workinggraphics extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
setContentView(new Panel(this));
LinearLayout l1 = (LinearLayout) findViewById(R.id.LAYOUTTEST1);
Panel p1 = new Panel(null);
}
class Panel extends View{
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
// private Paint nPaint;
Bitmap bitmap;
Canvas pcanvas ;
int x = 0;
int y =0;
int r =0;
public Panel(Context context) {
super(context);
Log.v("Panel", "STROKE");
setFocusable(true);
setBackgroundColor(Color.TRANSPARENT);
/*
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
//WallpaperDrawable wallpaperDrawable=wallpaperManager.getDrawable();
try {
wallpaperManager.setBitmap(bmp);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
//setBackgroundDrawable(bmp);
// setting paint
/* nPaint = new Paint();
nPaint.setStyle(Paint.Style.FILL);*/
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//.Mode.DST_IN));
mPaint.setAntiAlias(false);
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.mainscreen_background);
Bitmap bm1 = BitmapFactory.decodeResource(getResources(),R.drawable.foreground_image);
// converting image bitmap into mutable bitmap
bitmap = bm.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888);
//bitmap = bm1.createBitmap(bm1.getWidth(),bm1.getHeight(),Config.ARGB_8888);
pcanvas = new Canvas();
pcanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
pcanvas.drawBitmap(bm, 0, 0, null);
pcanvas.drawBitmap(bm1,0,0,null);
}
#Override
protected void onDraw(Canvas canvas) {
Rect cBK = new Rect();
//canvas.set
cBK.set(0,0,canvas.getWidth(),canvas.getHeight());
//pcanvas.drawRect(cBK, nPaint);
// draw a circle that is erasing bitmap
pcanvas.drawCircle(x, y, r, mPaint);
//pcanvas.drawLine(x, y, 0, 0, mPaint);
canvas.drawBitmap(bitmap, 0, 0,null);
super.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// set paramete to draw circle on touch event
x = (int) event.getX();
y = (int) event.getY();
r =20;
// Atlast invalidate canvas
invalidate();
return true;
}
}
}
Now as you can see above there is a lot of comments for things we have tried. Ideally I would like to be able to create an instance of the Panel class, set the workinggraphics class contextview to a XML LinearLayout (OR SurfaceView w/e would be used I dont really know), and then just set a background image on the defined LinearLayout in XML to be revealed when the canvas erases the bitmap image we have set.
Anyway any suggestions would be appricated thanks alot in advanced!
I created a library call WScrarchView where you can implement scratch view just few lines in layout xml. Hope this can help those who still looking for the solution https://github.com/winsontan520/Android-WScratchView

Categories