android - 如何在 Canvas 上绘制可变宽度的路径

标签 android android-canvas

我正在使用以下代码行在 Canvas 上绘制路径。到目前为止,一切正常,我可以使用此代码轻松绘制路径。

但是现在我们的要求是绘制一条可变宽度的路径,这意味着用户绘制的路径是基于用户施加的压力,我的意思是说,如果用户施加轻的压力,则路径会很细,如果用户施加压力,则路径会很细。施加高压,路径会变厚等等。到目前为止,我也成功地绘制了一条可变宽度的路径,但绘制的线条并不平滑。为什么会发生这种情况?我的代码中有什么遗漏的吗?

帮我解决这个问题。

我用于绘制具有单一宽度的路径的代码

 public class FingerPaint extends GraphicsActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }

    public void colorChanged(int color) 
    {

    }

    public class MyView extends View 
    {
        private static final float STROKE_WIDTH = 5f;       

        private Paint paint = new Paint();
        
        private Path mPath = new Path();
        ArrayList<Path> mPaths = new ArrayList<Path>();
        
        ArrayList<Integer> mStrokes = new ArrayList<Integer>();

        private float lastTouchX;
        private float lastTouchY;
        private final RectF dirtyRect = new RectF();
        
        private int lastStroke = -1;
        int variableWidthDelta = 0;
        
        private float           mX, mY;
        
        private static final float       TOUCH_TOLERANCE = 4;

        public MyView(Context context) 
        {
            super(context);

            paint.setAntiAlias(true);
            paint.setDither(true);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setStrokeCap(Paint.Cap.ROUND);    
            paint.setStrokeWidth(STROKE_WIDTH);
        }

        public void clear()
        {
            mPath.reset();
            // Repaints the entire view.
            invalidate();
        }

        @Override
        protected void onDraw(Canvas canvas) 
        {
            for(int i=0; i<mPaths.size();i++)
            {
                paint.setStrokeWidth(mStrokes.get(i));
                canvas.drawPath(mPaths.get(i), paint);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) 
        {
            float eventX = event.getX();
            float eventY = event.getY();
            int historySize = event.getHistorySize();

            switch (event.getAction()) 
            {
                case MotionEvent.ACTION_DOWN:
                {
                    resetDirtyRect(eventX, eventY);
                    mPath.reset();
                    mPath.moveTo(eventX, eventY);
                    mX = eventX;
                    mY = eventY;
                    break;                  
                }
                case MotionEvent.ACTION_MOVE:
                {                   
                    if (event.getPressure()>=0.00 && event.getPressure()<0.05)
                    {
                        variableWidthDelta = -2;
                    }
                    else if (event.getPressure()>=0.05 && event.getPressure()<0.10)
                    {
                        variableWidthDelta = -2;
                    }
                    else if (event.getPressure()>=0.10 && event.getPressure()<0.15)
                    {
                        variableWidthDelta = -2;
                    }
                    else if (event.getPressure()>=0.15 && event.getPressure()<0.20)
                    {
                        variableWidthDelta = -2;
                    }
                    else if (event.getPressure()>=0.20 && event.getPressure()<0.25)
                    {
                        variableWidthDelta = -2;
                    }
                    else if (event.getPressure() >= 0.25 && event.getPressure()<0.30)
                    {
                        variableWidthDelta = 1;
                    }
                    else if (event.getPressure() >= 0.30 && event.getPressure()<0.35)
                    {
                        variableWidthDelta = 2;
                    }
                    else if (event.getPressure() >= 0.35 && event.getPressure()<0.40)
                    {
                        variableWidthDelta = 3;
                    }
                    else if (event.getPressure() >= 0.40 && event.getPressure()<0.45)
                    {
                        variableWidthDelta = 4;
                    }
                    else if (event.getPressure() >= 0.45 && event.getPressure()<0.60)
                    {
                        variableWidthDelta = 5;
                    }
                    
                    float dx = Math.abs(eventX - mX);
                    float dy = Math.abs(eventY - mY);

                    if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
                    {
                        if(lastStroke != variableWidthDelta)
                        {   
                            mPath.lineTo(mX, mY);
                            
                            mPath = new Path();
                            mPath.moveTo(mX,mY);
                            mPaths.add(mPath);
                            mStrokes.add(variableWidthDelta);
                        }
                        
                        mPath.quadTo(mX, mY, (eventX + mX)/2, (eventY + mY)/2);
                        mX = eventX;
                        mY = eventY;
                    }

                    for (int i = 0; i < historySize; i++)
                    {
                        float historicalX = event.getHistoricalX(i);
                        float historicalY = event.getHistoricalY(i);
                        expandDirtyRect(historicalX, historicalY);
                    }
                    
                    break;
                }
                case MotionEvent.ACTION_UP:
                {
                    for (int i = 0; i < historySize; i++)
                    {
                        float historicalX = event.getHistoricalX(i);
                        float historicalY = event.getHistoricalY(i);
                        expandDirtyRect(historicalX, historicalY);
                    }

                   mPath.lineTo(mX, mY);                   
                   break;
                }
            }
            
            // Include half the stroke width to avoid clipping.
            invalidate();

            lastTouchX = eventX;
            lastTouchY = eventY;
            lastStroke = variableWidthDelta;
            
            return true;
        }
        
        private void expandDirtyRect(float historicalX, float historicalY) 
        {
            if (historicalX < dirtyRect.left)
            {
                dirtyRect.left = historicalX;
            } 
            else if (historicalX > dirtyRect.right)
            {
                dirtyRect.right = historicalX;
            }
            if (historicalY < dirtyRect.top) 
            {
                dirtyRect.top = historicalY;
            } 
            else if (historicalY > dirtyRect.bottom) 
            {
                dirtyRect.bottom = historicalY;
            }
        }

        /**
         * Resets the dirty region when the motion event occurs.
         */
        private void resetDirtyRect(float eventX, float eventY) 
        {
            // The lastTouchX and lastTouchY were set when the ACTION_DOWN
            // motion event occurred.
            dirtyRect.left = Math.min(lastTouchX, eventX);
            dirtyRect.right = Math.max(lastTouchX, eventX);
            dirtyRect.top = Math.min(lastTouchY, eventY);
            dirtyRect.bottom = Math.max(lastTouchY, eventY);
        }
    }
}

enter image description here

最佳答案

路径不支持可变宽度绘制。

您可以使用 MotionEvent.getPressure() 获取触摸压力和速度和 VelocityTracker.computeCurrentVelocity()方法。

接下来,您需要创建一个函数来将特定速度或压力映射到特定宽度。

假设您已准备好这些,绘制可变宽度路径的一种方法是将路径分为多个路径,每个路径具有不同的宽度。例如,如果宽度需要在单个路径的过程中从 10 增加到 50,则可以有 10 条路径,而不是宽度 5、10、15、20...等等。您必须进行大量优化,因为创建大量 Path 对象会导致内存消耗。

另一种方法是使用 quad-curvesbezier curves .

关于android - 如何在 Canvas 上绘制可变宽度的路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20560322/

相关文章:

android - 子 fragment 无缘无故被销毁

android - 在搜索栏中创建垂直线

android - 在android中旋转90度

android - 在 Android 中使用 arcTo 创建凹路径

java - 如何在同一 Activity 中使用不同的 View

android - 如何查找 Android Google Play 服务版本

Java:TCP套接字连接。客户端接收 null/readline() 返回 null

java - 是否可以在视频中添加文字叠加层?

android - lockCanvas是什么意思(详解)

android - 如何在android中的 Canvas 中绘制垂直文本