Tworzę apkę na androida, która ma wyświetlać za pomocą Canvas wiele różnych parametrów w różny sposób. Wykryłem, że mam w kodzie klasę, w której nic specjalnego się nie dzieje, ale gdy jest umieszczona na layoucie i odpalana na urządzeniu z Androidem lub w emulatorze, to po chwili gradualnie przybiera na wykresie wymaganej mocy baterii i mocy procesora - jak dla mnie to dość dziwne zachowanie... W OnDraw chyba nie mam instancjonowania obiektów poza pierwszym wywołaniem, gdzie ustawiam sobie wysokość i szerokość canvas. Czytałem, że ludzie z podobnym problemem sobie radzą, obchodząc to tworzeniem grafiki na bitmapie i dopiero wtedy aplikując bitmapę na Canvas. W trzech innych częściach to pomogło, ale w ostatniej ni chu chu... Co może być problemem?
pokaż kod
Oto kod klasy:
public class AttitudeMeter extends View {
private TextPaint tp;
private float textHeight;
private Paint basicPaint;
private float mStringHeight;
private float mTextWidth;
private float mTextHeight;
private Path path;
private Bitmap mainBitmap;
private Paint bitmapPaint;
private float contentWidth;
private float contentHeight;
public AttitudeMeter(Context context) {
super(context);
init(null, 0);
}
public AttitudeMeter(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public AttitudeMeter(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.AttitudeMeter, defStyle, 0);
mStringHeight = a.getDimension(
R.styleable.AttitudeMeter_stringHeight,
mStringHeight
);
a.recycle();
basicPaint = new Paint();
path = new Path();
// Update TextPaint and text measurements from attributes
invalidateTextPaintAndMeasurements();
Handler redrawHandler = new Handler();
redrawHandler.postDelayed(new Runnable() {
@Override
public void run() {
drawBitmap();
redrawHandler.postDelayed(this, 1000 / 60);
}
}, 1000 / 60);
}
private void invalidateTextPaintAndMeasurements() {
this.tp = new TextPaint();
this.tp.setTextAlign(Paint.Align.CENTER);
this.tp.setAntiAlias(true);
this.tp.setColor(Color.YELLOW);
}
private void drawBitmap() {
if (contentWidth == 0 || contentHeight == 0) {
return;
}
// if (contentHeight > 0) {
// return;
// }
mainBitmap = Bitmap.createBitmap(Math.round(contentWidth), Math.round(contentHeight), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mainBitmap);
this.textHeight = 12 * getHeight() / (float) 220;
this.tp.setTextSize(this.textHeight);
canvas.save();
canvas.rotate((float) MainThread.getCurrentBank(), Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));
int halfWidth = Math.round(getWidth() / (float) 2);
int halfHeight = Math.round(getHeight() / (float) 2);
basicPaint.setStrokeWidth((float) 2 * getHeight() / 220);
basicPaint.setAntiAlias(true);
basicPaint.setColor(Color.rgb(0, 91, 0));
basicPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(-halfWidth, -halfHeight, getWidth() + halfWidth, getHeight() + halfHeight, basicPaint);
basicPaint.setColor(Color.rgb(0, 0, 91));
int hor = Math.round(getHeight() / (float) 2)
+ Math.round(-MainThread.getCurrentPitch() * 2 * 1.3f / (float) 180 * getHeight());
canvas.drawRect(-halfWidth, -halfHeight,
getWidth() + halfWidth,
hor, basicPaint);
basicPaint.setColor(Color.WHITE);
this.drawBankIndicator(canvas, 4, 0, 2.7f);
this.drawBankIndicator(canvas, 2, 10, 1.8f);
this.drawBankIndicator(canvas, 2, 20, 1.8f);
this.drawBankIndicator(canvas, 3, 30, 2.1f);
this.drawBankIndicator(canvas, 1, 45, 1.0f);
this.drawBankIndicator(canvas, 3, 60, 2.1f);
this.drawBankIndicator(canvas, 5, 90, 2.1f);
int i;
int cur = hor;
boolean even = false;
float evencheck;
basicPaint.setColor(Color.WHITE);
this.tp.setColor(Color.WHITE);
Paint.Align a = this.tp.getTextAlign();
this.tp.setTextAlign(Paint.Align.LEFT);
for (i = 0; i < 20; i += 5) {
// if (i > 40 & !even) {
// even = !even;
// cur += Math.round(10 / (float) 180 * getHeight());
// continue;
// }
basicPaint.setStrokeWidth((float) (even ? 2 : 1) * getHeight() / 220);
even = !even;
cur += Math.round(13 / (float) 180 * getHeight());
evencheck = (float) (even ? 20 : i == 15 ? 7.5 : 12);
canvas.drawLine(getWidth() / (float) 2 - getWidth() / evencheck, cur,
getWidth() / (float) 2 + getWidth() / evencheck, cur, basicPaint);
this.tp.setTextAlign(Paint.Align.LEFT);
canvas.drawText(Integer.toString(i + 5), getWidth() / (float) 1.95 + getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
this.tp.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(Integer.toString(i + 5), getWidth() / (float) 2.05 - getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
this.tp.setTextAlign(Paint.Align.LEFT);
}
basicPaint.setColor(Color.WHITE);
basicPaint.setStrokeWidth((float) 2 * getHeight() / 220);
canvas.drawLine(getWidth() / (float) 2 - getWidth() / (float) 10, hor,
getWidth() / (float) 2 + getWidth() / (float) 10, hor, basicPaint);
this.tp.setTextAlign(Paint.Align.LEFT);
canvas.drawText(Integer.toString(0), getWidth() / (float) 1.95 + getWidth() / (float) 10, hor + this.tp.getTextSize() / 3, this.tp);
this.tp.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(Integer.toString(0), getWidth() / (float) 2.05 - getWidth() / (float) 10, hor + this.tp.getTextSize() / 3, this.tp);
this.tp.setTextAlign(Paint.Align.LEFT);
cur = hor;
even = false;
for (i = 0; i > -20; i -= 5) {
// if (i < -40 & !even) {
// even = !even;
// cur -= Math.round(10 / (float) 180 * getHeight());
// continue;
// }
basicPaint.setStrokeWidth((float) (even ? 2 : 1) * getHeight() / 220);
even = !even;
cur -= Math.round(13 / (float) 180 * getHeight());
evencheck = (float) (even ? 20 : i == -15 ? 7.5 : 12);
canvas.drawLine(getWidth() / (float) 2 - getWidth() / evencheck, cur,
getWidth() / (float) 2 + getWidth() / evencheck, cur, basicPaint);
this.tp.setTextAlign(Paint.Align.LEFT);
canvas.drawText(Integer.toString(Math.abs(i - 5)), getWidth() / (float) 1.95 + getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
this.tp.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(Integer.toString(Math.abs(i - 5)), getWidth() / (float) 2.05 - getWidth() / evencheck, cur + this.tp.getTextSize() / 3, this.tp);
this.tp.setTextAlign(Paint.Align.LEFT);
}
this.tp.setTextAlign(a);
canvas.restore();
this.drawCurrentBankValue(canvas);
basicPaint.setColor(Color.YELLOW);
basicPaint.setStrokeWidth((float) 4 * getHeight() / 220);
basicPaint.setStyle(Paint.Style.STROKE);
path.moveTo(
halfWidth,
Math.round(getHeight() / (float) 2)
);
path.lineTo(
halfWidth + halfWidth / (float) 4,
Math.round(getHeight() / (float) 2) + halfHeight / (float) 40
);
path.lineTo(
halfWidth,
Math.round(getHeight() / (float) 2 - halfHeight / (float) 50)
);
path.lineTo(
halfWidth - halfWidth / (float) 4,
Math.round(getHeight() / (float) 2) + halfHeight / (float) 40
);
path.lineTo(
halfWidth,
Math.round(getHeight() / (float) 2)
);
canvas.drawPath(
path,
basicPaint
);
basicPaint.setColor(Color.WHITE);
basicPaint.setStrokeWidth((float) 2 * getHeight() / 220);
canvas.drawLine(
getWidth() - halfWidth / (float) 4,
Math.round(getHeight() / (float) 2),
getWidth(),
Math.round(getHeight() / (float) 2),
basicPaint
);
canvas.drawLine(
0,
Math.round(getHeight() / (float) 2),
halfWidth / (float) 4,
Math.round(getHeight() / (float) 2),
basicPaint
);
invalidate();
}
void drawBankIndicator(Canvas canvas, float length, int degree, float strokeWidth) {
Paint p = new Paint();
p.setColor(Color.WHITE);
p.setAntiAlias(true);
p.setStrokeWidth((float) getHeight() / 220 * strokeWidth);
canvas.rotate(degree, Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));
canvas.drawLine(getWidth() / (float) 2, getHeight() / (float) 16, getWidth() / (float) 2, getHeight() / (float) 16 + getHeight() / (24 + length), p);
canvas.rotate(-degree*2, Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));
canvas.drawLine(getWidth() / (float) 2, getHeight() / (float) 16, getWidth() / (float) 2, getHeight() / (float) 16 + getHeight() / (24 + length), p);
canvas.rotate(degree, Math.round(getWidth() / (float) 2), Math.round(getHeight() / (float) 2));
}
void drawCurrentBankValue(Canvas canvas)
{
Paint p = new Paint();
p.setColor(Color.YELLOW);
p.setAntiAlias(true);
p.setStrokeWidth((float) getHeight() / 220 * 3);
canvas.drawLine(getWidth() / (float) 2, getHeight() / (float) 16, getWidth() / (float) 2, getHeight() / (float) 16 + getHeight() / (float) 24, p);
p.setStyle(Paint.Style.FILL);
p.setColor(Color.BLACK);
Rect r = new Rect(getWidth() / 2 - getWidth() / 16, getHeight() / 15,
getWidth() / 2 + getWidth() / 16, getHeight() / 13);
canvas.drawRect(r, p);
canvas.drawText(Integer.toString(Math.abs(MainThread.getCurrentBank())), getWidth() / (float) 2, getHeight() / (float) 15, tp);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (0 == contentHeight) {
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
contentWidth = getWidth() - paddingLeft - paddingRight;
contentHeight = getHeight();
}
if (mainBitmap != null) {
canvas.drawBitmap(mainBitmap, 0, 0, bitmapPaint);
}
}
}
Nie znam się na androidzie, więc ślepy strzał ;-)
- Framework wywołuje onDraw -> tam robisz canvas.drawBitamp i na końcu tego drawBitmap masz invalidate(), które pewnie triggeruje framework do wywołania onDraw, które woła drawBitmap...
- Skoro w onDraw dostajesz canvas, to po co tworzysz osobny canvas w onDraw? Nie możesz wykorzystać tego przekazanego? Z tego co widzę, to Canvas ma prywatny stos (save(), restore())
yarel napisał(a):
Nie znam się na androidzie, więc ślepy strzał ;-)
- Framework wywołuje onDraw -> tam robisz canvas.drawBitamp i na końcu tego drawBitmap masz invalidate(), które pewnie triggeruje framework do wywołania onDraw, które woła drawBitmap...
- Skoro w onDraw dostajesz canvas, to po co tworzysz osobny canvas w onDraw? Nie możesz wykorzystać tego przekazanego? Z tego co widzę, to Canvas ma prywatny stos (save(), restore())
onDraw wywołuje drawBitmap na Canvas z onDraw, a nie drawBitmap z obiektu, którego kod tu jest :) drawBitmap z tego obiektu jest wywoływany 60 razy na sekundę przez redrawHandler i invalidate musi być albo w nim, albo w redrawHandlerze, bo w innym przypadku onDraw na obiekcie nie wykona się więcej niż raz :/
Tworzenie osobnego Canvas potrzebne jest mi ze względu na to, że chcę rysować na bitmapie, a dopiero później bitmapę nałożyć na Canvas z onDraw.
Widzę na profilerze, że nie tylko w tej klasie występuje problem. Coś musiałem źle ogarnąć z rysowaniem, bo im dłużej chodzi apka, tym więcej procka zżera...
Doszedłem do tego, że problemem może być Code Cache, albo ja źle z tego korzystam. Idzie to jakoś wyłączyć?
Dobra, ja wymiękam z tym problemem. Jest ktoś w stanie nakierować mnie na jakiś konkretny tutorial, który mówi, jak używać canvas draw tak, żeby to nie lagowało?