Android:使用 onDraw 和 onTouchEvent 绘制线条呈现缓慢

Android: Drawing lines using onDraw and onTouchEvent render slow

我正在尝试使用如下所示的 ontouch 方法和 onDraw 方法绘制四条线:

 @Override
       protected void onDraw(Canvas canvas) {
        initPoints();
        path.reset();
        path.addCircle(topLeftPoint.x,topLeftPoint.y, 16f,       
       Path.Direction.CW);
        canvas.drawLines(points, paint);

        path.addCircle(middleTopPoint.x, middleTopPoint.y, 16f, Path.Direction.CW);
        path.addCircle(topRightPoint.x, topRightPoint.y, 16f, Path.Direction.CW);

//      canvas.drawLine(topRightPoint.x, topRightPoint.y, bottomRightPoint.x, bottomRightPoint.y, paint);
        path.addCircle(bottomRightPoint.x, bottomRightPoint.y, 16f, Path.Direction.CW);

        path.addCircle(middleRightPoint.x, middleRightPoint.y, 16f, Path.Direction.CW);
//      canvas.drawLine(bottomRightPoint.x, bottomRightPoint.y, bottomLeftPoint.x, bottomLeftPoint.y, paint);
        path.addCircle(bottomLeftPoint.x, bottomLeftPoint.y, 16f, Path.Direction.CW);
        path.addCircle(middleBottomPoint.x, middleBottomPoint.y, 16f, Path.Direction.CW);

//      canvas.drawLine(bottomLeftPoint.x, bottomLeftPoint.y, topLeftPoint.x, topLeftPoint.y, paint);
        path.addCircle(topRightPoint.x, topRightPoint.y, 16f, Path.Direction.CW);
        path.addCircle(middleLeftPoint.x, middleLeftPoint.y, 16f, Path.Direction.CW);
        path.close();
        canvas.drawPath(path, paint);
            }

    @Override
    public boolean onTouchEvent(final MotionEvent event) {

        int action = event.getActionMasked();

        if (event.getAction() == MotionEvent.ACTION_DOWN ||
                event.getAction() == MotionEvent.ACTION_MOVE
                || event.getAction() == MotionEvent.ACTION_UP) {

            float xCoordinate = 16;
            float yCoordinate = 16;

            if (event.getX() < 16) {
                xCoordinate = 16;
            } else if (event.getX() > getWidth() - 16) {
                xCoordinate = getWidth() - 16;
            } else {
                xCoordinate = event.getX();
            }

            if (event.getY() < 16) {
                yCoordinate = 16;
            } else if (event.getY() > getHeight() - 16) {
                yCoordinate = getHeight() - 16;
            } else {
                yCoordinate = event.getY();
            }
            if (isInsideTopLeft(event.getX(), event.getY())) {
                topLeftPoint.set(xCoordinate, yCoordinate);

                middleTopPoint.set((topRightPoint.x + topLeftPoint.x) / 2,
                        (topRightPoint.y + topLeftPoint.y) / 2);
                middleLeftPoint.set((bottomLeftPoint.x + topLeftPoint.x) / 2,
                        (bottomLeftPoint.y + topLeftPoint.y) / 2);
                invalidate();
                return true;
            } else if (isInsideTopRight(event.getX(), event.getY())) {
                topRightPoint.set(xCoordinate, yCoordinate);

                middleTopPoint.set((topRightPoint.x + topLeftPoint.x) / 2,
                        (topRightPoint.y + topLeftPoint.y) / 2);
                middleRightPoint.set((bottomRightPoint.x + topRightPoint.x) / 2,
                        (bottomRightPoint.y + topRightPoint.y) / 2);
                invalidate();
                return true;
            } else if (isInsideBottomRight(event.getX(), event.getY())) {
                bottomRightPoint.set(xCoordinate, yCoordinate);

                middleBottomPoint.set((bottomLeftPoint.x + bottomRightPoint.x) / 2,
                        (bottomLeftPoint.y + bottomRightPoint.y) / 2);
                middleRightPoint.set((bottomRightPoint.x + topRightPoint.x) / 2,
                        (bottomRightPoint.y + topRightPoint.y) / 2);
                invalidate();
                return true;
            } else if (isInsideBottomLeft(event.getX(), event.getY())) {
                bottomLeftPoint.set(xCoordinate, yCoordinate);

                middleBottomPoint.set((bottomLeftPoint.x + bottomRightPoint.x) / 2,
                        (bottomLeftPoint.y + bottomRightPoint.y) / 2);
                middleLeftPoint.set((bottomLeftPoint.x + topLeftPoint.x) / 2,
                        (bottomLeftPoint.y + topLeftPoint.y) / 2);
                invalidate();
                return true;
            } 
        }

        return super.onTouchEvent(event);
    }   

但是当我提高触摸速度时,渲染速度变慢了。 我已经使用了路径和线,但性能提升仍然很小。

我从代码中删除的其余方法,例如声明 PointF 对象和使用默认值启动这些对象。

如果有人有任何想法,我将不胜感激。

此致, AurelianR

如果没有完整的代码,就很难检查可能需要改进的地方。 您正在调用 initPoints() 并在每次绘制调用时重置路径。

如果我们在touch事件中考虑以下情况:

if (isInsideTopLeft(event.getX(), event.getY())) {
      // update topLeftPoint, middleTopPoint and middleLeftPoint.
        invalidate();
        return true;
    }

然后在onDraw()

path.reset();  // may be not necessary.
path.addCircle(topLeftPoint.x, topLeftPoint.y, 16f,
            Path.Direction.CW);  // point changed
path.addCircle(middleTopPoint.x, middleTopPoint.y,16f,Path.Direction.CW);//point changed
path.addCircle(middleLeftPoint.x, middleLeftPoint.y, 16f,Path.Direction.CW);// point changed

只有三个调用有更新rest 6路径更新可以避免。可以使用 postInvalidate(dirtyRect)。 如果您添加完整的代码(连同基本注释),那么我可以通过 运行 检查它。

您的问题与性能无关,与触摸处理执行错误有关。触摸处理概念简述:您的视图会跟随触摸事件

ACTION_DOWN :当用户将手指放在您的视图上时收到(一次性)。始终生成。

ACTION_MOVE :当用户移动手指时收到(多次)。如果用户没有移动手指,则不会生成此事件。

ACTION_UP :当用户将手指从您的视图中移开时收到(一次)。始终生成。

DOWN直到UP完成一个触摸事件周期,用户可以多次触摸你的视图。

您的代码:

if (event.getAction() == MotionEvent.ACTION_DOWN ||
            event.getAction() == MotionEvent.ACTION_MOVE
            || event.getAction() == MotionEvent.ACTION_UP){

    if(isInsideTopLeft()){
    // do something
    // return
    } 
    // do same for other cases also.
}

这样你每次都在计算你的案例(isInsideTopLeft() 等),这会导致掉落触摸事件(任何案例都没有涵盖的事件)。您应该做的是在 ACTION_DOWN 中针对特定情况锁定触摸事件,然后只需在 ACTION_MOVE 中移动您的视图,最后在 ACTION_UP 中释放触摸锁定。

工作代码如下:

//create touch lock cases 
private final int TOUCH_STATE_UNLOCKED = 0;
private final int TOUCH_STATE_LOCKED_TOP_LEFT = 1;
private final int TOUCH_STATE_LOCKED_TOP_RIGHT = 2;
private final int TOUCH_STATE_LOCKED_BOTTOM_LEFT = 3;
private final int TOUCH_STATE_LOCKED_BOTTOM_RIGHT = 4;
private final int TOUCH_STATE_LOCKED_MIDDLE_LEFT = 5;
private final int TOUCH_STATE_LOCKED_MIDDLE_BOTTOM = 6;

private int TOUCH_STATE = TOUCH_STATE_UNLOCKED;

 @Override
public boolean onTouchEvent(final MotionEvent event) {

    int action = event.getActionMasked();
    float xCoordinate = 16;
    float yCoordinate = 16;
    int eventX = (int) event.getX();
    int eventY = (int) event.getY();

    if (eventX < 16) {
        xCoordinate = 16;
    } else if (eventX > getWidth() - 16) {
        xCoordinate = getWidth() - 16;
    } else {
        xCoordinate = eventX;
    }

    if (eventY < 16) {
        yCoordinate = 16;
    } else if (eventY > getHeight() - 16) {
        yCoordinate = getHeight() - 16;
    } else {
        yCoordinate = eventY;
    }

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        /* Touch event handling in brief,  read about touch event handling,

        in ACTION_DOWN lock touch event for a particular point.


         */
        case MotionEvent.ACTION_DOWN:

            // lock touch event for a point . subsequent touch events
            if (isInsideTopLeft(event.getX(), event.getY())) {
                TOUCH_STATE = TOUCH_STATE_LOCKED_TOP_LEFT;
            } else if (isInsideTopRight(event.getX(), event.getY())) {
                TOUCH_STATE = TOUCH_STATE_LOCKED_TOP_RIGHT;
            } else if (isInsideBottomLeft(event.getX(), event.getY())) {
                TOUCH_STATE = TOUCH_STATE_LOCKED_BOTTOM_LEFT;
            } else if (isInsideBottomRight(event.getX(), event.getY())) {
                TOUCH_STATE = TOUCH_STATE_LOCKED_BOTTOM_RIGHT;
            } else if (isInsideMiddleLeft(event.getX(), event.getY())) {
                TOUCH_STATE = TOUCH_STATE_LOCKED_MIDDLE_LEFT;
            } else if (isInsideMiddleBottom(event.getX(), event.getY())) {
                TOUCH_STATE = TOUCH_STATE_LOCKED_MIDDLE_BOTTOM;
            }
            Log.e(TAG, "onTouchEvent: TOUCH_STATE = " + TOUCH_STATE);
            break;

        case MotionEvent.ACTION_MOVE:
// simply check for locked case
            if (TOUCH_STATE == TOUCH_STATE_LOCKED_TOP_LEFT) {
                Log.e(TAG, "onTouchEvent: top left locked");
                topLeftPoint.set(xCoordinate, yCoordinate);

                middleTopPoint.set((topRightPoint.x + topLeftPoint.x) / 2,
                        (topRightPoint.y + topLeftPoint.y) / 2);
                middleLeftPoint.set((bottomLeftPoint.x + topLeftPoint.x) / 2,
                        (bottomLeftPoint.y + topLeftPoint.y) / 2);
                // working fine with full invalidate also.
                invalidate();
            }
            // handle other cases also
            break;

        case MotionEvent.ACTION_UP:
            TOUCH_STATE = TOUCH_STATE_UNLOCKED;  // unlock

    }
    return true;
}

发现了一个小的性能优化可能性,为此我正在 GitHub 中创建分支并打开一个拉取请求。