Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>It's a synchronization problem. Basically, <code>invalidate()</code> is <strong>not</strong> a blocking call: it just tells the system to redraw at some point in the future. So what happens:</p> <ul> <li>You set a value for (x1, y1) and (x2, y2)</li> <li><code>invaldate()</code> schedules a redraw as soon as possible</li> <li>but another touch event overwrites (x1, y1) and (x2, y2) <strong>before the line is drawn</strong>: the old values are lost forever</li> </ul> <p>You can prove it simply by adding two hits counter, one for <code>invalidate()</code> and one for <code>onDraw()</code>. After some time you see that the number of calls to <code>invalidate()</code> is greater than the hits for <code>onDraw()</code>. My suggestion is to keep the points from touch events in a <code>Queue</code>. Also note that every time you allocate a <code>Bitmap</code> you need to call <code>recycle</code> when you have done to avoid memory leaks. In fact, the pixels are stored in native memory and are not garbage collected when the view is destroyed. I usually call <a href="http://developer.android.com/reference/android/graphics/Bitmap.html#recycle%28%29" rel="nofollow"><code>recycle</code></a> when my activity stops. Here is my code:</p> <pre><code>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&lt;PointF&gt; points; private PointF from; public Drawer(Context ctx, AttributeSet attrs) { super(ctx, attrs); points = new ConcurrentLinkedQueue&lt;PointF&gt;(); } @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&lt;PointF&gt; 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; } } } } </code></pre> <p>and this is the layout</p> <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;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"&gt; &lt;view class="com.zybnet.test.MainActivity$Drawer" android:layout_width="fill_parent" android:layout_height="fill_parent" /&gt; &lt;/LinearLayout&gt; </code></pre> <p>You can see that customs view can be used in XML, too :)</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload