2012-12-29 39 views
1

我有3個圓圈的窗口,它們同時旋轉。一切都很好,直到向圈子添加文本,然後旋轉開始落後。如何優化畫布上的繪畫文字

main window

我怎麼能畫進行優化畫布? 這是我的代碼:

@Override 
protected void onDraw(final Canvas canvas) { 
    if (mPaint == null) { 
     mPaint = new Paint(); 
     mPaint.setTextSize(20f); 
    }  
    drawUpperCircle(canvas); 
    drawBottomCircle(canvas); 
    drawMainCircle(canvas); 

    try { 
     Thread.sleep(1, 1); 
     invalidate(); 
     mRotation += 0.9; 
    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

    super.onDraw(canvas); 
} 
    private void drawUpperCircle(Canvas canvas) { 
    canvas.save(); 
    canvas.rotate(mRotation, 0, mUpperCircleCentr); 
    mPaint.setColor(Color.CYAN); 
    canvas.drawCircle(0, mUpperCircleCentr, mUpperCirclRadius, mPaint); 
    mPaint.setColor(Color.BLACK); 
    for (int i = 0; i < SEG_COUNT; i++) { 
     canvas.rotate(SEG_IN_GRAD, 0, mUpperCircleCentr); 
     canvas.drawLine(0, mUpperCircleCentr, mUpperCirclRadius, mUpperCircleCentr, mPaint); 
     //   canvas.drawText("my text" + String.valueOf(i), mUpperCirclRadius * 2/3, mUpperCircleCentr - 4, mPaint); 
    } 
    canvas.restore(); 
} 

private void drawBottomCircle(Canvas canvas) { 
    canvas.save(); 
    canvas.rotate(mRotation, 0, mBottomCircleCentr); 
    mPaint.setColor(Color.RED); 
    canvas.drawCircle(0, mBottomCircleCentr, mBottomCirclRadius, mPaint); 
    mPaint.setColor(Color.BLACK); 
    for (int i = 0; i < SEG_COUNT; i++) { 
     canvas.rotate(SEG_IN_GRAD, 0, mBottomCircleCentr); 
     canvas.drawLine(0, mBottomCircleCentr, mBottomCirclRadius, mBottomCircleCentr, mPaint); 
     //   canvas.drawText("my text" + String.valueOf(i), mBottomCirclRadius * 2/3, mBottomCircleCentr - 4, mPaint); 
    } 
    canvas.restore(); 
} 

private void drawMainCircle(Canvas canvas) { 
    canvas.save(); 
    canvas.rotate(mRotation, 0, mMainCircleCentr); 
    mPaint.setColor(Color.argb(100, 100, 100, 100)); 
    canvas.drawCircle(0, mMainCircleCentr, mMainCirclRadius, mPaint); 
    mPaint.setColor(Color.BLACK); 
    for (int i = 0; i < SEG_COUNT; i++) { 
     canvas.rotate(SEG_IN_GRAD, 0, mMainCircleCentr); 
     canvas.drawLine(0, mMainCircleCentr, mMainCirclRadius, mMainCircleCentr, mPaint); 
     canvas.drawText("my text" + String.valueOf(i), mMainCirclRadius * 2/3, mMainCircleCentr - 4, mPaint); 
    } 
    canvas.restore(); 
} 

編輯 爲了提高性能並從UI線程我已經使用雙緩衝帶SurfaceView繪製和實施@Morgans優化。這是它如何實現的。

DrawView.java

public class DrawView extends SurfaceView implements SurfaceHolder.Callback { 

............................................................... 

public DrawView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    getHolder().addCallback(this); 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    float currentX = event.getX(); 
    float currentY = event.getY(); 
    float deltaX, deltaY; 
    switch (event.getAction()) { 
    case MotionEvent.ACTION_MOVE: 
     // Modify rotational angles according to movement 
     deltaX = currentX - previousX; 
     deltaY = currentY - previousY; 
     mDrawThread.mRotation += deltaY * 180/getHeight(); 
    } 
    // Save current x, y 
    previousX = currentX; 
    previousY = currentY; 
    return true; // Event handled 
} 

@Override 
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) { 

} 

@Override 
public void surfaceCreated(SurfaceHolder surfaceHolder) { 
    mDrawThread = new DrawThread(getHolder(), this); 
    mDrawThread.setRunning(true); 
    mDrawThread.start(); 
} 

@Override 
public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 
    boolean retry = true; 
    mDrawThread.setRunning(false); 
    while (retry) { 
     try { 
      mDrawThread.join(); 
      retry = false; 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 
} 

和主要工作是在DrawThread.java做

public class DrawThread extends Thread { 

private ArrayList<Path> mMainCirclePaths = new ArrayList<Path>(SEG_COUNT); 
private ArrayList<Path> mUpperCirclePaths = new ArrayList<Path>(SEG_COUNT); 
private ArrayList<Path> mCenterCirclePaths = new ArrayList<Path>(SEG_COUNT); 
private ArrayList<Path> mBottomCirclePaths = new ArrayList<Path>(SEG_COUNT); 

private boolean mRun = false; 
private SurfaceHolder mSurfaceHolder; 
private DrawView mDrawView; 
private Paint mPaint; 

private CirclesModel mCirclesModel; 
public float mRotation = 0; 

public DrawThread(SurfaceHolder surfaceHolder, DrawView drawView) { 
    mSurfaceHolder = surfaceHolder; 
    mDrawView = drawView; 
    mCirclesModel = new CirclesModel(mDrawView.getHeight()); 
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    mPaint.setTextSize(18f); 
    initPaths(); 
} 

public void setRunning(boolean b) { 
    mRun = b; 
} 

@Override 
public void run() { 
    while (mRun) { 
     Canvas canvas = null; 
     try { 
      canvas = mSurfaceHolder.lockCanvas(null); 
      synchronized (mSurfaceHolder) { 
       drawMainCircle(canvas); 
       mPaint.setColor(Color.WHITE); 
       canvas.drawCircle(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y], 
         mCirclesModel.mSmallCirclesRadius, mPaint); 
       drawCenterCircle(canvas); 
       drawUpperCircle(canvas); 
       drawBottomCircle(canvas); 
       //mRotation += 0.5f; 

      } 
     } finally { 
      if (canvas != null) { 
       mSurfaceHolder.unlockCanvasAndPost(canvas); 
      } 
     } 
    } 
} 

private void drawMainCircle(Canvas canvas) { 
    canvas.save(); 
    canvas.rotate(mRotation, mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y]); 
    float rot = mRotation; 
    mPaint.setColor(Color.LTGRAY/* argb(100, 255, 255, 255) */); 
    canvas.drawCircle(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y], 
      mCirclesModel.mBigCirclesRadius, mPaint); 
    mPaint.setColor(Color.BLACK); 
    for (int i = 0; i < SEG_COUNT; i++) { 
     canvas.rotate(SEG_IN_GRAD, mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y]); 
     rot += SEG_IN_GRAD; 
     float absRot = Math.abs(rot % 360); 
     if (absRot > mCirclesModel.mMainCircleSegment[0] && absRot < mCirclesModel.mMainCircleSegment[1]) { 
      continue; 
     } 
     canvas.drawLine(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y], 
       mCirclesModel.mBigCirclesRadius, mCirclesModel.mMainCircleCentr[CirclesModel.Y], mPaint); 
     canvas.drawPath(mMainCirclePaths.get(i), mPaint); 
     // canvas.drawText("my text" + String.valueOf(i), 
     // mMainCirclRadius * 2/3, mMainCircleCentr - 4, mPaint); 
    } 
    canvas.restore(); 
} 
    ................................................................. 
} 

雙緩衝中的兩行代碼實現

canvas = mSurfaceHolder.lockCanvas(null);這裏我從表面視圖畫布中畫出ne xt框架。

mSurfaceHolder.unlockCanvasAndPost(canvas);這裏我用新的canwa在SurfaceView上疊加當前圖像,這是圖像變化的時刻。請注意,如果您擁有透明元素,那麼以前的圖像將仍然可見,圖像不會被替換,而是重疊。

+0

你看過http://codereview.stackexchange.com/嗎?我想知道這個問題是否會更適合去那裏... – 2012-12-29 22:18:57

+0

@DumbProducts codereview.stackexchange.com和stackoverflow之間有什麼不同? – Lemberg

+1

codereview.stackexchange.com是爲了類似的代碼優化,stackoverflow是更多的錯誤修復代碼,如何做到這一點等。 – 2012-12-30 16:43:44

回答

8

以下是包含一些優化的代碼版本。

首先,我儘量不要畫出當前屏幕外的線條和文字。我通過跟蹤旋轉角度並跳過90到270度之間的淨旋轉圖來做到這一點。在我的2.3模擬器上,整體性能提高了25%。第二,我通過初始化一個數組(ArrayList<Path>)來爲每個我需要繪製的字符串「012」緩存「我要繪製的字符串。我在一次性初始化mPaint的同一個地方執行此操作。然後我使用canvas.drawPath(...)繪製字符串。在我的2.3模擬器上,性能提高了33%。淨效應是轉速的兩倍左右。此外,它阻止了「擺弄」文本。

其他一些注意事項:

我刪除了Thread.sleep(1,1)。不確定你到底想要完成什麼。

我把旋轉增量從0.9改爲1.0。不知道你爲什麼使用0.9。請注意,如果您更改回來,我的「旋轉10度所需的日誌時間」將不起作用,因爲mRotation % 10可能很少爲0.

在4.1模擬器上,旋轉速度通常比在我的2.3模擬器上。 4.1設備的速度更快。

public class AnimView extends View { 
Paint mPaint; 
ArrayList<Path> mTextPaths; 

float mRotation = 0f; 

float mUpperCircleCentr = 150f; 
float mUpperCirclRadius = 150f; 

private static final int SEG_COUNT = 60; 
private static final float SEG_IN_GRAD = 360.0f/SEG_COUNT; 

float mBottomCircleCentr = 450f; 
float mBottomCirclRadius = 150f; 

float mMainCircleCentr = 300f; 
float mMainCirclRadius = 300f; 

long mLastMillis = 0L; 

// ctors removed 

@Override 
protected void onDraw(final Canvas canvas) { 
    super.onDraw(canvas); 

    if (mPaint == null) { 
     mPaint = new Paint(); 
     mPaint.setTextSize(20f); 

     // init text paths 
     mTextPaths = new ArrayList<Path>(SEG_COUNT); 
     for (int i = 0; i < SEG_COUNT; i++) { 
      Path path = new Path(); 
      String s = "my text" + String.valueOf(i); 
      mPaint.getTextPath(s, 0, s.length(), mMainCirclRadius * 2/3, mMainCircleCentr - 4, path); 
      path.close(); // not required on 2.2/2.3 devices 
      mTextPaths.add(path); 
     } 
    } 
    if (mLastMillis == 0L) { 
     mLastMillis = System.currentTimeMillis(); 
    } 

    drawUpperCircle(canvas); 
    drawBottomCircle(canvas); 
    drawMainCircle(canvas); 

    invalidate(); 

    if (((int) mRotation) % 10 == 0) { 
     long millis = System.currentTimeMillis(); 
     Log.w("AnimateCanvas", "OnDraw called with mRotation == " + mRotation); 
     Log.w("AnimateCanvas", "Last 10 degrees took millis: " + (millis - mLastMillis)); 
     mLastMillis = millis; 
    } 
} 

private void drawUpperCircle(Canvas canvas) { 
    canvas.save(); 
    canvas.rotate(mRotation, 0, mUpperCircleCentr); 
    float rot = mRotation; 
    mPaint.setColor(Color.CYAN); 
    canvas.drawCircle(0, mUpperCircleCentr, mUpperCirclRadius, mPaint); 
    mPaint.setColor(Color.BLACK); 
    for (int i = 0; i < SEG_COUNT; i++) { 
     canvas.rotate(SEG_IN_GRAD, 0, mUpperCircleCentr); 
     rot += SEG_IN_GRAD; 
     if (rot % 360 > 90 && rot % 360 < 270) 
      continue; 
     canvas.drawLine(0, mUpperCircleCentr, mUpperCirclRadius, mUpperCircleCentr, mPaint); 
    } 
    canvas.restore(); 
} 

private void drawBottomCircle(Canvas canvas) { 
    canvas.save(); 
    canvas.rotate(mRotation, 0, mBottomCircleCentr); 
    float rot = mRotation; 
    mPaint.setColor(Color.RED); 
    canvas.drawCircle(0, mBottomCircleCentr, mBottomCirclRadius, mPaint); 
    mPaint.setColor(Color.BLACK); 
    for (int i = 0; i < SEG_COUNT; i++) { 
     canvas.rotate(SEG_IN_GRAD, 0, mBottomCircleCentr); 
     rot += SEG_IN_GRAD; 
     if (rot % 360 > 90 && rot % 360 < 270) 
      continue; 
     canvas.drawLine(0, mBottomCircleCentr, mBottomCirclRadius, mBottomCircleCentr, mPaint); 
    } 
    canvas.restore(); 
} 

private void drawMainCircle(Canvas canvas) { 
    canvas.save(); 

    canvas.rotate(mRotation, 0, mMainCircleCentr); 
    float rot = mRotation; 
    mPaint.setColor(Color.argb(100, 100, 100, 100)); 
    canvas.drawCircle(0, mMainCircleCentr, mMainCirclRadius, mPaint); 
    mPaint.setColor(Color.BLACK); 
    for (int i = 0; i < SEG_COUNT; i++) { 
     canvas.rotate(SEG_IN_GRAD, 0, mMainCircleCentr); 
     rot += SEG_IN_GRAD; 
     if (rot % 360 > 90 && rot % 360 < 270) 
      continue; 
     canvas.drawLine(0, mMainCircleCentr, mMainCirclRadius, mMainCircleCentr, mPaint); 
     canvas.drawPath(mTextPaths.get(i), mPaint); 
     // canvas.drawText("my text" + String.valueOf(i), mMainCirclRadius * 2/3, mMainCircleCentr - 4, mPaint); 
    } 
    canvas.restore(); 
} 
} 
+1

你的優化特別是在'路徑'幫助了我很多。 – Lemberg

3

你的代碼是相當不錯的,簡單的。您可以通過使用較少的循環來優化它,例如一起繪製所有東西或組合變量,但這很快就會變得混亂。

我會建議你保持你的繪圖代碼或多或少相等。你實際上不會做最糟糕的事情:實例化對象,並且它很容易維護。

但你也許可以嘗試使用雙緩衝區:在RAM中繪製緩衝區並翻轉屏幕上的一個緩衝區。這通常表現相當好,以獲​​得恆定的動畫速度。使用畫布的鎖定和解鎖:Double buffering in Java on Android with canvas and surfaceview

+1

你應該肯定使用抗鋸齒:http://stackoverflow.com/questions/5816068/how-to-antialiasing-in-the-canvas-and-path – Snicolas