How To Draw A Path On An Android Canvas With Animation?
I'm making an Android app and I've got a tricky thing to do. I need to draw a path on a canvas but the drawing should be animated (ie. drawing point after point with a slight delay
Solution 1:
Try this code, I used it to draw a heartbeat using Path
& Canvas
:
publicclassTestActivityextendsActivity {
/** Called when the activity is first created. */@OverridepublicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(newHeartbeatView(this));
}
publicstaticclassHeartbeatViewextendsView {
privatestatic Paint paint;
privateint screenW, screenH;
privatefloat X, Y;
private Path path;
privatefloat initialScreenW;
privatefloat initialX, plusX;
privatefloat TX;
privateboolean translate;
privateint flash;
private Context context;
publicHeartbeatView(Context context) {
super(context);
this.context=context;
paint = newPaint();
paint.setColor(Color.argb(0xff, 0x99, 0x00, 0x00));
paint.setStrokeWidth(10);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStyle(Paint.Style.STROKE);
paint.setShadowLayer(7, 0, 0, Color.RED);
path= newPath();
TX=0;
translate=false;
flash=0;
}
@OverridepublicvoidonSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
screenW = w;
screenH = h;
X = 0;
Y = (screenH/2)+(screenH/4)+(screenH/10);
initialScreenW=screenW;
initialX=((screenW/2)+(screenW/4));
plusX=(screenW/24);
path.moveTo(X, Y);
}
@OverridepublicvoidonDraw(Canvas canvas) {
super.onDraw(canvas);
//canvas.save();
flash+=1;
if(flash<10 || (flash>20 && flash<30))
{
paint.setStrokeWidth(16);
paint.setColor(Color.RED);
paint.setShadowLayer(12, 0, 0, Color.RED);
}
else
{
paint.setStrokeWidth(10);
paint.setColor(Color.argb(0xff, 0x99, 0x00, 0x00));
paint.setShadowLayer(7, 0, 0, Color.RED);
}
if(flash==100)
{
flash=0;
}
path.lineTo(X,Y);
canvas.translate(-TX, 0);
if(translate==true)
{
TX+=4;
}
if(X<initialX)
{
X+=8;
}
else
{
if(X<initialX+plusX)
{
X+=2;
Y-=8;
}
else
{
if(X<initialX+(plusX*2))
{
X+=2;
Y+=14;
}
else
{
if(X<initialX+(plusX*3))
{
X+=2;
Y-=12;
}
else
{
if(X<initialX+(plusX*4))
{
X+=2;
Y+=6;
}
else
{
if(X<initialScreenW)
{
X+=8;
}
else
{
translate=true;
initialX=initialX+initialScreenW;
}
}
}
}
}
}
canvas.drawPath(path, paint);
//canvas.restore();
invalidate();
}
}
}
It uses drawing a Path point by point with couple of effects using counters. You can take what you need and transfer it to SurfaceView which is more efficient.
Solution 2:
I hope this is what you are looking for. It draws the path on user touch, you could simply tweek it to achieve what you desire.
publicclassMyCanvasextendsActivityimplementsOnTouchListener{
DrawPanel dp;
privateArrayList<Path> pointsToDraw = newArrayList<Path>();
privatePaint mPaint;
Path path;
@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);
dp = newDrawPanel(this);
dp.setOnTouchListener(this);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
mPaint = newPaint();
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 = newFrameLayout(this);
fl.setLayoutParams(newLayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
fl.addView(dp);
setContentView(fl);
}
@OverrideprotectedvoidonPause() {
// TODO Auto-generated method stubsuper.onPause();
dp.pause();
}
@OverrideprotectedvoidonResume() {
// TODO Auto-generated method stubsuper.onResume();
dp.resume();
}
publicclassDrawPanelextendsSurfaceViewimplementsRunnable{
Thread t = null;
SurfaceHolder holder;
boolean isItOk = false ;
publicDrawPanel(Context context) {
super(context);
// TODO Auto-generated constructor stub
holder = getHolder();
}
@Overridepublicvoidrun() {
// TODO Auto-generated method stubwhile( isItOk == true){
if(!holder.getSurface().isValid()){
continue;
}
Canvas c = holder.lockCanvas();
c.drawARGB(255, 0, 0, 0);
onDraw(c);
holder.unlockCanvasAndPost(c);
}
}
@OverrideprotectedvoidonDraw(Canvas canvas) {
// TODO Auto-generated method stubsuper.onDraw(canvas);
synchronized(pointsToDraw)
{
for (Path path : pointsToDraw) {
canvas.drawPath(path, mPaint);
}
}
}
publicvoidpause(){
isItOk = false;
while(true){
try{
t.join();
}catch(InterruptedException e){
e.printStackTrace();
}
break;
}
t = null;
}
publicvoidresume(){
isItOk = true;
t = newThread(this);
t.start();
}
}
@OverridepublicbooleanonTouch(View v, MotionEvent me) {
// TODO Auto-generated method stubsynchronized(pointsToDraw)
{
if(me.getAction() == MotionEvent.ACTION_DOWN){
path = newPath();
path.moveTo(me.getX(), me.getY());
//path.lineTo(me.getX(), me.getY());
pointsToDraw.add(path);
}elseif(me.getAction() == MotionEvent.ACTION_MOVE){
path.lineTo(me.getX(), me.getY());
}elseif(me.getAction() == MotionEvent.ACTION_UP){
//path.lineTo(me.getX(), me.getY());
}
}
returntrue;
}
}
Solution 3:
I have made it with ObjectAnimator. We have any Path and our CustomView (in wich we'll draw our path)
privateCustomView view;
privatePath path;
@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = findViewById(R.id.custom_view);
path = newPath();
path.moveTo(0f, 0f);
path.lineTo(getResources().getDimension(R.dimen.point_250), 0f);
path.lineTo(getResources().getDimension(R.dimen.point_250), getResources().getDimension(R.dimen.point_150));
findViewById(R.id.btnStart).setOnClickListener(v -> {
test();
});
}
privatevoidtest() {
ValueAnimator pathAnimator = ObjectAnimator.ofFloat(view, "xCoord", "yCoord", path);
pathAnimator.setDuration(5000);
pathAnimator.start();
}
And just pass our "xCoord" and "yCoord" to CustomView
publicclassCustomViewextendsView {
private Paint paint;
privatefloat xCoord;
privatefloat yCoord;
privatePathpath=newPath();
publicvoidsetXCoord(float xCoord) {
this.xCoord = xCoord;
}
publicvoidsetYCoord(float yCoord) {
this.yCoord = yCoord;
path.lineTo(xCoord, yCoord);
invalidate();
}
publicCustomView(Context context) {
super(context);
init();
}
publicCustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
voidinit() {
paint = newPaint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(20);
}
@OverrideprotectedvoidonDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
}
}
Solution 4:
This might help... It draws adjacent circles instead of a path to simulate an animatable path.
publicclassPathAnimatable {
privatefinalfloatCIRCLE_SIZE=2.5f;
publicfloatSPPED_SCALE=1f;
privatefloatsteps=0;
privatefloat pathLength;
private PathMeasure pathMeasure;
privatefloat totalStepsNeeded;
privatefloat[] point = newfloat[]{0f, 0f};
privatefloat stride;
publicPathAnimatable() {
this(null);
}
publicPathAnimatable(Path path) {
super(path);
init();
}
privatevoidinit() {
pathMeasure = newPathMeasure(path, false);
pathLength = pathMeasure.getLength();
stride = CIRCLE_SIZE * 0.5f;
totalStepsNeeded = pathLength / stride;
steps = 0;
}
@OverridepublicvoidsetPath(Path path) {
super.setPath(path);
init();
}
// Called this from your locked canvas loop functionpublicvoiddrawShape(Canvas canvas, Paint paint) {
if (steps <= pathLength) {
for (floati=0; i < steps ; i += stride) {
pathMeasure.getPosTan(i, point, null);
canvas.drawCircle(point[0], point[1], CIRCLE_SIZE, paint);
}
steps += stride * SPPED_SCALE;
} else {
steps = 0;
}
}
}
Post a Comment for "How To Draw A Path On An Android Canvas With Animation?"