如何优化在画布上绘制文本画布、文本

2023-09-06 11:22:50 作者:走在春暖花開處

我有3个圈的窗口,它们同时旋转。一切都很好,直到文本添加到圈子里,然后旋转开始落后。

I have window with 3 circles, they are rotating simultaneously. Everything is good until a Add text to the circles, then the rotation starts to lagging.

如何绘制进行优化画布?这是我的code:

How can i optimize drawing on canvas ? This is my code:

@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优化。这是如何实现的。

EDIT To improve performance and remove drawing from UI thread I have Used Double Buffering With SurfaceView and implement @Morgans optimizations. That is how it realized.

的 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 的

And the main work is done in the 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();
}
   .................................................................
}

双缓冲在code两行实施

Double Buffering is implemented in the two lines of code

帆布= mSurfaceHolder.lockCanvas(NULL); 在这里我从表面观画布,我会画下一个帧

canvas = mSurfaceHolder.lockCanvas(null); here I take from surface view canvas in which i will draw next frame.

mSurfaceHolder.unlockCanvasAndPost(画布); 我在这里overlaping上SurfaceView当前图像的新canwas,这就是时刻,影像学改变。要知道,如果你有透明元素则previous图像将仍然可见,图像不会被替换,但overlaped。

mSurfaceHolder.unlockCanvasAndPost(canvas); here I am overlaping current image on SurfaceView with new canwas, this is the moment where image changes. Be aware if you have transparent elements then the previous image will be still visible, Image is not replaced, but overlaped.

推荐答案

下面是一个版本的code,它包含了一些优化。

Below is a version of your code that contains a few optimizations.

首先,我尽量不画,目前离屏的线条和文字。我通过跟踪的旋转角度,并跳过对90度和270度之间的净旋转绘图做到这一点。在我的2.3模拟器这种改进了25%的整体性能。

First, I try not to draw the lines and text that currently offscreen. I do this by tracking the rotation angle, and skipping the drawing for net rotations between 90 and 270 degrees. On my 2.3 simulator this improved performance overall by 25%.

第二,我缓存的字符串我将通过初始化数组(的ArrayList&LT;路径&GT; )来绘制一个路径每串我需要绘制。我这样做是在同一个地方你是一次性的初始化 mPaint 。然后,我用画canvas.drawPath(...)的字符串。在我的2.3模拟器由其他33%的这种改善的性能。净效应是大约两倍转速。此外,从​​摆动围绕停止的文字。

Second, I "cache" the strings I am going to draw by initializing an array (ArrayList<Path>) with one Path for each string I need to draw. I do this in the same place you were one-time initializing the mPaint. Then I draw the strings using canvas.drawPath(...). On my 2.3 simulator this improved performance by another 33%. The net effect was to about double the rotation speed. Also, it stopped the text from "wiggling around".

其他一些注意事项:

我删除了视频下载(1,1)。不知道正是你试图与该完成的任务。

I removed the Thread.sleep(1,1). Not sure exactly what you were trying to accomplish with that.

我是从0.9改为旋转增量为1.0。不知道为什么你正在使用0.9。请注意,如果您更改备份,我的登录时需要旋转10度会因为不是那么回事 mRotation%10 可能很少是0。

I changed rotation delta to 1.0 from 0.9. Not sure why you were using 0.9. Note that if you change to back, my "log time it takes to rotate 10 degrees" will not quite work since mRotation % 10 may seldom be 0.

在4.1模拟器,旋转一般比我的2.3模拟器更快(约4倍)。和4.1设备也较快呢。

On a 4.1 simulator, the rotation was generally much faster (about 4x) than on my 2.3 simulator. And a 4.1 device was faster yet.

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();
}
}
 
精彩推荐
图片推荐