I have the following code that blows the app. Methods like drawLine work fine if they are directly inside "onDraw" but not if placed outside in a separate method as shown below. Does anyone have any idea what's wrong? The version with the method compiles ok but the app crashes when started.
Many thanks
Roberto
public class Painter extends View {
private static Canvas canvas;
private static Paint paintG = null;
public static int screenW; // screen width
public static int screenH; // screen height
private String tag = "Painter";
public Painter(Context context) {
super(context);
readDrawingData();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
screenW = canvas.getWidth(); // screen width
screenH = canvas.getHeight(); // screen height
Rect allScreen = new Rect(0, 0, screenW, screenH);
// works OK
paintG = new Paint();
paintG.setAntiAlias(true);
paintG.setStrokeWidth(2);
paintG.setStyle(Paint.Style.FILL);
paintG.setColor(0xFFf7f9d2);
canvas.drawRect(allScreen, paintG);
// paint circle/oval - works OK
double R = ((screenW > screenH) ? screenH : screenW) / 2 - 100;
double Z = R + 20;
double X = screenW /2;
double Y = screenH /2;
RectF oval1 = new RectF(Math.round(X-R), Math.round(Y-R),
Math.round(X+R), Math.round(Y+R));
paintG.setStyle(Paint.Style.STROKE);
paintG.setColor(0xffff0000);
canvas.drawOval(oval1, paintG);
// this code works OK here
paintG.setColor(0xffff0000);
if (i > 0) {
for (int j=0; j<i; j++) {
canvas.drawLine(x1[j], y1[j], x2[j], y2[j], paintG);
}
}
// calling this will crash app
paintDrawing();
}
// ************************************************************
void paintDrawing() {
// PROBLEM: same code as above - causes app to crash...
// when outside onDraw
paintG.setColor(0xffff0000);
if (i > 0) {
for (int j=0; j<i; j++) {
canvas.drawLine(x1[j], y1[j], x2[j], y2[j], paintG);
}
}
// ************************************************************
}
Related
I have a custom view I created which takes up a fair amount of memory. The fragment that this view is attached to is recreated by a fragmentViewPager when the user goes to that page. The problem I am running into is that after 7 or 8 recreations I get an OutOfMemoryError. I know it is this view because when I remove it the OOM completely stops. My gander is that something is still holding a reference to the old view which causes the GC to skip it. To try and fix this I created a close method in the custom view that de-reference everything inside the custom view class, this did not seem to help either.
Each hump is when the view is recreated. trying to cause a GC by the button in the debugger doesn't cause any of the memory to be cleaned up.
public void close(){
bitmapFace .recycle();
largeLinePaint = null;
smallLinePaint = null;
dialIndicatorPaint = null;
solidSeekPaint = null;
textPaint = null;
dialShader = null;
dialPaint = null;
dial = null;
bitmapFace = null;
listener = null;
dialFacePoints = null;
matrix = null;
dialPath = null;
leftTrianglePath = null;
rightTrianglePath = null;
}
This is the method that I use to dereference everything; it is called from the onPause of the fragment. I cannot think of any other place that the view is being referenced from. Does anyone know why the GC is not cleaning up the memory.
Here is the heap dumb, I see that most all of the memory is in these two methods but I don't know why the memory is not getting cleaned up from the previous view.
private void initializeDial(){
float textPoint = .91f;
float largeLineEnd = .9f;
float largeLineStart = .8f;
float smallLineEnd = .86f;
float smallLineStart = .8f;
Bitmap bitmap = Bitmap.createBitmap((int)radius*2, (int)radius*2, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(bitmap);
int station = 87;
for(int x = 0; x < 106; x++){
if(x % 5 == 0){ //big line
canvas.drawLine(
(float) (radius),
(float) (radius - (largeLineStart * radius)),
(float) (radius),
(float) (radius - (largeLineEnd * radius)),
largeLinePaint);
canvas.drawText(
Integer.toString(station),
radius,
radius - (textPoint * radius),
textPaint);
station = station + 1;
}else { //small line
canvas.drawLine(
(float)(radius),
(float)(radius - (smallLineStart*radius)),
(float)(radius),
(float)(radius - (smallLineEnd*radius)),
smallLinePaint);
}
canvas.rotate(3,radius,radius);
}
canvas.rotate(-3 * 106, radius,radius);
dialShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
dialWidth = bitmap.getWidth();
bitmap.recycle();
bitmap = null;
canvas.setBitmap(null);
canvas = null;
}
I use this bitmap on the draw canvas so I have to keep it (until the view is destroyed.
private void createFaceBitmap(){
bitmapFace = Bitmap.createBitmap(contentWidth,contentHeight,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmapFace);
canvas.drawLine(
dialFacePoints[0],
dialFacePoints[1],
dialFacePoints[2],
dialFacePoints[3],
dialIndicatorPaint);
canvas.drawLine(
dialFacePoints[4],
dialFacePoints[5],
dialFacePoints[6],
dialFacePoints[7],
smallLinePaint);
canvas.drawLine(
dialFacePoints[8],
dialFacePoints[9],
dialFacePoints[10],
dialFacePoints[11],
smallLinePaint);
canvas.drawPath(leftTrianglePath, solidSeekPaint);
canvas.drawPath(rightTrianglePath, solidSeekPaint);
canvas.drawPath(leftTrianglePath, largeLinePaint);
canvas.drawPath(rightTrianglePath, largeLinePaint);
canvas.setBitmap(null);
}
onDraw
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmapFace,0,0,null);
matrix.setRotate(-rotation, radius, radius);
matrix.postTranslate(dialFacePoints[12], (float) ((getHeight() / 2) - ((.25 * radius) / 2)));
dialShader.setLocalMatrix(matrix);
dialPaint.setShader(dialShader);
canvas.drawPath(dialPath, dialPaint);
}
I'm working on audio recording app.During recording i'm using visualizer like normal audio recording apps.But on specific time a want to draw a drawable on canvas visualizer(that is custom class extend View class).
Like this white drawable dot.
Here is my Custom view class.
public class VisualizerView extends View {
private static final int LINE_WIDTH = 1; // width of visualizer lines
private static final int LINE_SCALE = 75; // scales visualizer lines
private List<VisuallizerItem> visuallizerItems; // amplitudes for line lengths
private int width; // width of this View
private int height; // height of this View
private Paint linePaint; // specifies line drawing characteristics
//Boolean isImage=false;
// constructor
public VisualizerView(Context context, AttributeSet attrs) {
super(context, attrs); // call superclass constructor
linePaint = new Paint(); // create Paint for lines
linePaint.setColor(getResources().getColor(R.color.visualizerColor)); // set color to green
linePaint.setStrokeWidth(LINE_WIDTH); // set stroke width
}
// called when the dimensions of the View change
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width = w; // new width of this View
height = h; // new height of this View
visuallizerItems = new ArrayList<VisuallizerItem>();
}
// clear all amplitudes to prepare for a new visualization
public void clear() {
visuallizerItems.clear();
}
// add the given amplitude to the amplitudes ArrayList
public void addAmplitude(VisuallizerItem visuallizerItem) {
// isImage=false;
visuallizerItems.add(visuallizerItem); // add newest to the amplitudes ArrayList
// if the power lines completely fill the VisualizerView
if (visuallizerItems.size() * LINE_WIDTH >= width) {
visuallizerItems.remove(0); // remove oldest power value
}
}
public void addAmplitudeImage(VisuallizerItem visuallizerItem) {
//isImage=true;
visuallizerItems.add(visuallizerItem); // add newest to the amplitudes ArrayList
// if the power lines completely fill the VisualizerView
if (visuallizerItems.size() * LINE_WIDTH >= width) {
visuallizerItems.remove(0); // remove oldest power value
}
}
// draw the visualizer with scaled lines representing the amplitudes
#Override
public void onDraw(Canvas canvas) {
int middle = height / 2; // get the middle of the View
float curX = 0; // start curX at zero
// for each item in the amplitudes ArrayList
for (VisuallizerItem power : visuallizerItems) {
if (power.isImage == -100) {
float scaledHeight = power.amplitude / LINE_SCALE; // scale the power
curX += LINE_WIDTH; // increase X by LINE_WIDTH
// draw a line representing this item in the amplitudes ArrayList
canvas.drawLine(curX, middle + scaledHeight / 2, curX, middle
- scaledHeight / 2, linePaint);
} else {//**Here i'm trying to draw mark on canvas**
power.isImage = -100;
//TODO draw attachment image here
Paint p = new Paint();
Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.yellow);
p.setColor(Color.RED);
canvas.drawBitmap(b, visuallizerItems.size(), middle, p);
}
}
}
}
This code add white mark once but remove it when onDraw calls again.
actually i want to draw a mark on canvas like above white dot.
Don't know why it remove it.Please help me
Thanks in advance
I'm having a bit of a problem with a (very basic and crude) implementation of Parallax. Basically I have an image which is supposed to move across the screen at a slightly different rate to the background. It does this- until it reaches the end of the image. It seems to loop back to the start of the image-- but when it reaches the end of the image, it seems to stretch the bitmap out across the length of the screen until it's stretched out completely- then it resets itself.
I can't attach a screenshot (not enough rep) but here's the code.
Init method:
private void init(Context context) {
screenSize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getSize(screenSize);
backGroundViewPort = new Rect();
cityscapeRect = new Rect();
backGroundViewPort.set(0, 0, 400, 300);
GGRAssetManager ggrAssetManager = new GGRAssetManager(context);
background = ggrAssetManager.loadImage("background.jpg");
cityscape = ggrAssetManager.loadImage("cityscape3.png");
clouds = ggrAssetManager.loadImage("clouds.png");
backGroundViewPort.set(0, 0, 400, 300);
}
DoUpdate method:
public void doUpdate() {
Game.getInstance().viewport.set(Math.max(0, (int) (game.getPlayer().getxPos() - screenSize.x / 2)),
0,
(int) (game.getPlayer().getxPos() + (screenSize.x / 2)),
screenSize.y);
if (backGroundViewPort.right + 1 >= background.getWidth()) {
backGroundViewPort.left = 0;
backGroundViewPort.right = 400;
} else {
backGroundViewPort.right += 1;
backGroundViewPort.left += 1;
}
if (cityscapeRect.right + 1 >= cityscape.getWidth()) {
cityscapeRect.left = cityscape.getWidth() - screenSize.x;
cityscapeRect.right = 0;
} else {
cityscapeRect.right += 1;
cityscapeRect.left += 1;
}
}
DoDraw method:
public void doDraw(Canvas canvas) {
//PARALLAX
backGroundRect.set(0, 0, screenSize.x, screenSize.y);
canvas.drawBitmap(background, backGroundViewPort, backGroundRect, null);
cityscapeRect.set(0, screenSize.y/2, (screenSize.x), screenSize.y);
Paint paint = new Paint();
paint.setAlpha(150);
canvas.drawBitmap(cityscape, backGroundViewPort, cityscapeRect, paint);
}
I'm sure it's to do with the rectangles, but I don't know how to fix it. Help is greatly appreciated.
I am building an Android game where a ball is controlled and moved using the motion sensor.
There are some posts on how to draw an inverted circle like this one, but it is unusable as Android does not support BufferedImage.
I created the player using the codes below
public class Player extends Task {
private final static float MAX_SPEED = 20;
private final static float SIZE = 16;
private Circle _cir = null;
private Paint _paint = new Paint();
private Vec _vec = new Vec();
private Vec _sensorVec = new Vec();
public Player(){
_cir = new Circle( 15, 15, SIZE ); //15,15 is the initial x,y coordinates
}
public final Circle getPt(){
return _cir;
}
private void setVec(){
float x = -AcSensor.Inst().getX()*2;
float y = AcSensor.Inst().getY()*2;
_sensorVec._x = x < 0 ? -x*x : x*x;
_sensorVec._y = y < 0 ? -y*y : y*y;
_sensorVec.setLengthCap(MAX_SPEED);
_vec.blend( _sensorVec, 0.05f );
}
private void Move(){
_cir._x += _vec._x;
_cir._y += _vec._y;
}
#Override
public boolean onUpdate(){
setVec();
Move();
return true;
}
#Override
public void onDraw( Canvas c ){
c.drawCircle(_cir._x, _cir._y, _cir._r, _paint);
}
}
Question is, how to create an inverted circle around the player so that the player only sees a limited distance while the outer part is filled with black color? For example, something like this:
.
In my Android app, I am trying to show letters one by one with a short delay between each, while also playing a sound for each letter. I have everything working, and the sounds play with the correct delay, but the text always prints to the screen far too fast. The canvas seems to be updated even when i am not specifically invalidating the view.
Here is what I have so far - I also tried a variant of this based on the "snake" example and had the same results... any help would be appreciated!
public class SpellingView extends View {
private static final String WORD = "TRUCK";
int width;
int height;
String textToPrint;
float textspace;
int j=0;
private final Path arc;
private final Paint tPaint;
//constructor for SpellingView
public SpellingView(Context context) {
super(context);
arc = new Path();
tPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
displayLetterLoop();
}
public void displayLetterLoop(){
for (int i = 0; i < WORD.length(); i++){
final Runnable mUpdateUITimerTask = new Runnable() {
public void run() {
Spelling.mp.start();
}
};
final Handler mHandler = new Handler();
mHandler.postDelayed(mUpdateUITimerTask, i*1500);
}
}
#Override
protected void onDraw(Canvas canvas) {
int k;
// Drawing commands go here
width = canvas.getWidth();
height = canvas.getHeight();
arc.addArc(new RectF((width*.15f), (height*.15f), (width*.85f), (height*.4f)), 180,180);
tPaint.setStyle(Paint.Style.FILL_AND_STROKE);
tPaint.setColor(Color.RED);
tPaint.setTextSize(height * 0.1f);
tPaint.setTextAlign(Paint.Align.LEFT);
setBackgroundColor(Color.BLACK);
for (k = 0; k < j; k++){
char c = WORD.charAt(k);
String cs = Character.toString(c);
textToPrint+= cs;
textspace =(float) (k*(width/WORD.length())*.9);
canvas.drawTextOnPath(cs, arc, textspace , 0, tPaint);
}
if(j<WORD.length()){
j++;
}
}
}
Custom view will invalidate itself when is a part of a layout which for some reason redraw itself. Therefore you could envelop your code in onDraw() with a condition and a flag so that it draws your stuff only when the timer sets the flag and calls invalidate. After one letter is drawn then the flag shoud be set on false like:
if (drawLetter){
drawLetter = false;
/code...
}
However this also may need to be a sychronized block.
OnDraw should happen 60 times a second and not only when you are invalidating.
So maybe you need to update some class variables (when you are invalidating) and use those for your draw logic # OnDraw.