2012-12-29 4 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(); 
} 

편집 성능을 개선하고 내가 SurfaceView으로 더블 버퍼링을 사용했다 UI 스레드에서 그리기 제거하고 @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); 여기 나는 표면을 볼 수있는 캔버스에서 가져온다. xt 프레임.

mSurfaceHolder.unlockCanvasAndPost(canvas); 여기 저는 새로운 캔버스로 SurfaceView의 현재 이미지를 겹치기 때문에 이미지가 바뀌는 순간입니다. 투명 요소가있는 경우 이전 이미지가 계속 표시되고 이미지가 대체되지는 않지만 겹쳐 지는지 확인하십시오.

+0

http://codereview.stackexchange.com/을 살펴 보셨습니까? 이 질문이 더 적절하게 거기에 가는지 궁금 하네 ... –

+0

@DumbProducts codereview.stackexchange.com과 stackoverflow 사이에 다른 점은 무엇입니까? – Lemberg

+1

codereview.stackexchange.com은 코드 최적화와 마찬가지로 stackoverflow가 오류 코드를 수정하는 방법이며,이 요법을 수행하는 방법입니다. –

답변

8

다음은 몇 가지 최적화가 포함 된 코드의 버전입니다.

첫째, 나는 현재 화면 밖에있는 선과 텍스트를 그리지 않으려 고합니다. 나는 회전 각도를 추적하고 90도에서 270도 사이의 순 회전을 위해 도면을 건너 뛴다. 내 2.3 시뮬레이터에서이 성능이 전반적으로 25 % 향상되었습니다.

두 번째로 배열 (ArrayList<Path>)을 초기화하여 그릴 문자열을 각각 ""으로 그릴 문자열을 "캐시"합니다. 나는 당신이 한 번만 mPaint을 초기화하는 동일한 장소에서 이것을합니다. 그런 다음 canvas.drawPath (...)를 사용하여 문자열을 그립니다. 내 2.3 시뮬레이터에서이 성능이 33 % 더 향상되었습니다. 그물 효과는 회전 속도의 두 배 정도였습니다. 또한, 텍스트가 "흔들 리기"에서 멈췄습니다.

다른 몇 가지 참고 사항 :

은 내가 Thread.sleep(1,1)를 제거했습니다.정확히 당신이 그걸로 무엇을하려했는지 확신 할 수 없습니다.

로테이션 델타를 0.9에서 1.0으로 변경했습니다. 0.9를 사용하는 이유를 잘 모릅니다. 뒤쪽으로 돌아 가면 내 "10도 회전에 걸리는 로그 시간"이 036으로 거의되지 않을 수도 있습니다.

4.1 시뮬레이터에서 회전은 일반적으로 훨씬 빠릅니다 (약 4 배). 내 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

당신의 최적화는 특히'Path' 나에게 많은 도움이되었다. – Lemberg

3

코드가 꽤 멋지고 간단합니다. 예를 들어 루프를 적게 사용하거나, 모든 것을 함께 그리거나 변수를 결합하여 최적화 할 수 있습니다. 그러나 이렇게하면 지저분해질 수 있습니다.

그리기 코드를 일정하게 유지하는 것이 좋습니다. 당신은 실제로 가장 나쁜 일을하지 않습니다 : 물건을 인스턴스화하고, 명확하고 쉽게 유지할 수 있습니다.

하지만 이중 버퍼를 사용하려고 할 수도 있습니다. 램의 버퍼에서 그림을 그려서 화면에 버퍼를 한 번 대칭 이동시킬 수 있습니다. 이는 일반적으로 일정한 애니메이션 페이스를 얻기 위해 꽤 잘 수행됩니다. 캔버스의 잠금 및 잠금 해제 사용 : 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

관련 문제