Object allocation during draw/layout? - java

I get 3 warning for object allocation during draw/layout
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
Paint textPaint = new Paint();
textPaint.setARGB(50,100,100,250);
textPaint.setTextAlign(Align.CENTER);
textPaint.setTextSize(50);
textPaint.setTypeface(font);
canvas.drawText("Logan is awesom",canvas.getWidth()/2,200,textPaint);
canvas.drawBitmap(pBall, (canvas.getWidth()/2), changingY, null);
if (changingY <canvas.getHeight()){
changingY += 10;
}else{
changingY=0;
}
Rect middleRect = new Rect();
middleRect.set(0, 400, canvas.getWidth(), 550);
Paint ourBlue = new Paint();
ourBlue.setColor(Color.BLUE);
canvas.drawRect(middleRect, ourBlue);
I get an error on new Rect();
and on both new Paint();
The exact error is avoid object allocation during draw/layout operations (prelocate and reuse instead)

Well, your 'error' points to exact problem. onDraw() method is called by OS many-many times, thus allocating something within this function is extremely bad idea.
You need to allocate your Rect and Paint beforehand and just use them within onDraw
class YourClass extends View
{
Rect middleRect;
Paint ourBlue;
Paint textPaint;
public YourClass()
{
//constructor
init();
}
private void init()
{
middleRect = new Rect();
ourBlue; = new Paint();
textPaint = new Paint();
ourBlue.setColor(Color.BLUE);
textPaint.setARGB(50,100,100,250);
textPaint.setTextAlign(Align.CENTER);
textPaint.setTextSize(50);
textPaint.setTypeface(font);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.drawText("Logan is awesom",canvas.getWidth()/2,200,textPaint);
canvas.drawBitmap(pBall, (canvas.getWidth()/2), changingY, null);
if (changingY <canvas.getHeight()){
changingY += 10;
}else{
changingY=0;
}
//if canvas size doesn't change - this can be moved to init() as well
middleRect.set(0, 400, canvas.getWidth(), 550);
canvas.drawRect(middleRect, ourBlue);
}
}

For me, I have got this error in layout especially when using display metrices.
I have used this imageview :
<com.steve.thirdparty.ScalableImageView
android:id="#+id/iv_posted_img_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:background="#drawable/golive_load_image"
android:layout_below="#+id/tv_user_posted_msg_post_items_home"
android:contentDescription="#string/cont_desc"/>
ScalableImageView.java:
import static com.steve.TabHomeActivity.displaymetrics;
public class ScalableImageView extends ImageView {
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
}
I solved this issue by adding the static DisplayMetrices in onCreate() method in Activity.
TabHomeActivity.java:
public static DisplayMetrics displaymetrics;
*inside onCreate()*
displaymetrics = new DisplayMetrics();

avoid object allocation during draw/layout operations (prelocate and reuse instead)
The message give You a solution on plate, so I don't understand what is Your question.
You have to move creation of new objects to onCreate method, so they are created just once and use them later in onDraw method.

Related

Cannot use context in class?

I'm currently trying to put in a color from my colors resources xml into my app using ContextCompact.getColor() but for some reason I cannot pass in a single version of context.
I'm using a class as a handler so I am not trying to pass in from an activity. In my activity I can use them, but in my class I cannot pass in getActivityContext() this etc. Nothing works. What do I do?
Also, I'm adding the color to a canvas so I cannot add the color in in an xml.
canvas.drawColor(Color.BLACK);
Is what I'm currently forced to use. I want to replace it with a color from my xml. ( I'm trying to set the background of the canvas essentially )
full code of my class: (I'm making this app as a "note" app so that I can look back on it for future projects, hence all the comments)
public class GameHandling {
private SurfaceHolder holder;
private Resources resources;
private int screenWidth;
private int screenHeight;
private Ball ball;
private Bat player;
private Bat opponent;
public GameHandling(int width, int height, SurfaceHolder holder, Resources resources){
this.holder = holder;
this.resources = resources;
this.screenWidth = width;
this.screenHeight = height;
this.ball = new Ball(screenWidth, screenHeight, 400, 400);
this.player = new Bat(screenWidth, screenHeight, 0, 0, Bat.Position.LEFT);
this.opponent = new Bat(screenWidth, screenHeight, 0, 0, Bat.Position.RIGHT);
}
// setting the ball images to be drawn
public void inIt(){
Bitmap ballShadow = BitmapFactory.decodeResource(resources, R.mipmap.grey_dot);
Bitmap ballImage = BitmapFactory.decodeResource(resources, R.mipmap.red_dot);
Bitmap batPlayer = BitmapFactory.decodeResource(resources, R.mipmap.bat_player);
Bitmap batOpponent = BitmapFactory.decodeResource(resources, R.mipmap.bat_opponent);
ball.inIt(ballImage, ballShadow, 2, 0);
player.inIt(batPlayer, batPlayer, 0, 0);
opponent.inIt(batOpponent, batOpponent, 0, 0);
}
// calling Balls update method to update the ball
public void update(long elapsed){
ball.update(elapsed);
}
public void draw(){
Canvas canvas = holder.lockCanvas(); // Making a canvas object to draw on - .lockcanvas locks canvas
if(canvas != null) {
// draw in area between locking and unlocking
canvas.drawColor(Color.BLACK);
ball.draw(canvas);
player.draw(canvas);
opponent.draw(canvas);
holder.unlockCanvasAndPost(canvas); //-unlockcanvasandposts unlocks the canvas
}
}
}
Change your constructor to this and use ContextCompat.getColor(context,... pattern.
Wherever you are creating this class (activity/fragment), pass the context calling either getActivity() or getApplicationContext()
new GameHandling( getActivity()/getApplicationContext(), ...)
public GameHandling(Context context, int width, int height, SurfaceHolder holder, Resources resources){
this.context = context;
this.holder = holder;
this.resources = resources;
this.screenWidth = width;
this.screenHeight = height;
this.ball = new Ball(screenWidth, screenHeight, 400, 400);
this.player = new Bat(screenWidth, screenHeight, 0, 0, Bat.Position.LEFT);
this.opponent = new Bat(screenWidth, screenHeight, 0, 0, Bat.Position.RIGHT);
}
I kind of came up with a workaround, I created an image with my desirable color and then used that as the background of the app.
However it is still a workaround, so it doesn't solve my issue

onDraw Method not Called?

I am creating a custom view but the onDraw method is never being called. I tried doing this - setWillNotDraw(false) but it still doesn't work.
In fact, it doesn't even work after calling invalidate(). Here's my code -
public class Box extends View {
public Box (Context context) {
super (context);
init();
}
private void init() { // Initialize paint object
paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(mBoxColor);
setMeasuredDimension(200, 200);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(100, 100, 200, 200, paint);
}
}
My activity code -
Box box = new Box(this);
Please help
In your MainActivity.java
use
setContentView(box);

Drawing Mulitple Views with Paths (Android)

I've been having a lot of issues drawing multiple paths in the same relative layout. What happens is that all my paths are drawn on the same spot they were originally drawn. Instead I want to shrink each drawn path/canvas and have them displayed side by side on the page.
My code to draw the paths looks like
for (int x=0; x < paths.size(); x++){
DrawView dw = new CustomView(this);
dw.path = paths.get(x);
dw.paint = paints.get(x);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (x == 0){
} else {
params.addRule(RelativeLayout.BELOW, x-1);
}
dw.setId(x);
layout.addView(dw, params);
}
I followed other suggestions for TextViews to add an custom layout parameter to display each TextView below each other but this does not appear to work for dynamically drawn paths.
Note: CustomView is a class that extends View and overwrites the onDraw method to draw out my paths.
EDIT:
If it helps my custom class looks like
public class CustomView extends View {
public Path path;
public Paint paint = new Paint();
public CustomView(Context context){
super(context);
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas){
canvas.drawPath(path, paint);
}
}
Is it necessary for you to use a RelativeLayout? If it is not too binding, consider having a LinearLayout which either replaces the RelativeLayout, or is contained within the RelativeLayout. Then you can add all the CustomViews to this LinearLayout, setting the weight field in LinearLayout.LayoutParams to 1.
Alternatively, consider manually setting each CustomView's height to getHeight()/(paths.size()) instead of RelativeLayout.LayoutParams.WRAP_CONTENT.

Bitmap.decodeResource resizing image

I have this method where I paint some text over an image:
public BitmapDrawable writeOnDrawable(int drawableId, String text){
Bitmap bm = BitmapFactory.decodeResource(getResources(), drawableId);
Typeface mFace = Typeface.createFromAsset(this.getAssets(),"fonts/progbot.ttf");
Paint paint = new Paint();
paint.setStyle(Style.FILL);
paint.setColor(Color.RED);
paint.setTypeface(mFace);
paint.setTextSize(30);
Canvas canvas = new Canvas(bm);
canvas.drawText(text, 0, bm.getHeight()/2, paint);
return new BitmapDrawable(bm);
}
This is how I call this method:
lineIconView.setImageDrawable(writeOnDrawable(R.drawable.icon_line_numbre, linea.getNumero()));
This ImageView (lineIconView) already has the R.drawable.icon_line_numbre resource set. If I don't call the writeOnDrawable function then the image is shown in its original size, but after calling it the image gets heavily reduced in size.
Is this normal behavior?
Turns out I was using the wrong constructor for BitmapDrawable.
The official documentation says the following on this matter:
BitmapDrawable(Bitmap bitmap) This constructor is deprecated. Use
BitmapDrawable(Resources, Bitmap) to ensure that the drawable has
correctly set its target density.
So I ended up using:
return new BitmapDrawable(bm);

View in Android taking the whole screen- how to tell the canvas to respect the limits of the view?

EDIT:
OK, so I've extended the lenght and the width of the view to a big part of the screen I can assume that the onDraw() method is NOT TAKING the limits of the view, because it goes through all the screen as I can suppose. So the question now would be how to tell the canvas to respect the limits of the view? Any ideas, maybe forcing some parameters?
END OF EDIT
I have implemented some code for drawing a custom view, but the canvas is blank. I am sure it is running, because I have added log traces, and those are showing up just fine.
How do I properly implement drawing for custom views, and what is wrong with what I've done below?
My custom view that goes inside a layout like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayout1" android:layout_height="fill_parent"
android:layout_width="fill_parent">
<pis.core.JMI_VistaMenuIn android:layout_height="150dip" android:layout_width="150dip" android:layout_alignParentTop="true" android:id="#+id/viewAnd01" ></pis.core.JMI_VistaMenuIn>
<Button android:layout_width="wrap_content" android:id="#+id/btnJugar" android:layout_height="wrap_content" android:text="Jugar" android:layout_below="#+id/viewAnd01" android:layout_alignLeft="#+id/viewAnd01" android:layout_alignRight="#+id/viewAnd01"></Button>
</RelativeLayout>
The customview is the one called: pis.core.JMI_VistaMenuIn. In the preview I got an "java.lang.UnsupportedOperationException" but when the .apk is loaded the xml layout WORKS.
Here is the code for my custom view. As I said before, the lines that contain the logging are getting hit, but my canvas is blank:
public class JMI_VistaMenuIn extends View {
public static final String QUOTE = "Mind Logic+";
public Animation anim;
//Default constructors
public void createAnim(Canvas canvas) {
Log.i("SC", "Exe canvas 01");
anim = new RotateAnimation(0, 360, canvas.getWidth() / 2, canvas
.getHeight() / 2);
anim.setRepeatMode(Animation.REVERSE);
anim.setRepeatCount(Animation.INFINITE);
anim.setDuration(10000L);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
startAnimation(anim);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// creates the animation the first time
if (anim == null) {
createAnim(canvas);
}
Path circle = new Path();
int centerX = canvas.getWidth() / 2;
int centerY = canvas.getHeight() / 2;
int r = Math.min(centerX, centerY);
circle.addCircle(centerX, centerY, r, Direction.CW);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(30);
paint.setAntiAlias(true);
canvas.drawTextOnPath(QUOTE, circle, 0, 30, paint);
Log.i("SC", "Exe canvas 02");
}
}
Similar code works just fine for me, including the animation. You have another issue somewhere else in your code: try setting the paint style.
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(30);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);

Categories

Resources