I tried to get all the x,y coordinates that are drawn in the path on the canvas. But i couldn't get the points. I am getting only a few points. Why is that?
Is there any way i can get all the x and y points?
I use FingerPaint.java as a starting point.
Link to Original FingerPaint.java
Here is the code i modified and using:
package com.schogini.whiteboard;
import java.util.ArrayList;
import java.util.StringTokenizer;
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
//import android.widget.Toast;
public class FingerPaint extends Activity implements ColorPickerDialog.OnColorChangedListener {
//color - ARGB
int a = 255;
int r = 255;
int g = 0;
int b = 0;
//erase Flag
boolean eraseFlag=false;
//Internet Posting
static String toSend;
static String parseStr;
//array for storing x,y,r,g,b,a
static ArrayList<String> rArray,gArray,bArray,alphaArray,xPointArray,yPointArray;
//ArrayList<String> rArray_in,gArray_in,bArray_in,alphaArray_in,xPointArray_in,yPointArray_in;
//Drawing types
private static Paint mPaint;
private MaskFilter mEmboss;
private MaskFilter mBlur;
//touch - Path Coordinates
private static float mX;
private static float mY;
//MyView object
MyView m;
//point populate
float dx,dy,distance;
int dd,ii;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m = new MyView(FingerPaint.this);
setContentView(m);
//move to appropriate module
rArray = new ArrayList<String>();
gArray = new ArrayList<String>();
bArray = new ArrayList<String>();
alphaArray = new ArrayList<String>();
xPointArray = new ArrayList<String>();
yPointArray = new ArrayList<String>();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFF0000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
//width of the drawing brush
mPaint.setStrokeWidth(12);
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
}
public void parsing(String str22){
//example string received from server
// parseStr = "255,0,0,1.0,88.0,100.0||255,0,0,1.0,88.0,101.0||255,0,0,1.0,88.0,102.0||255,0,0,1.0,88.0,103.0||255,0,0,1.0,88.0,104.0||255,0,0,1.0,88.0,105.0||255,0,0,1.0,88.0,106.0||255,0,0,1.0,88.0,107.0||255,0,0,1.0,88.0,108.0," +
// "||255,0,0,1.0,88.0,109.0||255,0,0,1.0,88.0,110.0||255,0,0,1.0,88.0,111.0||255,0,0,1.0,88.0,112.0||255,0,0,1.0,88.0,113.0||255,0,0,1.0,88.0,114.0||255,0,0,1.0,88.0,115.0||255,0,0,1.0,88.0,116.0||255,0,0,1.0,88.0,117.0," +
// "||255,0,0,1.0,88.0,118.0||255,0,0,1.0,88.0,119.0||255,0,0,1.0,88.0,120.0||255,0,0,1.0,80.0,100.0||255,0,0,1.0,81.0,100.0||255,0,0,1.0,82.0,100.0||255,0,0,1.0,83.0,100.0||255,0,0,1.0,84.0,100.0||255,0,0,1.0,85.0,100.0," +
// "||255,0,0,1.0,86.0,100.0||255,0,0,1.0,87.0,100.0||255,0,0,1.0,88.0,100.0||255,0,0,1.0,89.0,100.0||255,0,0,1.0,91.0,100.0||255,0,0,1.0,92.0,100.0||255,0,0,1.0,93.0,100.0||255,0,0,1.0,94.0,100.0||255,0,0,1.0,95.0,100.0," +
// "||255,0,0,1.0,96.0,100.0||255,0,0,1.0,97.0,100.0||255,0,0,1.0,98.0,100.0||255,0,0,1.0,99.0,100.0||255,0,0,1.0,100.0,100.0||255,0,0,1.0,101.0,100.0||255,0,0,1.0,102.0,100.0||255,0,0,1.0,103.0,100.0,";
parseStr="255,0,0,255,85.351585,90.17996||255,0,0,255,85.351585,90.17996||255,0,0,255,123.97784,80.883064||255,0,0,255,164.47311,71.58616||255,0,0,255,182.22874,65.543175||255,0,0,255,233.9381,48.3439||255,0,0,255,247.95567,42.30091||255,0,0,255,252.62822,40.441532||255,0,0,255,252.62822,40.441532||255,0,0,255,86.9091,167.34427||255,0,0,255,86.9091,167.34427||255,0,0,255,86.9091,167.34427||255,0,0,255,151.70154,133.41057||255,0,0,255,190.3278,118.07068||255,0,0,255,235.18411,98.54717||255,0,0,255,248.57867,92.03935||255,0,0,255,256.36627,89.71512||255,0,0,255,257.30078,89.250275||255,0,0,255,257.30078,89.250275||255,0,0,255,82.548065,277.97742||255,0,0,255,82.548065,277.97742||255,0,0,255,82.548065,277.97742||255,0,0,255,116.50179,258.91876||255,0,0,255,152.63605,240.7898||255,0,0,255,209.95244,215.68816||255,0,0,255,239.23363,199.41858|开发者_如何转开发|255,0,0,255,249.8247,192.4459||255,0,0,255,261.35028,186.86777||255,0,0,255,261.9733,186.86777||255,0,0,255,261.9733,186.86777||255,0,0,255,50.77485,53.92204||255,0,0,255,50.77485,53.92204||255,0,0,255,50.77485,53.92204||255,0,0,255,50.77485,53.92204||255,0,0,255,48.905834,62.7541||255,0,0,255,39.56077,133.87541||255,0,0,255,44.856304,185.93808||255,0,0,255,53.578365,266.82114||255,0,0,255,53.578365,287.27435||255,0,0,255,51.70935,301.6845||255,0,0,255,51.39785,303.07904||255,0,0,255,51.086346,303.5439||255,0,0,255,51.086346,303.5439||255,0,0,255,90.02411,362.57922||255,0,0,255,88.155106,355.14172||255,0,0,255,109.02575,317.0244||255,0,0,255,133.01141,323.53226||255,0,0,255,155.43958,340.26666||255,0,0,255,166.65363,344.91513||255,0,0,255,185.03226,338.4073||255,0,0,255,193.13133,326.78616||255,0,0,255,210.26395,292.38763||255,0,0,255,228.01956,296.10638||255,0,0,255,259.7928,283.09073||255,0,0,255,266.33432,263.5672||255,0,0,255,267.58032,258.91876||255,0,0,255,269.76086,256.1297||255,0,0,255,271.94138,255.20001||255,0,0,255,284.71295,248.22733||255,0,0,255,294.3695,232.42259||255,0,0,255,297.48453,212.8991||255,0,0,255,297.48453,212.8991||255,0,0,255,101.86119,202.67252||255,0,0,255,101.86119,202.67252||255,0,0,255,101.86119,202.67252||255,0,0,255,113.07527,194.77013||255,0,0,255,141.73346,178.9654||255,0,0,255,144.8485,179.43024||255,0,0,255,166.96513,192.4459||255,0,0,255,170.70317,195.23497||255,0,0,255,193.75432,195.69983||255,0,0,255,201.85338,185.93808||255,0,0,255,212.75597,146.42624||255,0,0,255,217.4285,136.19965||255,0,0,255,231.44609,135.26996||255,0,0,255,241.41415,136.66449||255,0,0,255,250.13622,136.19965||255,0,0,255,279.41742,128.76212||255,0,0,255,312.12512,100.406555||255,0,0,255,312.12512,100.406555||255,0,0,255,92.20462,52.527508||255,0,0,255,92.20462,52.527508||255,0,0,255,159.48909,44.160294||255,0,0,255,186.90128,34.398544||255,0,0,255,242.97165,20.918034||255,0,0,255,269.13785,13.48051||255,0,0,255,288.76245,11.62113||255,0,0,255,288.76245,11.62113,";
StringTokenizer tokens = new StringTokenizer(parseStr, "||");
while(tokens.hasMoreTokens())
{
String second = tokens.nextToken();
StringTokenizer tempTokens = new StringTokenizer(second, ",");
int index = 0;
while(tempTokens.hasMoreTokens())
{
String splitStr = tempTokens.nextToken();
switch (index%6) {
case 0:
rArray.add(splitStr);
break;
case 1:
gArray.add(splitStr);
break;
case 2:
bArray.add(splitStr);
break;
case 3:
alphaArray.add(splitStr);
break;
case 4:
xPointArray.add(splitStr);
break;
case 5:
yPointArray.add(splitStr);
break;
default:
break;
}
index++;
Log.v("String44"," value = "+splitStr);
}
}
m.redraw();
}
public void colorChanged(int color) {
mPaint.setColor(color);
//method to convert integer color to r g b a
b = (color)&0xFF;
g = (color>>8)&0xFF;
r = (color>>16)&0xFF;
a = Math.round(((color>>>24)/255)*10);
}
public class MyView extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
public Path mPath;
public Paint mBitmapPaint;
public MyView(Context c) {
super(c);
// reading screen size (for device Independence)
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
mBitmap = Bitmap.createBitmap(displaymetrics.widthPixels,displaymetrics.heightPixels, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
//background -- White
canvas.drawColor(0xFFFFFFFF);
//draw the bitmap from 0,0 the firstpoint in device's screen
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
public void touch_start(float x, float y) {
//reset touch so that a new path is to be drawn
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
public void touch_move(float x, float y) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
//point tracing
tracePoints((int)x, (int)y,(int)mX,(int)mY);
mX = x;
mY = y;
}
public 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.reset();
}
public void arrayPopulate(float atx,float aty){
if(eraseFlag==false){
//entering points into arrayLists
xPointArray.add(""+atx);
yPointArray.add(""+aty);
//add color as well
alphaArray.add(""+a);
rArray.add(""+r);
gArray.add(""+g);
bArray.add(""+b);
// if(toSend==null){
// toSend=""+r+","+g+","+b+","+a+","+atx+","+aty+"||";
// }
// else{
// toSend+=""+r+","+g+","+b+","+a+","+atx+","+aty+"||";
// }
//add data to a queue for sending to server
// Log.v("touch", toSend);
//send data here to server
//eraseFlag is true ends
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
arrayPopulate(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
arrayPopulate(x, y);
invalidate();
break;
}
return true;
}
public void redraw() {
int arraysize = FingerPaint.rArray.size();
int index=0;
float xx,yy;
//setting properties of tPaint
Paint tPaint= new Paint();
tPaint.setAntiAlias(true);
tPaint.setDither(true);
tPaint.setStyle(Paint.Style.STROKE);
tPaint.setStrokeJoin(Paint.Join.ROUND);
tPaint.setStrokeCap(Paint.Cap.ROUND);
tPaint.setColor(-16777216);
tPaint.setStrokeWidth(12);
while(index<arraysize){
//tPaint.setARGB(Integer.parseInt(alphaArray.get(0)), Integer.parseInt(rArray.get(0)), Integer.parseInt(gArray.get(0)), Integer.parseInt(bArray.get(0)));
//tPaint.setColor(Color.argb(Integer.parseInt(alphaArray.get(0)), Integer.parseInt(rArray.get(0)), Integer.parseInt(gArray.get(0)), Integer.parseInt(bArray.get(0))));
xx=Float.parseFloat(xPointArray.get(index));
yy=Float.parseFloat(yPointArray.get(index));
m.mCanvas.drawPoint(xx,yy,tPaint);
m.invalidate();
index++;
Log.d("__REDRAW__", "X="+xx+" Y="+yy);
}
}
}
//creating Options Menu - Inflater
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
//Option Menu Selection - Actions
@Override
public boolean onOptionsItemSelected(MenuItem item) {
mPaint.setXfermode(null);
mPaint.setAlpha(255);
switch (item.getItemId()) {
case R.id.COLOR:
this.eraseFlag=false;
new ColorPickerDialog(this, this, mPaint.getColor()).show();
return true;
case R.id.EMBOSS:
this.eraseFlag=false;
if (mPaint.getMaskFilter() != mEmboss) {
mPaint.setMaskFilter(mEmboss);
} else {
mPaint.setMaskFilter(null);
}
return true;
case R.id.BLUR:
this.eraseFlag=false;
if (mPaint.getMaskFilter() != mBlur) {
mPaint.setMaskFilter(mBlur);
} else {
mPaint.setMaskFilter(null);
}
return true;
case R.id.ERASE:
//for toggle detection
this.eraseFlag=true;
//reset blurr & Emboss
mPaint.setMaskFilter(null);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
return true;
case R.id.REDRAW:
this.eraseFlag=false;
//redraw the drawn points
m.redraw();
return true;
}
return super.onOptionsItemSelected(item);
}
//Bressenham's mid-point algorithm for finding all points between any two points --- Straight line
public void tracePoints(int xxx,int yyy,int x2, int y2) {
int w = x2 - xxx ;
int h = y2 - yyy ;
int dx1 = 0, dy1 = 0, dx2 = 0, dy2 = 0 ;
if (w<0) dx1 = -1 ; else if (w>0) dx1 = 1 ;
if (h<0) dy1 = -1 ; else if (h>0) dy1 = 1 ;
if (w<0) dx2 = -1 ; else if (w>0) dx2 = 1 ;
int longest = Math.abs(w) ;
int shortest = Math.abs(h) ;
if (!(longest>shortest)) {
longest = Math.abs(h) ;
shortest = Math.abs(w) ;
if (h<0) dy2 = -1 ; else if (h>0) dy2 = 1 ;
dx2 = 0 ;
}
int numerator = longest >> 1 ;
for (int i=0;i<=longest;i++) {
m.arrayPopulate((float)xxx,(float)yyy) ;
numerator += shortest ;
if (!(numerator<longest)) {
numerator -= longest ;
xxx += dx1 ;
yyy += dy1 ;
} else {
xxx += dx2 ;
yyy += dy2 ;
}
}
}
}
Try the following code. I hope it solves your problem. When I draw, I perfectly get all the points, whether I draw slowly or at a rapid rate.
public class HRCanvas extends Activity implements OnTouchListener{
DrawPanel dp;
private ArrayList<Path> pointsToDraw = new ArrayList<Path>();
private Paint mPaint;
Path path;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
dp = new DrawPanel(this);
dp.setOnTouchListener(this);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
mPaint = new Paint();
mPaint.setDither(true);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(30);
FrameLayout fl = new FrameLayout(this);
fl.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
fl.addView(dp);
setContentView(fl);
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
dp.pause();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
dp.resume();
}
public class DrawPanel extends SurfaceView implements Runnable{
Thread t = null;
SurfaceHolder holder;
boolean isItOk = false ;
public DrawPanel(Context context) {
super(context);
// TODO Auto-generated constructor stub
holder = getHolder();
}
@Override
public void run() {
// TODO Auto-generated method stub
while( isItOk == true){
if(!holder.getSurface().isValid()){
continue;
}
Canvas c = holder.lockCanvas();
c.drawARGB(255, 0, 0, 0);
onDraw(c);
holder.unlockCanvasAndPost(c);
}
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
synchronized(pointsToDraw)
{
for (Path path : pointsToDraw) {
canvas.drawPath(path, mPaint);
}
}
}
public void pause(){
isItOk = false;
while(true){
try{
t.join();
}catch(InterruptedException e){
e.printStackTrace();
}
break;
}
t = null;
}
public void resume(){
isItOk = true;
t = new Thread(this);
t.start();
}
}
@Override
public boolean onTouch(View v, MotionEvent me) {
// TODO Auto-generated method stub
synchronized(pointsToDraw)
{
if(me.getAction() == MotionEvent.ACTION_DOWN){
path = new Path();
path.moveTo(me.getX(), me.getY());
//path.lineTo(me.getX(), me.getY());
pointsToDraw.add(path);
}else if(me.getAction() == MotionEvent.ACTION_MOVE){
path.lineTo(me.getX(), me.getY());
}else if(me.getAction() == MotionEvent.ACTION_UP){
//path.lineTo(me.getX(), me.getY());
}
}
return true;
}
}
You need to interpolate to find the points between updates. I recommend cosine interpolation. Its easy to implement, produces decent results, and is computationally inexpensive.
public double cosineInterpolation(double x1, double x2, double normal)
{
double ft = normal * 3.1415927;
double f = (1 - Math.cos(ft)) * .5;
return x1 * (1 - f) + x2 * f;
}
You'll need to call this method twice; once for the x, and once for the y coordinates of 2 subsequent points.
You are able to solve this using MATH equation.
Use the line equation, y = mx + c Put (x1,y1) and and (x2,y2) to get two equations and solve to get values of m and c. (you will be able to find the direct equations to get m and c somewhere). Then create a loop to start from x = x1 till x = x2 Using y=mx+c, get the value of y (now that you know m and c)
I hope this is help.
Replace this code for your touch event....
float downx = 0;
float downy = 0;
float upx = 0;
float upy = 0;
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
downx = event.getX();
downy = event.getY();
break;
case MotionEvent.ACTION_MOVE:
upx = event.getX();
upy = event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
imageView.invalidate();
downx = upx;
downy = upy;
break;
break;
case MotionEvent.ACTION_UP:
upx = event.getX();
upy = event.getY();
canvas.drawLine(downx, downy, upx, upy, paint);
imageView.invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
Check out this code. The project is unfinished, but the lines draw quickly and smoothly on my Atrix. Although it does use quadTo which it sounds like you want to avoid. What device are you testing with?
I noticed that you aren't calling arrayPopulate(x, y);
in the onTouchEvent(MotionEvent event)
function under the MotionEvent.ACTION_MOVE
case. It's quite possible you're losing data there?
Is there any reason why you're trying to recode the Path class? If you really need to you'll probably have to include both bezier curves and straight lines and more if you ever decide to use any other functions in it. If they are using native to do bezier curves, then you'll probably want to as well.
I am facing the same problem as AndroidKid,
Due to the number of background actions, my app doesnt able to record all points of the touch event. My test app captures twenty location for an action, wheras my actual app captures only 5 to 6 points. So am getting polygon like object as the result.
As Android Kid said, when im drawing slowly, i get the proper curve as the points are recorded properly.
In my test app, i had a path object to append the points and used canvas.drawPath
, which gave me better result than canvas.drawLine
.
Android Kid,
Also try this if you can compromise the sharp edges with smooth curves in your path.
paint.setPathEffect(new CornerPathEffect(25));
Anyways, will keep you guys Posted!!
I know this question is old, but you can get all the points in an event, when the hardware tracks the events faster than they are delivered using the event's history. Something like:
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
expandDirtyRect(historicalX, historicalY);
path.lineTo(historicalX, historicalY);
}
Source
Why don't you just draw it onto a bitmap using black color, then loop through all the pixels to get the points?
Paint cPaint = new Paint();
cPaint.setAntiAlias(false);
cPaint.setColor(0xFF000000);
Bitmap topBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas topCanvas = new Canvas(topBitmap);
topCanvas.drawColor(0xFFFFFFFF, PorterDuff.Mode.CLEAR);
topCanvas.drawPath(mPath, cPaint);
for (int x=0; x<w; x++) {
for (int y=0; y<h; y++) {
if (topBitmap.getPixel(x, y) == 0xFF000000) {
// record the point
}
}
}
In any case, a path is not going to contain every point drawn. Also, if the other device is also running android or has something similar to a path object you can just recreate the path there using the points from your motion event.
精彩评论