Это проблема синхронизации. По сути, invalidate()
является , а не блокирующим вызовом: он просто говорит системе перерисовать в какой-то момент в будущем. Итак, что происходит:
- Вы устанавливаете значение для (x1, y1) и (x2, y2)
invaldate()
планирует перерисовку как можно скорее
- , но другое событие касания перезаписывает (x1, y1) и (x2, y2) до того, как нарисована линия : старые значения теряются навсегда
Вы можете доказать это, просто добавив два счетчика хитов, один для invalidate()
и один для onDraw()
. Через некоторое время вы видите, что количество звонков на invalidate()
превышает количество обращений на onDraw()
. Я предлагаю сохранить очки от сенсорных событий в Queue
. Также обратите внимание, что каждый раз, когда вы выделяете Bitmap
, вам необходимо вызывать recycle
, чтобы избежать утечек памяти. Фактически, пиксели хранятся в собственной памяти и не удаляются мусором при разрушении представления. Я обычно звоню recycle
, когда моя деятельность прекращается. Вот мой код:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public static class Drawer extends View {
private Bitmap cache;
private Queue<PointF> points;
private PointF from;
public Drawer(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
points = new ConcurrentLinkedQueue<PointF>();
}
@Override
public boolean onTouchEvent(MotionEvent evt) {
switch (evt.getAction()) {
case MotionEvent.ACTION_DOWN: from = new PointF(evt.getX(), evt.getY()); break;
case MotionEvent.ACTION_MOVE: points.add(new PointF(evt.getX(), evt.getY())); invalidate(); break;
case MotionEvent.ACTION_UP: from = null; break;
default: from = null;
}
return true;
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w == 0 || h == 0)
return;
cache = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
@Override
public void onDraw(Canvas systemCanvas) {
int w = getWidth();
int h = getHeight();
if (w == 0 || h == 0)
return;
if (cache == null)
cache = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
// Draw on the cache
Canvas canvas = new Canvas(cache);
Paint paint = new Paint();
paint.setStrokeWidth(4);
paint.setColor(Color.MAGENTA);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
drawPoints(points, canvas, paint);
// Draw the cache with the system canvas
systemCanvas.drawBitmap(cache, 0, 0, paint);
}
private void drawPoints(Queue<PointF> points, Canvas canvas, Paint paint) {
if (from == null)
return;
PointF to;
while ((to = points.poll()) != null) {
canvas.drawLine(from.x, from.y, to.x, to.y, paint);
from = to;
}
}
}
}
и это макет
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:id="@+id/drawView">
<view
class="com.zybnet.test.MainActivity$Drawer"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Вы можете видеть, что таможенное представление можно использовать и в XML:)