在画布中绘制路径时曲线被破坏

时间:2013-12-18 08:01:58

标签: 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  implements OnTouchListener
    {
        private static final float      STROKE_WIDTH = 3f;      

        private Paint                   paint = new Paint();

        private Path                    mPath = new Path();

        ArrayList<Path>                 mPaths = new ArrayList<Path>();

        private Canvas                  m_CanvasView;

        private Bitmap                  m_CanvasBitmap;

        int                             variableWidthDelta = 1;

        private float                   mX, mY;

        private static final float       TOUCH_TOLERANCE = 4;       

        ArrayList<Point>                points = new ArrayList<Point>(64);

        public MyView(Context context) 
        {
            super(context);
            setFocusable(true);
            setFocusableInTouchMode(true); 
            this.setOnTouchListener(this);

            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);
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) 
        {
            super.onSizeChanged(w, h, oldw, oldh);

            m_CanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            m_CanvasView = new Canvas(m_CanvasBitmap);
        }

        @Override
        protected void onDraw(Canvas canvas) 
        {
            canvas.drawBitmap(m_CanvasBitmap, 0f, 0f, null);            
            m_CanvasView.drawPath(mPath, paint);    
        }

        public boolean onTouch(View arg0, MotionEvent event)
        {
            float x = event.getX();
            float y = event.getY();

            switch (event.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                {        
                    mPath.reset();
                    mPath.moveTo(x, y);
                    mX = x;
                    mY = y;
                    break;
                }
                case MotionEvent.ACTION_MOVE:
                {                           
                    if (event.getPressure()>=0.00 && event.getPressure()<0.05)
                    {
                        variableWidthDelta = 1;
                    }
                    else if (event.getPressure()>=0.05 && event.getPressure()<0.10)
                    {
                        variableWidthDelta = 1;
                    }
                    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 = 1;
                    }
                    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 = 2;
                    }
                    else if (event.getPressure() >= 0.40 && event.getPressure()<0.45)
                    {
                        variableWidthDelta = 3;
                    }
                    else if (event.getPressure() >= 0.45 && event.getPressure()<0.50)
                    {
                        variableWidthDelta = 4;
                    }                  

                    paint.setStrokeWidth(STROKE_WIDTH + variableWidthDelta);

                    float dx = Math.abs(x - mX);
                    float dy = Math.abs(y - mY);

                    if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) 
                    {           
                        points.add(new Point(event.getX(), event.getY()));
                        mPath = new Path();

                        mPath = generatePath();                                                                                                                                     
                    }

                    break;
                }
                case MotionEvent.ACTION_UP:
                {
                    break;
                }
            }

            invalidate();
            return true;
        }

        class Point
        {
            public final float x;
            public final float y;
            public Point(float x, float y)
            {
                this.x = x;
                this.y = y;
            }
        }

        public Path generatePath()
        {
            final float tangentScale = 0.3F;
            int pointcount = points.size();

            mPath.moveTo(points.get(0).x, points.get(0).y);
            mPath.cubicTo(
                    points.get(0).x + (points.get(1).x - points.get(0).x)*tangentScale,
                    points.get(0).y + (points.get(1).y - points.get(0).y)*tangentScale,
                    points.get(1).x - (points.get(2).x - points.get(0).x)*tangentScale,
                    points.get(1).y - (points.get(2).y - points.get(0).y)*tangentScale,
                    points.get(1).x, points.get(1).y
                    );
            for(int p=2; p<pointcount-1; p++)
            {
                mPath.cubicTo(
                        points.get(p-1).x + (points.get(p).x - points.get(p-2).x)*tangentScale,
                        points.get(p-1).y + (points.get(p).y - points.get(p-2).y)*tangentScale,
                        points.get(p).x - (points.get(p+1).x - points.get(p-1).x)*tangentScale,
                        points.get(p  ).y - (points.get(p+1).y - points.get(p-1).y)*tangentScale,
                        points.get(p).x, points.get(p).y
                        );
            }
            mPath.cubicTo(
                    points.get(pointcount-2).x + (points.get(pointcount-1).x - points.get(pointcount-3).x)*tangentScale,
                    points.get(pointcount-2).y + (points.get(pointcount-1).y - points.get(pointcount-3).y)*tangentScale,
                    points.get(pointcount-1).x - (points.get(pointcount-1).x - points.get(pointcount-2).x)*tangentScale,
                    points.get(pointcount-1).y - (points.get(pointcount-1).y - points.get(pointcount-2).y)*tangentScale,
                    points.get(pointcount-1).x, points.get(pointcount-1).y
                    );

            return mPath;
        }
    }
}

enter image description here

1 个答案:

答案 0 :(得分:1)

onTouch事件的触发速度不够快,所以如果你画得足够快,你就会发现这种情况。

您可以通过获取此onTouch事件与最后一个事件之间的所有记录点来提高点分辨率。使用event.getHistorySize()获取可用积分金额,并使用event.getHistoricalX(int)event.getHistoricalY(int)获取其位置。

如果你仍有问题,你可能不得不实施某种点插值。 以下是使用cubicTo()获得平滑路径的简单示例:
points是要绘制的所有点的数组 pointcount是点数

//This code is untested
public Path generatePath(){
Path path = new Path();
if( points.size() < 3 ) return path;
final float tangentScale = 0.3;

path.moveTo(points[0].x, points[0].y);
path.cubicTo(
    points[0].x + (points[1].x - points[0].x)*tangentScale,
    points[0].y + (points[1].y - points[0].y)*tangentScale,
    points[1].x - (points[2].x - points[0].x)*tangentScale,
    points[1].y - (points[2].y - points[0].y)*tangentScale,
    points[1].x, points[1].y
    );
for(int p=2; p<pointcount-1; p++){
    path.cubicTo(
        points[p-1].x + (points[p].x - points[p-2].x)*tangentScale,
        points[p-1].y + (points[p].y - points[p-2].y)*tangentScale,
        points[p  ].x - (points[p+1].x - points[p-1].x)*tangentScale,
        points[p  ].y - (points[p+1].y - points[p-1].y)*tangentScale,
        points[p].x, points[p].y
        );
}
path.cubicTo(
    points[pointcount-2].x + (points[pointcount-1].x - points[pointcount-3].x)*tangentScale,
    points[pointcount-2].y + (points[pointcount-1].y - points[pointcount-3].y)*tangentScale,
    points[pointcount-1].x - (points[pointcount-1].x - points[pointcount-2].x)*tangentScale,
    points[pointcount-1].y - (points[pointcount-1].y - points[pointcount-2].y)*tangentScale,
    points[pointcount-1].x, points[pointcount-1].y
    );
return path;
}

根据您的具体情况进行调整: (您必须修改上述方法才能使用.get()而不是数组访问器,因为我在这里使用了一个列表。

//A class for holding x and y values:
class Point{
    public final float x;
    public final float y;
    public Point(float x, float y){
        this.x = x;
        this.y = y;
    }
}

//In your View:
ArrayList<Point> points = new ArrayList<Points>(64);

//In the onTouch-method if the tolerance is ok:
points.add(new Point(event.getX(), event.GetY());
mPath = generatePath();

以上将生成一个可以绘制的圆角路径。请注意,它使用所有点,因此每次都会重绘它们(您可能必须清除画布以避免一些人为因素)。您可能希望在抬起手指时移动generatePath - 呼叫,这样您在绘制时会获得更快但锯齿状的预览路径。