2012-03-26 94 views
1

我的活動課文件:繪畫通過觸摸屏跳過(不連續線)

package com.drawing.test; 

import android.app.Activity; 
import android.os.Bundle; 


import android.app.Activity; 

import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.os.Bundle; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 
import android.widget.LinearLayout; 

public class TstActivity extends Activity implements OnTouchListener 
{ 
    float x1 = 0, y1 = 0, x2 = 0, y2 = 0; 
    public static boolean action=false; 
    private Bitmap mBitmap; 
    private Canvas mCanvas; 
    private Paint mBitmapPaint; 

    Drawer mDrawer; 

    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     LinearLayout mLinearLayout = (LinearLayout) findViewById(R.id.drawView); 
     mLinearLayout.setOnTouchListener((OnTouchListener) this); 
     mLinearLayout.addView(new Drawer(this)); 
    } 

    public boolean onTouch(View v, MotionEvent event) 
    { 
     switch (event.getAction()) 
     { 
      case MotionEvent.ACTION_DOWN: 
       x1 = event.getX(); 
       y1 = event.getY(); 
       action=false; 
       //v.invalidate(); 
      return true; 
      case MotionEvent.ACTION_MOVE: 
       x1=x2; 
       y1=y2; 
       x2 = event.getX(); 
       y2 = event.getY(); 
       v.invalidate(); 
       action=true; 
      return true; 
      case MotionEvent.ACTION_UP: 
       x2 = event.getX(); 
       y2 = event.getY(); 
       v.invalidate(); 
       action=true; 
      return true; 

     } 
     return false; 
    } 

    public class Drawer extends View 
    { 

     public Drawer(Context context) 
     { 
      super(context); 
      mBitmap = Bitmap.createBitmap(400, 800, Bitmap.Config.ARGB_8888); 
      mCanvas = new Canvas(mBitmap); 
      mBitmapPaint = new Paint(Paint.DITHER_FLAG); 
      mBitmapPaint.setColor(Color.MAGENTA); 
      invalidate(); 
     } 

     protected void onDraw(Canvas canvas) 
     { 
      if(x1==x2 && y1==y2)return; 
      Paint p = new Paint(); 
      // Canvas mCanvas1=new Canvas(mBitmap); 
      p.setColor(Color.parseColor("#7CFC00")); 
      canvas.drawBitmap(mBitmap, 0, 0, p); 
      // canvas.drawLine(x1, y1, x2 , y2, p); 
      p.setColor(Color.RED); 
      // mCanvas1.drawLine(x1, y1, x2, y2,p); 
      if(action==true)mCanvas.drawLine(x1, y1, x2, y2, mBitmapPaint); 

     } 
    } 
} 

我的佈局XML:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" 
    android:id="@+id/drawView"> 

</LinearLayout> 

當我嘗試這一點上模擬器,換行符在中間,但繼續繪製。我也試過設備,同樣的問題發生但數量較少。此外,顯示器有時閃爍,或完全空白。當它完全黑色時,如果我觸摸一次就會回來。

我在做什麼錯,我應該改變我的方法。

任何建議表示讚賞。謝謝

+0

見我的答案,但請你提高標題:)我建議一個 – Raffaele 2012-03-26 20:30:33

+0

謝謝...我會把一個類似的標題,但我不明白什麼是技術問題:) – kishu27 2012-03-27 05:29:55

回答

2

這是一個同步問題。基本上,invalidate()而不是阻塞呼叫:它只是告訴系統在未來的某個時間點重新繪製。所以會發生什麼:

  • 您儘快
  • 設置(X1,Y1)和(x2,y2)的
  • invaldate()時間表重繪的值,而是另一觸摸事件覆蓋(X1,Y1)簡單地通過增加兩個命中計數器,一個用於invalidate(),一個用於onDraw()舊值永遠失去了

你可以證明這一點:與前行(x2,y2)繪製。經過一段時間後,您會看到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中使用,太:)

+0

感謝您的代碼。我剛剛學會了如何引用XML中的內部類。再次感謝這個 – kishu27 2012-03-27 05:37:02

+1

確保內部類是'public'和'static',並且它必須有一個像這樣的構造函數'View(Context,AttributeSet)':)我沒有在我的文章中概述這個,但可能會給你一些頭痛 – Raffaele 2012-03-27 07:47:52

+0

我想知道,當我正在經歷和理解你的代碼時,但由於我在時間的運行,我計劃稍後檢查..謝謝你告訴我:) – kishu27 2012-03-28 08:47:58

0

對於motionEvent.ACTION_MOVE,它們可以並將被批處理。我相信這就是爲什麼會有斷線的原因。

看這個從文檔:

配料

爲了提高效率,與ACTION_MOVE移動事件可以分批在一起的單個對象內的多個移動樣品。最新的指針座標可以使用getX(int)和getY(int)。使用getHistoricalX(int,int)和getHistoricalY(int,int)可訪問批處理中的較早座標。座標是「歷史的」,只是因爲它們比批處理中的當前座標更早;然而,它們仍然不同於之前動作事件中報告的任何其他座標。要按時間順序處理批次中的所有座標,請先消耗歷史座標,然後消耗當前座標。

+0

你錯了。在他的代碼中,事件批處理可能會導致直線而不是曲線,但**不會導致跳躍**。他沒有處理*連續*數據,他有**樣本**,即某些離散時間間隔的值。當您跳過流中的樣本時,您的評估只會更準確 – Raffaele 2012-03-26 20:08:56