当前位置: 首页 > 知识库问答 >
问题:

如何绘制具有可变笔划宽度的路径

欧阳炜
2023-03-14

我的代码基本上来自此示例(http://corner.squareup.com/2010/07/smooth-signatures.html)和Google API(FingerPaint),但现在我想使用类VelocityTracker,以便根据手指的速度更改笔画宽度。

我想我可以把一条路径分成更小的部分,但我没有找到任何例子。还有第二篇文章(http://corner.squareup.com/2012/07/smoother-signatures.html)但我既没有特定的bezier曲线类,也没有收集<code>ArrayList

有人知道如何处理这个问题吗?我两周前开始学习代码,所以我对这一切都很陌生。

编辑:我试图实现我的MotionEvents的速度,并在运行应用程序时使用LogCat来跟踪当前速度。它确实工作了,但是当我试图使用速度作为mPaint.setStrokeWidth的参数的一部分时,我没有得到我真正想要的。我在画布上画的路径的宽度一直在变化,从我开始画线的那一刻起,直到我把手指向上移动。这就是为什么我想把路径分成更小的部分,因为现在,只有最后追踪的速度影响行程宽度。

public class SignatureView extends View {

    private static final String TAG = SignatureView.class.getSimpleName();
    private static final float STROKE_WIDTH = 10;
    private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
    private final double TOUCH_TOLERANCE = 5;
    private int h = getResources().getDisplayMetrics().heightPixels;
    private int w = getResources().getDisplayMetrics().widthPixels;

    private Path mPath = new Path();
    private Paint mPaint = new Paint();
    private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
    private Bitmap mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    private Canvas mCanvas = new Canvas(mBitmap);

    private float mX, mY;
    private float lastTouchX, lastTouchY;
    private final RectF dirtyRect = new RectF();

public SignatureView(Context context, AttributeSet attrs) {
    super(context, attrs);

    mPaint.setAntiAlias(true);
    mPaint.setColor(Color.BLACK);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeWidth(INITIAL_STROKE_WIDTH);

    Log.d(TAG, "TOUCH_TOLERANCE = " +TOUCH_TOLERANCE);
}

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);            
    canvas.drawPath(mPath, mPaint);
}

@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:

            float dx = Math.abs(eventX - mX);
            float dy = Math.abs(eventY - mY);

            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {

                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);
            mCanvas.drawPath(mPath, mPaint);
            mPath.reset();
            break;

        default:
            Log.d(TAG, "Ignored touch event: " + event.toString());
        return false;
    }

    // Include half the stroke width to avoid clipping.
        invalidate(     (int) (dirtyRect.left - HALF_STROKE_WIDTH),
                        (int) (dirtyRect.top - HALF_STROKE_WIDTH),
                        (int) (dirtyRect.right + HALF_STROKE_WIDTH),
                        (int) (dirtyRect.bottom + HALF_STROKE_WIDTH));

    lastTouchX = eventX;
    lastTouchY = eventY;
    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;
    }
}

private void resetDirtyRect(float eventX, float eventY) {

    dirtyRect.left = Math.min(lastTouchX, eventX);
    dirtyRect.right = Math.max(lastTouchX, eventX);
    dirtyRect.top = Math.min(lastTouchY, eventY);
    dirtyRect.bottom = Math.max(lastTouchY, eventY);
}
}

共有1个答案

佴德曜
2023-03-14

每次笔划值根据速度变化时,都可以使用“分割路径对象”。在您的<code>SignatureView<code>类中,添加

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

并使用另一个ArrayList来保持每个路径的笔划值

ArrayList<int> strokes = new ArrayList<int>();

添加一个变量lastStroke,以及lastTouchXlastTouchY。我建议您将<code>lastStroke

private int lastStroke = -1; //give an initial value

现在您的onTouchEvent方法应该是这样的

@Override
public boolean onTouchEvent(MotionEvent event) {
    float eventX = event.getX();
    float eventY = event.getY();
    int historySize = event.getHistorySize();
    int eventStroke= //calculate stroke size with velocity and make it between 1-10 or any range you seem fit

    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:

            float dx = Math.abs(eventX - mX);
            float dy = Math.abs(eventY - mY);

            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                if(lastStroke != evetnStroke){
                    mPath = new Path();
                    mPath.moveTo(mX,mY);
                    mPaths.Add(mPath);
                    mStrokes.Add(eventStroke);
                }
                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;

        default:
            Log.d(TAG, "Ignored touch event: " + event.toString());
        return false;
    }
    // Include half the stroke width to avoid clipping.
    invalidate(     (int) (dirtyRect.left - HALF_STROKE_WIDTH),
                    (int) (dirtyRect.top - HALF_STROKE_WIDTH),
                    (int) (dirtyRect.right + HALF_STROKE_WIDTH),
                    (int) (dirtyRect.bottom + HALF_STROKE_WIDTH));

    lastTouchX = eventX;
    lastTouchY = eventY;
    lastStroke = eventStroke;
    return true;
}

你的ondraw方法是

@Override
protected void onDraw(Canvas canvas) {
    for(int i=0; i<mPaths.size();i++){
        mPaint.setStrokeWidth(strokes.get(i));
        canvas.drawPath(mPaths.get(i), mPaint);
    }
}

这是基本思想。您需要修改它才能使其工作。

 类似资料:
  • 我正在尝试绘制一个只有线条但笔画宽度不同的对象。这可能吗? 我的尝试: 不成功,最后一个笔画宽度用于整个< code >路径。

  • 问题内容: 具有SceneKit线图元类型的笔划宽度(http://beesandbombs.tumblr.com/post/96195652814/cube)) using scene kit. 我试图立刻解决具体是如何编辑的笔画宽度的SCNGeometryPrimitiveType.Line元素。 我创建线条的基本方法是这样的: 但我不知道如何增加使用SceneKit 绘制的线条的宽度。我该如

  • 问题内容: 谁能描述我如何使用opencv或simplecv在python中实现SWT? 问题答案: 好的,这里是这样: 该链接包含有关实现的详细信息,底部的代码下载链接:SWT 为了完整起见,还提到SWT或Stroke Width Transform由Epshtein等人在2010年设计,迄今为止已成为最成功的文本检测方法之一。它不使用机器学习或详尽的测试。基本上在对输入图像进行Canny边缘检

  • 为了做到这一点,我将需要找到控制点的“边缘”曲线决定形状,然后填充形状,这是发现在两个新的曲线之间。然而,我很不确定如何做到这一点。我想过的一件事是通过简单地在连接原控制点和原终点的直线上画垂直段来确定新曲线的起点和终点,但这仍然不能解决为新曲线寻找新控制点的问题。

  • 我希望生成一个可视化xml文件结构的图形。 我创建了一个节点列表来表示xml文件 每个节点包含3个字符串:xml标记、属性和内容。 xml 文件如下所示: 我希望通过枚举节点列表,使用Plotly和igraph库生成一个树形图。 我在这里使用这个网站作为参考。 我的XML文件包含子元素数量可变的元素。然而,给出的例子只向我展示了如何开发一个具有固定数量的子节点的树(这个例子展示了每个节点2个子节点

  • 我有一个自定义视图,我可以在其中画线,我也可以选择线的宽度。我还有一个统计数据,在另一个片段中,我应该显示每一个笔画画了多少行。 所以我想创建一个自定义的监听器,它应该对不同的笔划宽度做出反应。每当笔划发生变化时,统计数据应显示所选笔划的新计数。。。 我用界面尝试了一下,但我真的不知道如何前进。我该怎么做?