Java Android如何沿着手指画一条平滑的线
我在下面使用这个例子。但是当我的手指在屏幕上移动过快时,线条就会变成单个的点 我不确定我是否能加快绘图速度。或者我应该用一条直线连接最后两个点。这两种解决方案中的第二种似乎是一个很好的选择,除非当你的手指移动得很快时,你会有很长的直线段,然后是尖锐的曲线 如果还有其他的解决方案,听到它们会很好Java Android如何沿着手指画一条平滑的线,java,android,touch,interpolation,curve-fitting,Java,Android,Touch,Interpolation,Curve Fitting,我在下面使用这个例子。但是当我的手指在屏幕上移动过快时,线条就会变成单个的点 我不确定我是否能加快绘图速度。或者我应该用一条直线连接最后两个点。这两种解决方案中的第二种似乎是一个很好的选择,除非当你的手指移动得很快时,你会有很长的直线段,然后是尖锐的曲线 如果还有其他的解决方案,听到它们会很好 感谢您事先提供的帮助。您的MotionEvent中提供的信息可能比您意识到的要多得多 链接中的示例忽略事件中包含的历史接触点。请参阅MotionEvent文档顶部附近的“批处理”部分:除此之外,将点与线连
感谢您事先提供的帮助。您的
MotionEvent
中提供的信息可能比您意识到的要多得多
链接中的示例忽略事件中包含的历史接触点。请参阅
MotionEvent
文档顶部附近的“批处理”部分:除此之外,将点与线连接可能不是一个坏主意。我遇到了非常类似的问题。当调用onTouch方法时,还应该使用方法(在onTouch(MotionEvent)内部)
及
诸如此类
int histPointsAmount = event.getHistorySize();
for(int i = 0; i < histPointsAmount; i++){
// get points from event.getHistoricalX(i);
// event.getHistoricalY(i); and use them for your purpouse
}
int histPointsAmount=event.getHistorySize();
for(int i=0;i
正如您所提到的,一个简单的解决方案是简单地用一条直线连接点。下面是执行此操作的代码:
public void onDraw(Canvas canvas) {
Path path = new Path();
boolean first = true;
for(Point point : points){
if(first){
first = false;
path.moveTo(point.x, point.y);
}
else{
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, paint);
}
确保将油漆从“填充”更改为“笔划”:
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
另一个选项是使用quadTo方法将点与iterpolation连接:
public void onDraw(Canvas canvas) {
Path path = new Path();
boolean first = true;
for(int i = 0; i < points.size(); i += 2){
Point point = points.get(i);
if(first){
first = false;
path.moveTo(point.x, point.y);
}
else if(i < points.size() - 1){
Point next = points.get(i + 1);
path.quadTo(point.x, point.y, next.x, next.y);
}
else{
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, paint);
}
并将dx和dy值添加到点类:
class Point {
float x, y;
float dx, dy;
@Override
public String toString() {
return x + ", " + y;
}
}
这会产生平滑的线条,但有时必须使用循环连接点。
此外,对于较长的绘图任务,这会导致计算量过大
希望这有助于。。。好玩的东西
编辑
我创建了一个演示这些不同技术的快速项目,包括Square的suggessted签名实现。享受:这对你来说可能不再重要了,但我为解决这个问题付出了很多努力,我想与大家分享,可能对其他人有用 提供了@johncarl解决方案的教程对绘图很有帮助,但它们为我的目的提供了一个限制。如果将手指从屏幕中取出并放回屏幕,此解决方案将在上次单击和新单击之间画一条线,使整个图形始终保持连接。所以我试图找到一个解决方案,最后我得到了它!(如果听起来很明显,很抱歉,我是图形初学者)
公共类MainActivity扩展活动{
DrawView-DrawView;
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//设置全屏视图
getWindow().setFlags(WindowManager.LayoutParams.FLAG_全屏,
WindowManager.LayoutParams.FLAG(全屏);
requestWindowFeature(窗口。功能\u无\u标题);
drawView=新的drawView(此);
setContentView(drawView);
requestFocus();
}
}
公共类DrawingPanel扩展了视图实现OnTouchListener{
私有静态最终字符串标记=“DrawView”;
专用静态最终浮动最小值=0.25f;
专用静态最终浮动最大值=0.75f;
私人帆布mCanvas;
专用路径mPath;
私人油漆;
私有LinkedList路径=新LinkedList();
公共绘图面板(上下文){
超级(上下文);
设置聚焦(真);
setFocusableInTouchMode(真);
this.setOnTouchListener(this);
mPaint=新油漆();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(油漆、样式、笔划);
mPaint.setStrokeJoin(油漆.连接.圆形);
mPaint.setStrokeCap(油漆盖圆形);
mPaint.设定行程宽度(6);
mCanvas=新画布();
mPath=新路径();
路径。添加(mPath);
}
@凌驾
已更改尺寸的受保护空心(整数w、整数h、整数oldw、整数oldh){
super.onSizeChanged(w,h,oldw,oldh);
}
@凌驾
受保护的void onDraw(画布){
用于(路径p:路径){
画布绘制路径(p,mPaint);
}
}
私人浮动mX,我的;
专用静态最终浮动接触公差=4;
专用无效触摸启动(浮动x、浮动y){
mPath.reset();
移动到(x,y)的速度;
mX=x;
mY=y;
}
私有无效触摸移动(浮动x、浮动y){
float dx=Math.abs(x-mX);
float dy=Math.abs(y-mY);
如果(dx>=接触公差| | dy>=接触公差){
兆帕四分之一秒(mX,mY,(x+mX)/2,(y+mY)/2);
mX=x;
mY=y;
}
}
私人空间修补(){
mPath.lineTo(mX,mY);
//将路径提交到我们的屏幕外
mCanvas.drawPath(mPath,mPaint);
//杀了它,这样我们就不会重复抽签了
mPath=新路径();
路径。添加(mPath);
}
@凌驾
公共布尔onTouch(视图arg0,运动事件){
float x=event.getX();
float y=event.getY();
开关(event.getAction()){
case MotionEvent.ACTION\u DOWN:
触摸启动(x,y);
使无效();
打破
case MotionEvent.ACTION\u移动:
触摸移动(x,y);
使无效();
打破
case MotionEvent.ACTION\u UP:
润色;
使无效();
打破
}
返回true;
}
}
我用你的手指绘制了android示例,并对其进行了一些修改,以存储每条路径,而不仅仅是最后一条!希望它能帮助别人
干杯。我有这个问题,我在画画
public void onDraw(Canvas canvas) {
Path path = new Path();
if(points.size() > 1){
for(int i = points.size() - 2; i < points.size(); i++){
if(i >= 0){
Point point = points.get(i);
if(i == 0){
Point next = points.get(i + 1);
point.dx = ((next.x - point.x) / 3);
point.dy = ((next.y - point.y) / 3);
}
else if(i == points.size() - 1){
Point prev = points.get(i - 1);
point.dx = ((point.x - prev.x) / 3);
point.dy = ((point.y - prev.y) / 3);
}
else{
Point next = points.get(i + 1);
Point prev = points.get(i - 1);
point.dx = ((next.x - prev.x) / 3);
point.dy = ((next.y - prev.y) / 3);
}
}
}
}
boolean first = true;
for(int i = 0; i < points.size(); i++){
Point point = points.get(i);
if(first){
first = false;
path.moveTo(point.x, point.y);
}
else{
Point prev = points.get(i - 1);
path.cubicTo(prev.x + prev.dx, prev.y + prev.dy, point.x - point.dx, point.y - point.dy, point.x, point.y);
}
}
canvas.drawPath(path, paint);
}
public boolean onTouch(View view, MotionEvent event) {
if(event.getAction() != MotionEvent.ACTION_UP){
Point point = new Point();
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
Log.d(TAG, "point: " + point);
return true;
}
return super.onTouchEvent(event);
}
class Point {
float x, y;
float dx, dy;
@Override
public String toString() {
return x + ", " + y;
}
}
public class MainActivity extends Activity {
DrawView drawView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set full screen view
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
drawView = new DrawView(this);
setContentView(drawView);
drawView.requestFocus();
}
}
public class DrawingPanel extends View implements OnTouchListener {
private static final String TAG = "DrawView";
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
private LinkedList<Path> paths = new LinkedList<Path>();
public DrawingPanel(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPath = new Path();
paths.add(mPath);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath = new Path();
paths.add(mPath);
}
@Override
public boolean onTouch(View arg0, MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
Path path = new Path();
if (points.size() > 1) {
Point prevPoint = null;
for (int i = 0; i < points.size(); i++) {
Point point = points.get(i);
if (i == 0) {
path.moveTo(point.x, point.y);
} else {
float midX = (prevPoint.x + point.x) / 2;
float midY = (prevPoint.y + point.y) / 2;
if (i == 1) {
path.lineTo(midX, midY);
} else {
path.quadTo(prevPoint.x, prevPoint.y, midX, midY);
}
}
prevPoint = point;
}
path.lineTo(prevPoint.x, prevPoint.y);
}
public class DrawView extends View implements OnTouchListener {
private static final String TAG = "DrawView";
List<Point> points = new ArrayList<Point>();
Paint paint = new Paint();
List<Integer> newLine = new ArrayList<Integer>();
public DrawView(Context context, AttributeSet attrs){
super(context, attrs);
setFocusable(true);
setFocusableInTouchMode(true);
setClickable(true);
this.setOnTouchListener(this);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20);
}
public void setColor(int color){
paint.setColor(color);
}
public void setBrushSize(int size){
paint.setStrokeWidth((float)size);
}
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
paint.setColor(Color.BLUE);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20);
}
@Override
public void onDraw(Canvas canvas) {
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
for (int i = 0; i<points.size(); i++) {
Point newPoint = new Point();
if (newLine.contains(i)||i==0){
newPoint = points.get(i)
path.moveTo(newPoint.x, newPoint.y);
} else {
newPoint = points.get(i);
path.lineTo(newPoint.x, newPoint.y);
}
}
canvas.drawPath(path, paint);
}
public boolean onTouch(View view, MotionEvent event) {
Point point = new Point();
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
Log.d(TAG, "point: " + point);
if(event.getAction() == MotionEvent.ACTION_UP){
// return super.onTouchEvent(event);
newLine.add(points.size());
}
return true;
}
}
class Point {
float x, y;
@Override
public String toString() {
return x + ", " + y;
}
}
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.util.*;
public class DrawView extends View implements OnTouchListener {
private static final String TAG = "DrawView";
List<Point> points = new ArrayList<Point>();
Paint paint = new Paint();
List<Integer> newLine = new ArrayList<Integer>();
public DrawView(Context context, AttributeSet attrs){
super(context, attrs);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
}
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
}
@Override
public void onDraw(Canvas canvas) {
for (int i = 0; i<points.size(); i++) {
Point newPoint = new Point();
Point oldPoint = new Point();
if (newLine.contains(i)||i==0){
newPoint = points.get(i);
oldPoint = newPoint;
} else {
newPoint = points.get(i);
oldPoint = points.get(i-1);
}
canvas.drawLine(oldPoint.x, oldPoint.y, newPoint.x, newPoint.y, paint);
}
}
public boolean onTouch(View view, MotionEvent event) {
Point point = new Point();
point.x = event.getX();
point.y = event.getY();
points.add(point);
invalidate();
Log.d(TAG, "point: " + point);
if(event.getAction() == MotionEvent.ACTION_UP){
// return super.onTouchEvent(event);
newLine.add(points.size());
}
return true;
}
}
class Point {
float x, y;
@Override
public String toString() {
return x + ", " + y;
}
}
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
path.lineTo(historicalX, historicalY);
}
// After replaying history, connect the line to the touch point.
path.lineTo(eventX, eventY);
public class DrawByFingerCanvas extends View {
private Paint brush = new Paint(Paint.ANTI_ALIAS_FLAG);
private Path path = new Path();
public DrawByFingerCanvas(Context context) {
super(context);
brush.setStyle(Paint.Style.STROKE);
brush.setStrokeWidth(5);
}
@Override
protected void onDraw(Canvas c) {
c.drawPath(path, brush);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(x,y);
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(x, y);
break;
default:
return false;
}
invalidate();
return true;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawByFingerCanvas(this));
}
fun applySmoothing(smoothingIterations: Int) {
for (z in 1..smoothingIterations) {
for (i in graphPoints.indices) {
if (i > 0 && i < graphPoints.size-1) {
val previousPoint = graphPoints[i-1]
val currentPoint = graphPoints[i]
val nextPoint = graphPoints[i+1]
val midX = (previousPoint.x + currentPoint.x + nextPoint.x) / 3
val midY = (previousPoint.y + currentPoint.y + nextPoint.y) / 3
graphPoints[i].x = midX
graphPoints[i].y = midY
}
}
}
}